1 #define __SP_DESKTOP_C__
3 /** \file
4 * Editable view implementation
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * MenTaLguY <mental@rydia.net>
9 * bulia byak <buliabyak@users.sf.net>
10 * Ralf Stephan <ralf@ark.in-berlin.de>
11 * John Bintz <jcoswell@coswellproductions.org>
12 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
13 *
14 * Copyright (C) 2007 Jon A. Cruz
15 * Copyright (C) 2006-2008 Johan Engelen
16 * Copyright (C) 2006 John Bintz
17 * Copyright (C) 2004 MenTaLguY
18 * Copyright (C) 1999-2002 Lauris Kaplinski
19 * Copyright (C) 2000-2001 Ximian, Inc.
20 *
21 * Released under GNU GPL, read the file 'COPYING' for more information
22 */
24 /** \class SPDesktop
25 * SPDesktop is a subclass of View, implementing an editable document
26 * canvas. It is extensively used by many UI controls that need certain
27 * visual representations of their own.
28 *
29 * SPDesktop provides a certain set of SPCanvasItems, serving as GUI
30 * layers of different control objects. The one containing the whole
31 * document is the drawing layer. In addition to it, there are grid,
32 * guide, sketch and control layers. The sketch layer is used for
33 * temporary drawing objects, before the real objects in document are
34 * created. The control layer contains editing knots, rubberband and
35 * similar non-document UI objects.
36 *
37 * Each SPDesktop is associated with a SPNamedView node of the document
38 * tree. Currently, all desktops are created from a single main named
39 * view, but in the future there may be support for different ones.
40 * SPNamedView serves as an in-document container for desktop-related
41 * data, like grid and guideline placement, snapping options and so on.
42 *
43 * Associated with each SPDesktop are the two most important editing
44 * related objects - SPSelection and SPEventContext.
45 *
46 * Sodipodi keeps track of the active desktop and invokes notification
47 * signals whenever it changes. UI elements can use these to update their
48 * display to the selection of the currently active editing window.
49 * (Lauris Kaplinski)
50 */
52 #ifdef HAVE_CONFIG_H
53 # include "config.h"
54 #endif
56 #include <glibmm/i18n.h>
57 #include <sigc++/functors/mem_fun.h>
58 #include <gtkmm.h>
60 #include "macros.h"
61 #include "inkscape-private.h"
62 #include "desktop.h"
63 #include "desktop-events.h"
64 #include "desktop-handles.h"
65 #include "document.h"
66 #include "message-stack.h"
67 #include "selection.h"
68 #include "select-context.h"
69 #include "sp-namedview.h"
70 #include "color.h"
71 #include "sp-item-group.h"
72 #include "prefs-utils.h"
73 #include "object-hierarchy.h"
74 #include "helper/units.h"
75 #include "display/canvas-arena.h"
76 #include "display/nr-arena.h"
77 #include "display/gnome-canvas-acetate.h"
78 #include "display/sodipodi-ctrlrect.h"
79 #include "display/sp-canvas-util.h"
80 #include "display/canvas-temporary-item-list.h"
81 #include "display/snap-indicator.h"
82 #include "libnr/nr-matrix-div.h"
83 #include "libnr/nr-rect-ops.h"
84 #include "ui/dialog/dialog-manager.h"
85 #include "xml/repr.h"
86 #include "message-context.h"
87 #include "device-manager.h"
88 #include "layer-fns.h"
89 #include "layer-manager.h"
90 #include "event-log.h"
91 #include "display/canvas-grid.h"
92 #include "widgets/desktop-widget.h"
93 #include "box3d-context.h"
95 #include "display/sp-canvas.h"
97 namespace Inkscape { namespace XML { class Node; }}
99 // Callback declarations
100 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
101 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
102 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
103 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
104 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
105 static void _reconstruction_start(SPDesktop * desktop);
106 static void _reconstruction_finish(SPDesktop * desktop);
107 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
108 static void _update_snap_distances (SPDesktop *desktop);
110 /**
111 * Return new desktop object.
112 * \pre namedview != NULL.
113 * \pre canvas != NULL.
114 */
115 SPDesktop::SPDesktop() :
116 _dlg_mgr( 0 ),
117 namedview( 0 ),
118 canvas( 0 ),
119 selection( 0 ),
120 event_context( 0 ),
121 layer_manager( 0 ),
122 event_log( 0 ),
123 temporary_item_list( 0 ),
124 snapindicator( 0 ),
125 acetate( 0 ),
126 main( 0 ),
127 gridgroup( 0 ),
128 guides( 0 ),
129 drawing( 0 ),
130 sketch( 0 ),
131 controls( 0 ),
132 tempgroup ( 0 ),
133 table( 0 ),
134 page( 0 ),
135 page_border( 0 ),
136 current( 0 ),
137 zooms_past( 0 ),
138 zooms_future( 0 ),
139 dkey( 0 ),
140 number( 0 ),
141 window_state(0),
142 interaction_disabled_counter( 0 ),
143 waiting_cursor( false ),
144 guides_active( false ),
145 gr_item( 0 ),
146 gr_point_type( 0 ),
147 gr_point_i( 0 ),
148 gr_fill_or_stroke( true ),
149 _layer_hierarchy( 0 ),
150 _reconstruction_old_layer_id( 0 ),
151 _display_mode(Inkscape::RENDERMODE_NORMAL),
152 _saved_display_mode(Inkscape::RENDERMODE_NORMAL),
153 _widget( 0 ),
154 _inkscape( 0 ),
155 _guides_message_context( 0 ),
156 _active( false ),
157 _w2d(),
158 _d2w(),
159 _doc2dt( NR::Matrix(NR::scale(1, -1)) ),
160 grids_visible( false )
161 {
162 _d2w.set_identity();
163 _w2d.set_identity();
165 selection = Inkscape::GC::release( new Inkscape::Selection(this) );
166 }
168 void
169 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
170 {
171 // Temporary workaround for link order issues:
172 Inkscape::DeviceManager::getManager().getDevices();
174 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
176 current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
178 namedview = nv;
179 canvas = aCanvas;
181 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
182 /* Kill flicker */
183 sp_document_ensure_up_to_date (document);
185 /* Setup Dialog Manager */
186 _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
188 dkey = sp_item_display_key_new (1);
190 /* Connect document */
191 setDocument (document);
193 number = namedview->getViewCount();
196 /* Setup Canvas */
197 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
199 SPCanvasGroup *root = sp_canvas_root (canvas);
201 /* Setup adminstrative layers */
202 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
203 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
204 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
205 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
207 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
208 SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
209 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
210 sp_canvas_item_move_to_z (table, 0);
212 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
213 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
214 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
216 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
217 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
219 SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
221 if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
222 // Start in outline mode
223 setDisplayModeOutline();
224 } else {
225 // Start in normal mode, default
226 setDisplayModeNormal();
227 }
229 gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
230 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
231 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
232 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
233 tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
235 /* Push select tool to the bottom of stack */
236 /** \todo
237 * FIXME: this is the only call to this. Everything else seems to just
238 * call "set" instead of "push". Can we assume that there is only one
239 * context ever?
240 */
241 push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
243 // display rect and zoom are now handled in sp_desktop_widget_realize()
245 NR::Rect const d(NR::Point(0.0, 0.0),
246 NR::Point(sp_document_width(document), sp_document_height(document)));
248 SP_CTRLRECT(page)->setRectangle(d);
249 SP_CTRLRECT(page_border)->setRectangle(d);
251 /* the following sets the page shadow on the canvas
252 It was originally set to 5, which is really cheesy!
253 It now is an attribute in the document's namedview. If a value of
254 0 is used, then the constructor for a shadow is not initialized.
255 */
257 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
258 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
259 }
262 /* Connect event for page resize */
263 _doc2dt[5] = sp_document_height (document);
264 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
266 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
268 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
269 SP_CANVAS_ARENA (drawing)->arena,
270 dkey,
271 SP_ITEM_SHOW_DISPLAY);
272 if (ai) {
273 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
274 nr_arena_item_unref (ai);
275 }
277 namedview->show(this);
278 /* Ugly hack */
279 activate_guides (true);
280 /* Ugly hack */
281 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
283 /* Set up notification of rebuilding the document, this allows
284 for saving object related settings in the document. */
285 _reconstruction_start_connection =
286 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
287 _reconstruction_finish_connection =
288 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
289 _reconstruction_old_layer_id = NULL;
291 // ?
292 // sp_active_desktop_set (desktop);
293 _inkscape = INKSCAPE;
295 _activate_connection = _activate_signal.connect(
296 sigc::bind(
297 sigc::ptr_fun(_onActivate),
298 this
299 )
300 );
301 _deactivate_connection = _deactivate_signal.connect(
302 sigc::bind(
303 sigc::ptr_fun(_onDeactivate),
304 this
305 )
306 );
308 _sel_modified_connection = selection->connectModified(
309 sigc::bind(
310 sigc::ptr_fun(&_onSelectionModified),
311 this
312 )
313 );
314 _sel_changed_connection = selection->connectChanged(
315 sigc::bind(
316 sigc::ptr_fun(&_onSelectionChanged),
317 this
318 )
319 );
322 /* setup LayerManager */
323 // (Setting up after the connections are all in place, as it may use some of them)
324 layer_manager = new Inkscape::LayerManager( this );
326 showGrids(namedview->grids_visible, false);
328 temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
329 snapindicator = new Inkscape::Display::SnapIndicator ( this );
330 }
333 void SPDesktop::destroy()
334 {
335 if (snapindicator) {
336 delete snapindicator;
337 snapindicator = NULL;
338 }
339 if (temporary_item_list) {
340 delete temporary_item_list;
341 temporary_item_list = NULL;
342 }
344 namedview->hide(this);
346 _activate_connection.disconnect();
347 _deactivate_connection.disconnect();
348 _sel_modified_connection.disconnect();
349 _sel_changed_connection.disconnect();
350 _modified_connection.disconnect();
351 _commit_connection.disconnect();
352 _reconstruction_start_connection.disconnect();
353 _reconstruction_finish_connection.disconnect();
355 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
356 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
357 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
359 while (event_context) {
360 SPEventContext *ec = event_context;
361 event_context = ec->next;
362 sp_event_context_finish (ec);
363 g_object_unref (G_OBJECT (ec));
364 }
366 if (_layer_hierarchy) {
367 delete _layer_hierarchy;
368 // _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
369 }
371 if (layer_manager) {
372 delete layer_manager;
373 layer_manager = NULL;
374 }
376 if (_inkscape) {
377 _inkscape = NULL;
378 }
380 if (drawing) {
381 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
382 drawing = NULL;
383 }
385 delete _guides_message_context;
386 _guides_message_context = NULL;
388 g_list_free (zooms_past);
389 g_list_free (zooms_future);
390 }
392 SPDesktop::~SPDesktop() {}
394 //--------------------------------------------------------------------
395 /* Public methods */
398 /** Note that lifetime is measured in milliseconds
399 * it is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
400 * The return value should only be used as argument for SPDesktop::remove_temporary_canvasitem, because the object might be deleted already.
401 * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
402 */
403 Inkscape::Display::TemporaryItem *
404 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
405 {
406 if (move_to_bottom) {
407 sp_canvas_item_move_to_z(item, 0);
408 }
410 return temporary_item_list->add_item(item, lifetime);
411 }
413 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
414 */
415 void
416 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
417 {
418 // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
419 if (tempitem && temporary_item_list) {
420 temporary_item_list->delete_item(tempitem);
421 }
422 }
424 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
425 SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
426 canvas->rendermode = mode;
427 _display_mode = mode;
428 if (mode != Inkscape::RENDERMODE_OUTLINE) {
429 _saved_display_mode = _display_mode;
430 }
431 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
432 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
433 }
435 void SPDesktop::displayModeToggle() {
436 if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
437 _setDisplayMode(_saved_display_mode);
438 } else {
439 _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
440 }
441 }
443 /**
444 * Returns current root (=bottom) layer.
445 */
446 SPObject *SPDesktop::currentRoot() const
447 {
448 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
449 }
451 /**
452 * Returns current top layer.
453 */
454 SPObject *SPDesktop::currentLayer() const
455 {
456 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
457 }
459 /**
460 * Sets the current layer of the desktop.
461 *
462 * Make \a object the top layer.
463 */
464 void SPDesktop::setCurrentLayer(SPObject *object) {
465 g_return_if_fail(SP_IS_GROUP(object));
466 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
467 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
468 _layer_hierarchy->setBottom(object);
469 }
471 void SPDesktop::toggleLayerSolo(SPObject *object) {
472 g_return_if_fail(SP_IS_GROUP(object));
473 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
475 bool othersShowing = false;
476 std::vector<SPObject*> layers;
477 for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
478 layers.push_back(obj);
479 othersShowing |= !SP_ITEM(obj)->isHidden();
480 }
481 for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
482 layers.push_back(obj);
483 othersShowing |= !SP_ITEM(obj)->isHidden();
484 }
487 if ( SP_ITEM(object)->isHidden() ) {
488 SP_ITEM(object)->setHidden(false);
489 }
491 for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
492 SP_ITEM(*it)->setHidden(othersShowing);
493 }
494 }
496 /**
497 * Return layer that contains \a object.
498 */
499 SPObject *SPDesktop::layerForObject(SPObject *object) {
500 g_return_val_if_fail(object != NULL, NULL);
502 SPObject *root=currentRoot();
503 object = SP_OBJECT_PARENT(object);
504 while ( object && object != root && !isLayer(object) ) {
505 object = SP_OBJECT_PARENT(object);
506 }
507 return object;
508 }
510 /**
511 * True if object is a layer.
512 */
513 bool SPDesktop::isLayer(SPObject *object) const {
514 return ( SP_IS_GROUP(object)
515 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
516 == SPGroup::LAYER ) );
517 }
519 /**
520 * True if desktop viewport fully contains \a item's bbox.
521 */
522 bool SPDesktop::isWithinViewport (SPItem *item) const
523 {
524 NR::Rect const viewport = get_display_area();
525 NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
526 if (bbox) {
527 return viewport.contains(*bbox);
528 } else {
529 return true;
530 }
531 }
533 ///
534 bool SPDesktop::itemIsHidden(SPItem const *item) const {
535 return item->isHidden(this->dkey);
536 }
538 /**
539 * Set activate property of desktop; emit signal if changed.
540 */
541 void
542 SPDesktop::set_active (bool new_active)
543 {
544 if (new_active != _active) {
545 _active = new_active;
546 if (new_active) {
547 _activate_signal.emit();
548 } else {
549 _deactivate_signal.emit();
550 }
551 }
552 }
554 /**
555 * Set activate status of current desktop's named view.
556 */
557 void
558 SPDesktop::activate_guides(bool activate)
559 {
560 guides_active = activate;
561 namedview->activateGuides(this, activate);
562 }
564 /**
565 * Make desktop switch documents.
566 */
567 void
568 SPDesktop::change_document (SPDocument *theDocument)
569 {
570 g_return_if_fail (theDocument != NULL);
572 /* unselect everything before switching documents */
573 selection->clear();
575 setDocument (theDocument);
577 /* update the rulers, connect the desktop widget's signal to the new namedview etc.
578 (this can probably be done in a better way) */
579 Gtk::Window *parent = this->getToplevel();
580 g_assert(parent != NULL);
581 SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
582 if (dtw) dtw->desktop = this;
583 sp_desktop_widget_update_namedview(dtw);
585 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
586 _document_replaced_signal.emit (this, theDocument);
587 }
589 /**
590 * Make desktop switch event contexts.
591 */
592 void
593 SPDesktop::set_event_context (GtkType type, const gchar *config)
594 {
595 SPEventContext *ec;
596 while (event_context) {
597 ec = event_context;
598 sp_event_context_deactivate (ec);
599 event_context = ec->next;
600 sp_event_context_finish (ec);
601 g_object_unref (G_OBJECT (ec));
602 }
604 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
605 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
606 ec->next = event_context;
607 event_context = ec;
608 sp_event_context_activate (ec);
609 _event_context_changed_signal.emit (this, ec);
610 }
612 /**
613 * Push event context onto desktop's context stack.
614 */
615 void
616 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
617 {
618 SPEventContext *ref, *ec;
619 Inkscape::XML::Node *repr;
621 if (event_context && event_context->key == key) return;
622 ref = event_context;
623 while (ref && ref->next && ref->next->key != key) ref = ref->next;
624 if (ref && ref->next) {
625 ec = ref->next;
626 ref->next = ec->next;
627 sp_event_context_finish (ec);
628 g_object_unref (G_OBJECT (ec));
629 }
631 if (event_context) sp_event_context_deactivate (event_context);
632 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
633 ec = sp_event_context_new (type, this, repr, key);
634 ec->next = event_context;
635 event_context = ec;
636 sp_event_context_activate (ec);
637 _event_context_changed_signal.emit (this, ec);
638 }
640 /**
641 * Sets the coordinate status to a given point
642 */
643 void
644 SPDesktop::set_coordinate_status (NR::Point p) {
645 _widget->setCoordinateStatus(p);
646 }
648 /**
649 * \see sp_document_item_from_list_at_point_bottom()
650 */
651 SPItem *
652 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
653 {
654 g_return_val_if_fail (doc() != NULL, NULL);
655 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
656 }
658 /**
659 * \see sp_document_item_at_point()
660 */
661 SPItem *
662 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
663 {
664 g_return_val_if_fail (doc() != NULL, NULL);
665 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
666 }
668 /**
669 * \see sp_document_group_at_point()
670 */
671 SPItem *
672 SPDesktop::group_at_point (NR::Point const p) const
673 {
674 g_return_val_if_fail (doc() != NULL, NULL);
675 return sp_document_group_at_point (doc(), dkey, p);
676 }
678 /**
679 * \brief Returns the mouse point in document coordinates; if mouse is
680 * outside the canvas, returns the center of canvas viewpoint
681 */
682 NR::Point
683 SPDesktop::point() const
684 {
685 NR::Point p = _widget->getPointer();
686 NR::Point pw = sp_canvas_window_to_world (canvas, p);
687 p = w2d(pw);
689 NR::Rect const r = canvas->getViewbox();
691 NR::Point r0 = w2d(r.min());
692 NR::Point r1 = w2d(r.max());
694 if (p[NR::X] >= r0[NR::X] &&
695 p[NR::X] <= r1[NR::X] &&
696 p[NR::Y] >= r1[NR::Y] &&
697 p[NR::Y] <= r0[NR::Y])
698 {
699 return p;
700 } else {
701 return (r0 + r1) / 2;
702 }
703 }
705 /**
706 * Put current zoom data in history list.
707 */
708 void
709 SPDesktop::push_current_zoom (GList **history)
710 {
711 NR::Rect const area = get_display_area();
713 NRRect *old_zoom = g_new(NRRect, 1);
714 old_zoom->x0 = area.min()[NR::X];
715 old_zoom->x1 = area.max()[NR::X];
716 old_zoom->y0 = area.min()[NR::Y];
717 old_zoom->y1 = area.max()[NR::Y];
718 if ( *history == NULL
719 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
720 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
721 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
722 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
723 {
724 *history = g_list_prepend (*history, old_zoom);
725 }
726 }
728 /**
729 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
730 */
731 void
732 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
733 {
734 g_assert(_widget);
736 // save the zoom
737 if (log) {
738 push_current_zoom(&zooms_past);
739 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
740 g_list_free (zooms_future);
741 zooms_future = NULL;
742 }
744 double const cx = 0.5 * (x0 + x1);
745 double const cy = 0.5 * (y0 + y1);
747 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
749 double scale = expansion(_d2w);
750 double newscale;
751 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
752 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
753 } else {
754 newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
755 }
757 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
759 int clear = FALSE;
760 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
761 /* Set zoom factors */
762 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
763 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
764 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
765 clear = TRUE;
766 }
768 /* Calculate top left corner (in document pixels) */
769 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
770 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
772 /* Scroll */
773 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
775 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
776 sp_box3d_context_update_lines(event_context);
778 _widget->updateRulers();
779 _widget->updateScrollbars(expansion(_d2w));
780 _widget->updateZoom();
781 }
783 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
784 {
785 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
786 }
788 /**
789 * Return viewbox dimensions.
790 */
791 NR::Rect SPDesktop::get_display_area() const
792 {
793 NR::Rect const viewbox = canvas->getViewbox();
795 double const scale = _d2w[0];
797 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
798 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
799 }
801 /**
802 * Revert back to previous zoom if possible.
803 */
804 void
805 SPDesktop::prev_zoom()
806 {
807 if (zooms_past == NULL) {
808 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
809 return;
810 }
812 // push current zoom into forward zooms list
813 push_current_zoom (&zooms_future);
815 // restore previous zoom
816 set_display_area (((NRRect *) zooms_past->data)->x0,
817 ((NRRect *) zooms_past->data)->y0,
818 ((NRRect *) zooms_past->data)->x1,
819 ((NRRect *) zooms_past->data)->y1,
820 0, false);
822 // remove the just-added zoom from the past zooms list
823 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
824 }
826 /**
827 * Set zoom to next in list.
828 */
829 void
830 SPDesktop::next_zoom()
831 {
832 if (zooms_future == NULL) {
833 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
834 return;
835 }
837 // push current zoom into past zooms list
838 push_current_zoom (&zooms_past);
840 // restore next zoom
841 set_display_area (((NRRect *) zooms_future->data)->x0,
842 ((NRRect *) zooms_future->data)->y0,
843 ((NRRect *) zooms_future->data)->x1,
844 ((NRRect *) zooms_future->data)->y1,
845 0, false);
847 // remove the just-used zoom from the zooms_future list
848 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
849 }
851 /**
852 * Zoom to point with absolute zoom factor.
853 */
854 void
855 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
856 {
857 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
859 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
860 // this check prevents "sliding" when trying to zoom in at maximum zoom;
861 /// \todo someone please fix calculations properly and remove this hack
862 if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
863 return;
865 NR::Rect const viewbox = canvas->getViewbox();
867 double const width2 = viewbox.dimensions()[NR::X] / zoom;
868 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
870 set_display_area(cx - px * width2,
871 cy - py * height2,
872 cx + (1 - px) * width2,
873 cy + (1 - py) * height2,
874 0.0);
875 }
877 /**
878 * Zoom to center with absolute zoom factor.
879 */
880 void
881 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
882 {
883 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
884 }
886 /**
887 * Zoom to point with relative zoom factor.
888 */
889 void
890 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
891 {
892 NR::Rect const area = get_display_area();
894 if (cx < area.min()[NR::X]) {
895 cx = area.min()[NR::X];
896 }
897 if (cx > area.max()[NR::X]) {
898 cx = area.max()[NR::X];
899 }
900 if (cy < area.min()[NR::Y]) {
901 cy = area.min()[NR::Y];
902 }
903 if (cy > area.max()[NR::Y]) {
904 cy = area.max()[NR::Y];
905 }
907 gdouble const scale = expansion(_d2w) * zoom;
908 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
909 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
911 zoom_absolute_keep_point(cx, cy, px, py, scale);
912 }
914 /**
915 * Zoom to center with relative zoom factor.
916 */
917 void
918 SPDesktop::zoom_relative (double cx, double cy, double zoom)
919 {
920 gdouble scale = expansion(_d2w) * zoom;
921 zoom_absolute (cx, cy, scale);
922 }
924 /**
925 * Set display area to origin and current document dimensions.
926 */
927 void
928 SPDesktop::zoom_page()
929 {
930 NR::Rect d(NR::Point(0, 0),
931 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
933 if (d.isEmpty(1.0)) {
934 return;
935 }
937 set_display_area(d, 10);
938 }
940 /**
941 * Set display area to current document width.
942 */
943 void
944 SPDesktop::zoom_page_width()
945 {
946 NR::Rect const a = get_display_area();
948 if (sp_document_width(doc()) < 1.0) {
949 return;
950 }
952 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
953 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
955 set_display_area(d, 10);
956 }
958 /**
959 * Zoom to selection.
960 */
961 void
962 SPDesktop::zoom_selection()
963 {
964 NR::Maybe<NR::Rect> const d = selection->bounds();
966 if ( !d || d->isEmpty(0.1) ) {
967 return;
968 }
970 set_display_area(*d, 10);
971 }
973 /**
974 * Tell widget to let zoom widget grab keyboard focus.
975 */
976 void
977 SPDesktop::zoom_grab_focus()
978 {
979 _widget->letZoomGrabFocus();
980 }
982 /**
983 * Zoom to whole drawing.
984 */
985 void
986 SPDesktop::zoom_drawing()
987 {
988 g_return_if_fail (doc() != NULL);
989 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
990 g_return_if_fail (docitem != NULL);
992 NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
994 /* Note that the second condition here indicates that
995 ** there are no items in the drawing.
996 */
997 if ( !d || d->isEmpty(1.0) ) {
998 return;
999 }
1001 set_display_area(*d, 10);
1002 }
1004 /**
1005 * Scroll canvas by specific coordinate amount in svg coordinates.
1006 */
1007 void
1008 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1009 {
1010 double scale = expansion(_d2w);
1011 scroll_world(dx*scale, dy*scale, is_scrolling);
1012 }
1014 /**
1015 * Scroll canvas by specific coordinate amount.
1016 */
1017 void
1018 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1019 {
1020 g_assert(_widget);
1022 NR::Rect const viewbox = canvas->getViewbox();
1024 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
1026 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1027 sp_box3d_context_update_lines(event_context);
1029 _widget->updateRulers();
1030 _widget->updateScrollbars(expansion(_d2w));
1031 }
1033 bool
1034 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
1035 {
1036 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
1038 // autoscrolldistance is in screen pixels, but the display area is in document units
1039 autoscrolldistance /= expansion(_d2w);
1040 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
1042 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
1043 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
1045 NR::Point const s_w( (*p) * _d2w );
1047 gdouble x_to;
1048 if ((*p)[NR::X] < dbox.min()[NR::X])
1049 x_to = dbox.min()[NR::X];
1050 else if ((*p)[NR::X] > dbox.max()[NR::X])
1051 x_to = dbox.max()[NR::X];
1052 else
1053 x_to = (*p)[NR::X];
1055 gdouble y_to;
1056 if ((*p)[NR::Y] < dbox.min()[NR::Y])
1057 y_to = dbox.min()[NR::Y];
1058 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
1059 y_to = dbox.max()[NR::Y];
1060 else
1061 y_to = (*p)[NR::Y];
1063 NR::Point const d_dt(x_to, y_to);
1064 NR::Point const d_w( d_dt * _d2w );
1065 NR::Point const moved_w( d_w - s_w );
1067 if (autoscrollspeed == 0)
1068 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
1070 if (autoscrollspeed != 0)
1071 scroll_world (autoscrollspeed * moved_w);
1073 return true;
1074 }
1075 return false;
1076 }
1078 bool
1079 SPDesktop::is_iconified()
1080 {
1081 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1082 }
1084 void
1085 SPDesktop::iconify()
1086 {
1087 _widget->setIconified();
1088 }
1090 bool
1091 SPDesktop::is_maximized()
1092 {
1093 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1094 }
1096 void
1097 SPDesktop::maximize()
1098 {
1099 _widget->setMaximized();
1100 }
1102 bool
1103 SPDesktop::is_fullscreen()
1104 {
1105 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1106 }
1108 void
1109 SPDesktop::fullscreen()
1110 {
1111 _widget->setFullscreen();
1112 }
1114 void
1115 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1116 {
1117 _widget->getGeometry (x, y, w, h);
1118 }
1120 void
1121 SPDesktop::setWindowPosition (NR::Point p)
1122 {
1123 _widget->setPosition (p);
1124 }
1126 void
1127 SPDesktop::setWindowSize (gint w, gint h)
1128 {
1129 _widget->setSize (w, h);
1130 }
1132 void
1133 SPDesktop::setWindowTransient (void *p, int transient_policy)
1134 {
1135 _widget->setTransient (p, transient_policy);
1136 }
1138 Gtk::Window*
1139 SPDesktop::getToplevel( )
1140 {
1141 return _widget->getWindow();
1142 }
1144 void
1145 SPDesktop::presentWindow()
1146 {
1147 _widget->present();
1148 }
1150 bool
1151 SPDesktop::warnDialog (gchar *text)
1152 {
1153 return _widget->warnDialog (text);
1154 }
1156 void
1157 SPDesktop::toggleRulers()
1158 {
1159 _widget->toggleRulers();
1160 }
1162 void
1163 SPDesktop::toggleScrollbars()
1164 {
1165 _widget->toggleScrollbars();
1166 }
1168 void
1169 SPDesktop::layoutWidget()
1170 {
1171 _widget->layout();
1172 }
1174 void
1175 SPDesktop::destroyWidget()
1176 {
1177 _widget->destroy();
1178 }
1180 bool
1181 SPDesktop::shutdown()
1182 {
1183 return _widget->shutdown();
1184 }
1186 bool SPDesktop::onDeleteUI (GdkEventAny*)
1187 {
1188 if(shutdown())
1189 return true;
1191 destroyWidget();
1192 return false;
1193 }
1195 /**
1196 * onWindowStateEvent
1197 *
1198 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1199 * Since GTK doesn't have a way to query this state information directly, we
1200 * record it for the desktop here, and also possibly trigger a layout.
1201 */
1202 bool
1203 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1204 {
1205 // Record the desktop window's state
1206 window_state = event->new_window_state;
1208 // Layout may differ depending on full-screen mode or not
1209 GdkWindowState changed = event->changed_mask;
1210 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1211 layoutWidget();
1212 }
1214 return false;
1215 }
1217 void
1218 SPDesktop::setToolboxFocusTo (gchar const *label)
1219 {
1220 _widget->setToolboxFocusTo (label);
1221 }
1223 void
1224 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1225 {
1226 _widget->setToolboxAdjustmentValue (id, val);
1227 }
1229 void
1230 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1231 {
1232 _widget->setToolboxSelectOneValue (id, val);
1233 }
1235 bool
1236 SPDesktop::isToolboxButtonActive (gchar const *id)
1237 {
1238 return _widget->isToolboxButtonActive (id);
1239 }
1241 void
1242 SPDesktop::emitToolSubselectionChanged(gpointer data)
1243 {
1244 _tool_subselection_changed.emit(data);
1245 inkscape_subselection_changed (this);
1246 }
1248 void
1249 SPDesktop::updateNow()
1250 {
1251 sp_canvas_update_now(canvas);
1252 }
1254 void
1255 SPDesktop::enableInteraction()
1256 {
1257 _widget->enableInteraction();
1258 }
1260 void SPDesktop::disableInteraction()
1261 {
1262 _widget->disableInteraction();
1263 }
1265 void SPDesktop::setWaitingCursor()
1266 {
1267 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1268 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1269 gdk_cursor_unref(waiting);
1270 waiting_cursor = true;
1272 // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1273 // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1274 // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1275 // after the call to setWaitingCursor as it was before
1276 while( Gtk::Main::events_pending() )
1277 Gtk::Main::iteration();
1278 }
1280 void SPDesktop::clearWaitingCursor()
1281 {
1282 if (waiting_cursor)
1283 sp_event_context_update_cursor(sp_desktop_event_context(this));
1284 }
1286 void SPDesktop::toggleColorProfAdjust()
1287 {
1288 _widget->toggleColorProfAdjust();
1289 }
1291 void SPDesktop::toggleGrids()
1292 {
1293 if (namedview->grids) {
1294 if(gridgroup) {
1295 showGrids(!grids_visible);
1296 }
1297 } else {
1298 //there is no grid present at the moment. add a rectangular grid and make it visible
1299 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1300 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1301 showGrids(true);
1302 }
1303 }
1305 void SPDesktop::showGrids(bool show, bool dirty_document)
1306 {
1307 grids_visible = show;
1308 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1309 if (show) {
1310 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1311 } else {
1312 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1313 }
1314 }
1316 void SPDesktop::toggleSnapping()
1317 {
1318 bool v = namedview->snap_manager.getSnapEnabledGlobally();
1319 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1320 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1321 }
1323 //----------------------------------------------------------------------
1324 // Callback implementations. The virtual ones are connected by the view.
1326 void
1327 SPDesktop::onPositionSet (double x, double y)
1328 {
1329 _widget->viewSetPosition (NR::Point(x,y));
1330 }
1332 void
1333 SPDesktop::onResized (double /*x*/, double /*y*/)
1334 {
1335 // Nothing called here
1336 }
1338 /**
1339 * Redraw callback; queues Gtk redraw; connected by View.
1340 */
1341 void
1342 SPDesktop::onRedrawRequested ()
1343 {
1344 if (main) {
1345 _widget->requestCanvasUpdate();
1346 }
1347 }
1349 void
1350 SPDesktop::updateCanvasNow()
1351 {
1352 _widget->requestCanvasUpdateAndWait();
1353 }
1355 /**
1356 * Associate document with desktop.
1357 */
1358 void
1359 SPDesktop::setDocument (SPDocument *doc)
1360 {
1361 if (this->doc() && doc) {
1362 namedview->hide(this);
1363 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1364 }
1366 if (_layer_hierarchy) {
1367 _layer_hierarchy->clear();
1368 delete _layer_hierarchy;
1369 }
1370 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1371 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1372 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1373 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1374 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1376 /* setup EventLog */
1377 event_log = new Inkscape::EventLog(doc);
1378 doc->addUndoObserver(*event_log);
1380 _commit_connection.disconnect();
1381 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1383 /// \todo fixme: This condition exists to make sure the code
1384 /// inside is NOT called on initialization, only on replacement. But there
1385 /// are surely more safe methods to accomplish this.
1386 // TODO since the comment had reversed logic, check the intent of this block of code:
1387 if (drawing) {
1388 NRArenaItem *ai = 0;
1390 namedview = sp_document_namedview (doc, NULL);
1391 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1392 number = namedview->getViewCount();
1394 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1395 SP_CANVAS_ARENA (drawing)->arena,
1396 dkey,
1397 SP_ITEM_SHOW_DISPLAY);
1398 if (ai) {
1399 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1400 nr_arena_item_unref (ai);
1401 }
1402 namedview->show(this);
1403 /* Ugly hack */
1404 activate_guides (true);
1405 /* Ugly hack */
1406 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1407 }
1409 _document_replaced_signal.emit (this, doc);
1411 View::setDocument (doc);
1412 }
1414 void
1415 SPDesktop::onStatusMessage
1416 (Inkscape::MessageType type, gchar const *message)
1417 {
1418 if (_widget) {
1419 _widget->setMessage(type, message);
1420 }
1421 }
1423 void
1424 SPDesktop::onDocumentURISet (gchar const* uri)
1425 {
1426 _widget->setTitle(uri);
1427 }
1429 /**
1430 * Resized callback.
1431 */
1432 void
1433 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1434 {
1435 _doc2dt[5] = height;
1436 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1437 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1438 SP_CTRLRECT(page)->setRectangle(a);
1439 SP_CTRLRECT(page_border)->setRectangle(a);
1440 }
1443 void
1444 SPDesktop::_onActivate (SPDesktop* dt)
1445 {
1446 if (!dt->_widget) return;
1447 dt->_widget->activateDesktop();
1448 }
1450 void
1451 SPDesktop::_onDeactivate (SPDesktop* dt)
1452 {
1453 if (!dt->_widget) return;
1454 dt->_widget->deactivateDesktop();
1455 }
1457 void
1458 SPDesktop::_onSelectionModified
1459 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1460 {
1461 if (!dt->_widget) return;
1462 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1463 }
1465 static void
1466 _onSelectionChanged
1467 (Inkscape::Selection *selection, SPDesktop *desktop)
1468 {
1469 /** \todo
1470 * only change the layer for single selections, or what?
1471 * This seems reasonable -- for multiple selections there can be many
1472 * different layers involved.
1473 */
1474 SPItem *item=selection->singleItem();
1475 if (item) {
1476 SPObject *layer=desktop->layerForObject(item);
1477 if ( layer && layer != desktop->currentLayer() ) {
1478 desktop->setCurrentLayer(layer);
1479 }
1480 }
1481 }
1483 /**
1484 * Calls event handler of current event context.
1485 * \param arena Unused
1486 * \todo fixme
1487 */
1488 static gint
1489 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1490 {
1491 if (ai) {
1492 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1493 return sp_event_context_item_handler (desktop->event_context, spi, event);
1494 } else {
1495 return sp_event_context_root_handler (desktop->event_context, event);
1496 }
1497 }
1499 static void
1500 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1501 g_return_if_fail(SP_IS_GROUP(layer));
1502 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1503 }
1505 /// Callback
1506 static void
1507 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1508 g_return_if_fail(SP_IS_GROUP(layer));
1509 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1510 }
1512 /// Callback
1513 static void
1514 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1515 SPDesktop *desktop)
1516 {
1517 desktop->_layer_changed_signal.emit (bottom);
1518 }
1520 /// Called when document is starting to be rebuilt.
1521 static void
1522 _reconstruction_start (SPDesktop * desktop)
1523 {
1524 // printf("Desktop, starting reconstruction\n");
1525 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1526 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1528 /*
1529 GSList const * selection_objs = desktop->selection->list();
1530 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1532 }
1533 */
1534 desktop->selection->clear();
1536 // printf("Desktop, starting reconstruction end\n");
1537 }
1539 /// Called when document rebuild is finished.
1540 static void
1541 _reconstruction_finish (SPDesktop * desktop)
1542 {
1543 // printf("Desktop, finishing reconstruction\n");
1544 if (desktop->_reconstruction_old_layer_id == NULL)
1545 return;
1547 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1548 if (newLayer != NULL)
1549 desktop->setCurrentLayer(newLayer);
1551 g_free(desktop->_reconstruction_old_layer_id);
1552 desktop->_reconstruction_old_layer_id = NULL;
1553 // printf("Desktop, finishing reconstruction end\n");
1554 return;
1555 }
1557 /**
1558 * Namedview_modified callback.
1559 */
1560 static void
1561 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1562 {
1563 SPNamedView *nv=SP_NAMEDVIEW(obj);
1565 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1567 /* Recalculate snap distances */
1568 /* FIXME: why is the desktop getting involved in setting up something
1569 ** that is entirely to do with the namedview?
1570 */
1571 _update_snap_distances (desktop);
1573 /* Show/hide page background */
1574 if (nv->pagecolor & 0xff) {
1575 sp_canvas_item_show (desktop->table);
1576 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1577 sp_canvas_item_move_to_z (desktop->table, 0);
1578 } else {
1579 sp_canvas_item_hide (desktop->table);
1580 }
1582 /* Show/hide page border */
1583 if (nv->showborder) {
1584 // show
1585 sp_canvas_item_show (desktop->page_border);
1586 // set color and shadow
1587 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1588 if (nv->pageshadow) {
1589 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1590 }
1591 // place in the z-order stack
1592 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1593 sp_canvas_item_move_to_z (desktop->page_border, 2);
1594 } else {
1595 int order = sp_canvas_item_order (desktop->page_border);
1596 int morder = sp_canvas_item_order (desktop->drawing);
1597 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1598 morder - order);
1599 }
1600 } else {
1601 sp_canvas_item_hide (desktop->page_border);
1602 if (nv->pageshadow) {
1603 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1604 }
1605 }
1607 /* Show/hide page shadow */
1608 if (nv->showpageshadow && nv->pageshadow) {
1609 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1610 } else {
1611 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1612 }
1614 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1615 (SP_RGBA32_R_U(nv->pagecolor) +
1616 SP_RGBA32_G_U(nv->pagecolor) +
1617 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1618 // the background color is light or transparent, use black outline
1619 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1620 } else { // use white outline
1621 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1622 }
1623 }
1624 }
1626 /**
1627 * Callback to reset snapper's distances.
1628 */
1629 static void
1630 _update_snap_distances (SPDesktop *desktop)
1631 {
1632 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1634 SPNamedView &nv = *desktop->namedview;
1636 //tell all grid snappers
1637 for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1638 Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1639 grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1640 *nv.gridtoleranceunit,
1641 px));
1642 }
1644 nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1645 *nv.guidetoleranceunit,
1646 px));
1647 nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1648 *nv.objecttoleranceunit,
1649 px));
1650 }
1653 NR::Matrix SPDesktop::w2d() const
1654 {
1655 return _w2d;
1656 }
1658 NR::Point SPDesktop::w2d(NR::Point const &p) const
1659 {
1660 return p * _w2d;
1661 }
1663 NR::Point SPDesktop::d2w(NR::Point const &p) const
1664 {
1665 return p * _d2w;
1666 }
1668 NR::Matrix SPDesktop::doc2dt() const
1669 {
1670 return _doc2dt;
1671 }
1673 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1674 {
1675 return p * _doc2dt;
1676 }
1678 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1679 {
1680 return p / _doc2dt;
1681 }
1684 /**
1685 * Pop event context from desktop's context stack. Never used.
1686 */
1687 // void
1688 // SPDesktop::pop_event_context (unsigned int key)
1689 // {
1690 // SPEventContext *ec = NULL;
1691 //
1692 // if (event_context && event_context->key == key) {
1693 // g_return_if_fail (event_context);
1694 // g_return_if_fail (event_context->next);
1695 // ec = event_context;
1696 // sp_event_context_deactivate (ec);
1697 // event_context = ec->next;
1698 // sp_event_context_activate (event_context);
1699 // _event_context_changed_signal.emit (this, ec);
1700 // }
1701 //
1702 // SPEventContext *ref = event_context;
1703 // while (ref && ref->next && ref->next->key != key)
1704 // ref = ref->next;
1705 //
1706 // if (ref && ref->next) {
1707 // ec = ref->next;
1708 // ref->next = ec->next;
1709 // }
1710 //
1711 // if (ec) {
1712 // sp_event_context_finish (ec);
1713 // g_object_unref (G_OBJECT (ec));
1714 // }
1715 // }
1717 /*
1718 Local Variables:
1719 mode:c++
1720 c-file-style:"stroustrup"
1721 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1722 indent-tabs-mode:nil
1723 fill-column:99
1724 End:
1725 */
1726 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :