b1b9a29558733ebb207aab2f691af3950814d2f5
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 <2geom/rect.h>
61 #include "macros.h"
62 #include "inkscape-private.h"
63 #include "desktop.h"
64 #include "desktop-events.h"
65 #include "desktop-handles.h"
66 #include "document.h"
67 #include "message-stack.h"
68 #include "selection.h"
69 #include "select-context.h"
70 #include "sp-namedview.h"
71 #include "color.h"
72 #include "sp-item-group.h"
73 #include "preferences.h"
74 #include "object-hierarchy.h"
75 #include "helper/units.h"
76 #include "display/canvas-arena.h"
77 #include "display/nr-arena.h"
78 #include "display/gnome-canvas-acetate.h"
79 #include "display/sodipodi-ctrlrect.h"
80 #include "display/sp-canvas-util.h"
81 #include "display/canvas-temporary-item-list.h"
82 #include "display/snap-indicator.h"
83 #include "ui/dialog/dialog-manager.h"
84 #include "xml/repr.h"
85 #include "message-context.h"
86 #include "device-manager.h"
87 #include "layer-fns.h"
88 #include "layer-manager.h"
89 #include "event-log.h"
90 #include "display/canvas-grid.h"
91 #include "widgets/desktop-widget.h"
92 #include "box3d-context.h"
94 // TODO those includes are only for the braindead quick zoom implementation.
95 // Remove them after fixing this.
96 #include "ui/tool/node-tool.h"
97 #include "ui/tool/control-point-selection.h"
99 #include "display/sp-canvas.h"
101 namespace Inkscape { namespace XML { class Node; }}
103 // Callback declarations
104 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
105 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
106 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
107 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
108 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
109 static void _reconstruction_start(SPDesktop * desktop);
110 static void _reconstruction_finish(SPDesktop * desktop);
111 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
113 /**
114 * Return new desktop object.
115 * \pre namedview != NULL.
116 * \pre canvas != NULL.
117 */
118 SPDesktop::SPDesktop() :
119 _dlg_mgr( 0 ),
120 namedview( 0 ),
121 canvas( 0 ),
122 selection( 0 ),
123 event_context( 0 ),
124 layer_manager( 0 ),
125 event_log( 0 ),
126 temporary_item_list( 0 ),
127 snapindicator( 0 ),
128 acetate( 0 ),
129 main( 0 ),
130 gridgroup( 0 ),
131 guides( 0 ),
132 drawing( 0 ),
133 sketch( 0 ),
134 controls( 0 ),
135 tempgroup ( 0 ),
136 table( 0 ),
137 page( 0 ),
138 page_border( 0 ),
139 current( 0 ),
140 _focusMode(false),
141 zooms_past( 0 ),
142 zooms_future( 0 ),
143 dkey( 0 ),
144 number( 0 ),
145 window_state(0),
146 interaction_disabled_counter( 0 ),
147 waiting_cursor( false ),
148 guides_active( false ),
149 gr_item( 0 ),
150 gr_point_type( 0 ),
151 gr_point_i( 0 ),
152 gr_fill_or_stroke( true ),
153 _layer_hierarchy( 0 ),
154 _reconstruction_old_layer_id( 0 ),
155 _display_mode(Inkscape::RENDERMODE_NORMAL),
156 _widget( 0 ),
157 _inkscape( 0 ),
158 _guides_message_context( 0 ),
159 _active( false ),
160 _w2d(),
161 _d2w(),
162 _doc2dt( Geom::Scale(1, -1) ),
163 grids_visible( false )
164 {
165 _d2w.setIdentity();
166 _w2d.setIdentity();
168 selection = Inkscape::GC::release( new Inkscape::Selection(this) );
169 }
171 void
172 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWidgetInterface *widget)
173 {
174 _widget = widget;
176 // Temporary workaround for link order issues:
177 Inkscape::DeviceManager::getManager().getDevices();
178 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
180 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
182 current = prefs->getStyle("/desktop/style");
184 namedview = nv;
185 canvas = aCanvas;
187 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
188 /* Kill flicker */
189 sp_document_ensure_up_to_date (document);
191 /* Setup Dialog Manager */
192 _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
194 dkey = sp_item_display_key_new (1);
196 /* Connect document */
197 setDocument (document);
199 number = namedview->getViewCount();
202 /* Setup Canvas */
203 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
205 SPCanvasGroup *root = sp_canvas_root (canvas);
207 /* Setup adminstrative layers */
208 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
209 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
210 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
211 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
213 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
214 SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
215 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
216 sp_canvas_item_move_to_z (table, 0);
218 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
219 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
220 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
222 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
223 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
225 SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
227 if (prefs->getBool("/options/startmode/outline")) {
228 // Start in outline mode
229 setDisplayModeOutline();
230 } else {
231 // Start in normal mode, default
232 setDisplayModeNormal();
233 }
235 // The order in which these canvas items are added determines the z-order. It's therefore
236 // important to add the tempgroup (which will contain the snapindicator) before adding the
237 // controls. Only this way one will be able to quickly (before the snap indicator has
238 // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
239 // will not work (the snap indicator is on top of the node handler; is the snapindicator
240 // being selected? or does it intercept some of the events that should have gone to the
241 // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
242 gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
243 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
244 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
245 tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
246 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
248 /* Push select tool to the bottom of stack */
249 /** \todo
250 * FIXME: this is the only call to this. Everything else seems to just
251 * call "set" instead of "push". Can we assume that there is only one
252 * context ever?
253 */
254 push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
256 // display rect and zoom are now handled in sp_desktop_widget_realize()
258 Geom::Rect const d(Geom::Point(0.0, 0.0),
259 Geom::Point(sp_document_width(document), sp_document_height(document)));
261 SP_CTRLRECT(page)->setRectangle(d);
262 SP_CTRLRECT(page_border)->setRectangle(d);
264 /* the following sets the page shadow on the canvas
265 It was originally set to 5, which is really cheesy!
266 It now is an attribute in the document's namedview. If a value of
267 0 is used, then the constructor for a shadow is not initialized.
268 */
270 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
271 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
272 }
275 /* Connect event for page resize */
276 _doc2dt[5] = sp_document_height (document);
277 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
279 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
281 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
282 SP_CANVAS_ARENA (drawing)->arena,
283 dkey,
284 SP_ITEM_SHOW_DISPLAY);
285 if (ai) {
286 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
287 }
289 namedview->show(this);
290 /* Ugly hack */
291 activate_guides (true);
292 /* Ugly hack */
293 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
295 /* Set up notification of rebuilding the document, this allows
296 for saving object related settings in the document. */
297 _reconstruction_start_connection =
298 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
299 _reconstruction_finish_connection =
300 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
301 _reconstruction_old_layer_id = NULL;
303 // ?
304 // sp_active_desktop_set (desktop);
305 _inkscape = INKSCAPE;
307 _activate_connection = _activate_signal.connect(
308 sigc::bind(
309 sigc::ptr_fun(_onActivate),
310 this
311 )
312 );
313 _deactivate_connection = _deactivate_signal.connect(
314 sigc::bind(
315 sigc::ptr_fun(_onDeactivate),
316 this
317 )
318 );
320 _sel_modified_connection = selection->connectModified(
321 sigc::bind(
322 sigc::ptr_fun(&_onSelectionModified),
323 this
324 )
325 );
326 _sel_changed_connection = selection->connectChanged(
327 sigc::bind(
328 sigc::ptr_fun(&_onSelectionChanged),
329 this
330 )
331 );
334 /* setup LayerManager */
335 // (Setting up after the connections are all in place, as it may use some of them)
336 layer_manager = new Inkscape::LayerManager( this );
338 showGrids(namedview->grids_visible, false);
340 temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
341 snapindicator = new Inkscape::Display::SnapIndicator ( this );
342 }
345 void SPDesktop::destroy()
346 {
347 if (snapindicator) {
348 delete snapindicator;
349 snapindicator = NULL;
350 }
351 if (temporary_item_list) {
352 delete temporary_item_list;
353 temporary_item_list = NULL;
354 }
356 if (selection) {
357 delete selection;
358 selection = NULL;
359 }
361 namedview->hide(this);
363 _activate_connection.disconnect();
364 _deactivate_connection.disconnect();
365 _sel_modified_connection.disconnect();
366 _sel_changed_connection.disconnect();
367 _modified_connection.disconnect();
368 _commit_connection.disconnect();
369 _reconstruction_start_connection.disconnect();
370 _reconstruction_finish_connection.disconnect();
372 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
373 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
374 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
376 while (event_context) {
377 SPEventContext *ec = event_context;
378 event_context = ec->next;
379 sp_event_context_finish (ec);
380 g_object_unref (G_OBJECT (ec));
381 }
383 if (_layer_hierarchy) {
384 delete _layer_hierarchy;
385 // _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
386 }
388 if (layer_manager) {
389 delete layer_manager;
390 layer_manager = NULL;
391 }
393 if (_inkscape) {
394 _inkscape = NULL;
395 }
397 if (drawing) {
398 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
399 drawing = NULL;
400 }
402 delete _guides_message_context;
403 _guides_message_context = NULL;
405 g_list_free (zooms_past);
406 g_list_free (zooms_future);
407 }
409 SPDesktop::~SPDesktop() {}
411 //--------------------------------------------------------------------
412 /* Public methods */
415 /* These methods help for temporarily showing things on-canvas.
416 * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
417 * is when you want to prematurely remove the item from the canvas, by calling
418 * desktop->remove_temporary_canvasitem(tempitem).
419 */
420 /** Note that lifetime is measured in milliseconds
421 * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
422 * delete the object for you and the reference will become invalid without you knowing it.
423 * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
424 * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
425 * because the object might be deleted already without you knowing it.
426 * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
427 */
428 Inkscape::Display::TemporaryItem *
429 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
430 {
431 if (move_to_bottom) {
432 sp_canvas_item_move_to_z(item, 0);
433 }
435 return temporary_item_list->add_item(item, lifetime);
436 }
438 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
439 */
440 void
441 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
442 {
443 // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
444 if (tempitem && temporary_item_list) {
445 temporary_item_list->delete_item(tempitem);
446 }
447 }
449 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
450 SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
451 canvas->rendermode = mode;
452 _display_mode = mode;
453 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
454 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
455 }
457 void SPDesktop::displayModeToggle() {
458 switch (_display_mode) {
459 case Inkscape::RENDERMODE_NORMAL:
460 _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
461 break;
462 case Inkscape::RENDERMODE_NO_FILTERS:
463 _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
464 break;
465 case Inkscape::RENDERMODE_OUTLINE:
466 default:
467 _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
468 }
469 }
471 /**
472 * Returns current root (=bottom) layer.
473 */
474 SPObject *SPDesktop::currentRoot() const
475 {
476 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
477 }
479 /**
480 * Returns current top layer.
481 */
482 SPObject *SPDesktop::currentLayer() const
483 {
484 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
485 }
487 /**
488 * Sets the current layer of the desktop.
489 *
490 * Make \a object the top layer.
491 */
492 void SPDesktop::setCurrentLayer(SPObject *object) {
493 g_return_if_fail(SP_IS_GROUP(object));
494 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
495 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
496 _layer_hierarchy->setBottom(object);
497 }
499 void SPDesktop::toggleLayerSolo(SPObject *object) {
500 g_return_if_fail(SP_IS_GROUP(object));
501 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
503 bool othersShowing = false;
504 std::vector<SPObject*> layers;
505 for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
506 layers.push_back(obj);
507 othersShowing |= !SP_ITEM(obj)->isHidden();
508 }
509 for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
510 layers.push_back(obj);
511 othersShowing |= !SP_ITEM(obj)->isHidden();
512 }
515 if ( SP_ITEM(object)->isHidden() ) {
516 SP_ITEM(object)->setHidden(false);
517 }
519 for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
520 SP_ITEM(*it)->setHidden(othersShowing);
521 }
522 }
524 /**
525 * Return layer that contains \a object.
526 */
527 SPObject *SPDesktop::layerForObject(SPObject *object) {
528 g_return_val_if_fail(object != NULL, NULL);
530 SPObject *root=currentRoot();
531 object = SP_OBJECT_PARENT(object);
532 while ( object && object != root && !isLayer(object) ) {
533 object = SP_OBJECT_PARENT(object);
534 }
535 return object;
536 }
538 /**
539 * True if object is a layer.
540 */
541 bool SPDesktop::isLayer(SPObject *object) const {
542 return ( SP_IS_GROUP(object)
543 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
544 == SPGroup::LAYER ) );
545 }
547 /**
548 * True if desktop viewport fully contains \a item's bbox.
549 */
550 bool SPDesktop::isWithinViewport (SPItem *item) const
551 {
552 Geom::Rect const viewport = get_display_area();
553 Geom::OptRect const bbox = sp_item_bbox_desktop(item);
554 if (bbox) {
555 return viewport.contains(*bbox);
556 } else {
557 return true;
558 }
559 }
561 ///
562 bool SPDesktop::itemIsHidden(SPItem const *item) const {
563 return item->isHidden(this->dkey);
564 }
566 /**
567 * Set activate property of desktop; emit signal if changed.
568 */
569 void
570 SPDesktop::set_active (bool new_active)
571 {
572 if (new_active != _active) {
573 _active = new_active;
574 if (new_active) {
575 _activate_signal.emit();
576 } else {
577 _deactivate_signal.emit();
578 }
579 }
580 }
582 /**
583 * Set activate status of current desktop's named view.
584 */
585 void
586 SPDesktop::activate_guides(bool activate)
587 {
588 guides_active = activate;
589 namedview->activateGuides(this, activate);
590 }
592 /**
593 * Make desktop switch documents.
594 */
595 void
596 SPDesktop::change_document (SPDocument *theDocument)
597 {
598 g_return_if_fail (theDocument != NULL);
600 /* unselect everything before switching documents */
601 selection->clear();
603 setDocument (theDocument);
605 /* update the rulers, connect the desktop widget's signal to the new namedview etc.
606 (this can probably be done in a better way) */
607 Gtk::Window *parent = this->getToplevel();
608 g_assert(parent != NULL);
609 SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
610 if (dtw) dtw->desktop = this;
611 sp_desktop_widget_update_namedview(dtw);
613 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
614 _document_replaced_signal.emit (this, theDocument);
615 }
617 /**
618 * Make desktop switch event contexts.
619 */
620 void
621 SPDesktop::set_event_context (GtkType type, const gchar *config)
622 {
623 SPEventContext *ec;
624 while (event_context) {
625 ec = event_context;
626 sp_event_context_deactivate (ec);
627 // we have to keep event_context valid during destruction - otherwise writing
628 // destructors is next to impossible
629 SPEventContext *next = ec->next;
630 sp_event_context_finish (ec);
631 g_object_unref (G_OBJECT (ec));
632 event_context = next;
633 }
635 ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
636 ec->next = event_context;
637 event_context = ec;
638 sp_event_context_activate (ec);
639 _event_context_changed_signal.emit (this, ec);
640 }
642 /**
643 * Push event context onto desktop's context stack.
644 */
645 void
646 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
647 {
648 SPEventContext *ref, *ec;
650 if (event_context && event_context->key == key) return;
651 ref = event_context;
652 while (ref && ref->next && ref->next->key != key) ref = ref->next;
653 if (ref && ref->next) {
654 ec = ref->next;
655 ref->next = ec->next;
656 sp_event_context_finish (ec);
657 g_object_unref (G_OBJECT (ec));
658 }
660 if (event_context) sp_event_context_deactivate (event_context);
661 ec = sp_event_context_new (type, this, config, key);
662 ec->next = event_context;
663 event_context = ec;
664 sp_event_context_activate (ec);
665 _event_context_changed_signal.emit (this, ec);
666 }
668 /**
669 * Sets the coordinate status to a given point
670 */
671 void
672 SPDesktop::set_coordinate_status (Geom::Point p) {
673 _widget->setCoordinateStatus(p);
674 }
676 /**
677 * \see sp_document_item_from_list_at_point_bottom()
678 */
679 SPItem *
680 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
681 {
682 g_return_val_if_fail (doc() != NULL, NULL);
683 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
684 }
686 /**
687 * \see sp_document_item_at_point()
688 */
689 SPItem *
690 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
691 {
692 g_return_val_if_fail (doc() != NULL, NULL);
693 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
694 }
696 /**
697 * \see sp_document_group_at_point()
698 */
699 SPItem *
700 SPDesktop::group_at_point (Geom::Point const p) const
701 {
702 g_return_val_if_fail (doc() != NULL, NULL);
703 return sp_document_group_at_point (doc(), dkey, p);
704 }
706 /**
707 * \brief Returns the mouse point in document coordinates; if mouse is
708 * outside the canvas, returns the center of canvas viewpoint
709 */
710 Geom::Point
711 SPDesktop::point() const
712 {
713 Geom::Point p = _widget->getPointer();
714 Geom::Point pw = sp_canvas_window_to_world (canvas, p);
715 p = w2d(pw);
717 Geom::Rect const r = canvas->getViewbox();
719 Geom::Point r0 = w2d(r.min());
720 Geom::Point r1 = w2d(r.max());
722 if (p[Geom::X] >= r0[Geom::X] &&
723 p[Geom::X] <= r1[Geom::X] &&
724 p[Geom::Y] >= r1[Geom::Y] &&
725 p[Geom::Y] <= r0[Geom::Y])
726 {
727 return p;
728 } else {
729 return (r0 + r1) / 2;
730 }
731 }
733 /**
734 * Put current zoom data in history list.
735 */
736 void
737 SPDesktop::push_current_zoom (GList **history)
738 {
739 Geom::Rect const area = get_display_area();
741 NRRect *old_zoom = g_new(NRRect, 1);
742 old_zoom->x0 = area.min()[Geom::X];
743 old_zoom->x1 = area.max()[Geom::X];
744 old_zoom->y0 = area.min()[Geom::Y];
745 old_zoom->y1 = area.max()[Geom::Y];
746 if ( *history == NULL
747 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
748 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
749 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
750 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
751 {
752 *history = g_list_prepend (*history, old_zoom);
753 }
754 }
756 /**
757 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
758 */
759 void
760 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
761 {
762 g_assert(_widget);
764 // save the zoom
765 if (log) {
766 push_current_zoom(&zooms_past);
767 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
768 g_list_free (zooms_future);
769 zooms_future = NULL;
770 }
772 double const cx = 0.5 * (x0 + x1);
773 double const cy = 0.5 * (y0 + y1);
775 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
776 Geom::Rect viewbox = canvas->getViewbox();
777 viewbox.expandBy(-border);
779 double scale = _d2w.descrim();
780 double newscale;
781 if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
782 newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
783 } else {
784 newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
785 }
787 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
789 int clear = FALSE;
790 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
791 // zoom changed - set new zoom factors
792 _d2w = Geom::Scale(newscale, -newscale);
793 _w2d = Geom::Scale(1/newscale, 1/-newscale);
794 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
795 clear = TRUE;
796 signal_zoom_changed.emit(_d2w.descrim());
797 }
799 /* Calculate top left corner (in document pixels) */
800 x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
801 y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
803 /* Scroll */
804 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
806 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
807 sp_box3d_context_update_lines(event_context);
809 _widget->updateRulers();
810 _widget->updateScrollbars(_d2w.descrim());
811 _widget->updateZoom();
812 }
814 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
815 {
816 set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
817 }
819 /**
820 * Return viewbox dimensions.
821 */
822 Geom::Rect SPDesktop::get_display_area() const
823 {
824 Geom::Rect const viewbox = canvas->getViewbox();
826 double const scale = _d2w[0];
828 return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
829 Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
830 }
832 /**
833 * Revert back to previous zoom if possible.
834 */
835 void
836 SPDesktop::prev_zoom()
837 {
838 if (zooms_past == NULL) {
839 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
840 return;
841 }
843 // push current zoom into forward zooms list
844 push_current_zoom (&zooms_future);
846 // restore previous zoom
847 set_display_area (((NRRect *) zooms_past->data)->x0,
848 ((NRRect *) zooms_past->data)->y0,
849 ((NRRect *) zooms_past->data)->x1,
850 ((NRRect *) zooms_past->data)->y1,
851 0, false);
853 // remove the just-added zoom from the past zooms list
854 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
855 }
857 /**
858 * Set zoom to next in list.
859 */
860 void
861 SPDesktop::next_zoom()
862 {
863 if (zooms_future == NULL) {
864 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
865 return;
866 }
868 // push current zoom into past zooms list
869 push_current_zoom (&zooms_past);
871 // restore next zoom
872 set_display_area (((NRRect *) zooms_future->data)->x0,
873 ((NRRect *) zooms_future->data)->y0,
874 ((NRRect *) zooms_future->data)->x1,
875 ((NRRect *) zooms_future->data)->y1,
876 0, false);
878 // remove the just-used zoom from the zooms_future list
879 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
880 }
882 /** \brief Performs a quick zoom into what the user is working on
883 \param enable Whether we're going in or out of quick zoom
885 */
886 void
887 SPDesktop::zoom_quick (bool enable)
888 {
889 if (enable == _quick_zoom_enabled) {
890 return;
891 }
893 if (enable == true) {
894 _quick_zoom_stored_area = get_display_area();
895 bool zoomed = false;
897 // TODO This is brain damage. This needs to migrate into the node tool,
898 // but currently the design of this method is sufficiently broken
899 // to prevent this.
900 if (!zoomed && INK_IS_NODE_TOOL(event_context)) {
901 InkNodeTool *nt = static_cast<InkNodeTool*>(event_context);
902 if (!nt->_selected_nodes->empty()) {
903 Geom::Rect nodes = *nt->_selected_nodes->bounds();
904 double area = nodes.area();
905 // do not zoom if a single cusp node is selected aand the bounds
906 // have zero area.
907 if (!Geom::are_near(area, 0) && area * 2.0 < _quick_zoom_stored_area.area()) {
908 set_display_area(nodes, true);
909 zoomed = true;
910 }
911 }
912 }
914 if (!zoomed) {
915 Geom::OptRect const d = selection->bounds();
916 if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
917 set_display_area(*d, true);
918 zoomed = true;
919 }
920 }
922 if (!zoomed) {
923 zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
924 zoomed = true;
925 }
926 } else {
927 set_display_area(_quick_zoom_stored_area, false);
928 }
930 _quick_zoom_enabled = enable;
931 return;
932 }
934 /**
935 * Zoom to point with absolute zoom factor.
936 */
937 void
938 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
939 {
940 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
942 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
943 // this check prevents "sliding" when trying to zoom in at maximum zoom;
944 /// \todo someone please fix calculations properly and remove this hack
945 if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
946 return;
948 Geom::Rect const viewbox = canvas->getViewbox();
950 double const width2 = viewbox.dimensions()[Geom::X] / zoom;
951 double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
953 set_display_area(cx - px * width2,
954 cy - py * height2,
955 cx + (1 - px) * width2,
956 cy + (1 - py) * height2,
957 0.0);
958 }
960 /**
961 * Zoom to center with absolute zoom factor.
962 */
963 void
964 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
965 {
966 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
967 }
969 /**
970 * Zoom to point with relative zoom factor.
971 */
972 void
973 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
974 {
975 Geom::Rect const area = get_display_area();
977 if (cx < area.min()[Geom::X]) {
978 cx = area.min()[Geom::X];
979 }
980 if (cx > area.max()[Geom::X]) {
981 cx = area.max()[Geom::X];
982 }
983 if (cy < area.min()[Geom::Y]) {
984 cy = area.min()[Geom::Y];
985 }
986 if (cy > area.max()[Geom::Y]) {
987 cy = area.max()[Geom::Y];
988 }
990 gdouble const scale = _d2w.descrim() * zoom;
991 double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
992 double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
994 zoom_absolute_keep_point(cx, cy, px, py, scale);
995 }
997 /**
998 * Zoom to center with relative zoom factor.
999 */
1000 void
1001 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1002 {
1003 gdouble scale = _d2w.descrim() * zoom;
1004 zoom_absolute (cx, cy, scale);
1005 }
1007 /**
1008 * Set display area to origin and current document dimensions.
1009 */
1010 void
1011 SPDesktop::zoom_page()
1012 {
1013 Geom::Rect d(Geom::Point(0, 0),
1014 Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1016 if (d.minExtent() < 1.0) {
1017 return;
1018 }
1020 set_display_area(d, 10);
1021 }
1023 /**
1024 * Set display area to current document width.
1025 */
1026 void
1027 SPDesktop::zoom_page_width()
1028 {
1029 Geom::Rect const a = get_display_area();
1031 if (sp_document_width(doc()) < 1.0) {
1032 return;
1033 }
1035 Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1036 Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1038 set_display_area(d, 10);
1039 }
1041 /**
1042 * Zoom to selection.
1043 */
1044 void
1045 SPDesktop::zoom_selection()
1046 {
1047 Geom::OptRect const d = selection->bounds();
1049 if ( !d || d->minExtent() < 0.1 ) {
1050 return;
1051 }
1053 set_display_area(*d, 10);
1054 }
1056 /**
1057 * Tell widget to let zoom widget grab keyboard focus.
1058 */
1059 void
1060 SPDesktop::zoom_grab_focus()
1061 {
1062 _widget->letZoomGrabFocus();
1063 }
1065 /**
1066 * Zoom to whole drawing.
1067 */
1068 void
1069 SPDesktop::zoom_drawing()
1070 {
1071 g_return_if_fail (doc() != NULL);
1072 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1073 g_return_if_fail (docitem != NULL);
1075 Geom::OptRect d = sp_item_bbox_desktop(docitem);
1077 /* Note that the second condition here indicates that
1078 ** there are no items in the drawing.
1079 */
1080 if ( !d || d->minExtent() < 0.1 ) {
1081 return;
1082 }
1084 set_display_area(*d, 10);
1085 }
1087 /**
1088 * Scroll canvas by specific coordinate amount in svg coordinates.
1089 */
1090 void
1091 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1092 {
1093 double scale = _d2w.descrim();
1094 scroll_world(dx*scale, dy*scale, is_scrolling);
1095 }
1097 /**
1098 * Scroll canvas by specific coordinate amount.
1099 */
1100 void
1101 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1102 {
1103 g_assert(_widget);
1105 Geom::Rect const viewbox = canvas->getViewbox();
1107 sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1109 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1110 sp_box3d_context_update_lines(event_context);
1112 _widget->updateRulers();
1113 _widget->updateScrollbars(_d2w.descrim());
1114 }
1116 bool
1117 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1118 {
1119 using Geom::X;
1120 using Geom::Y;
1122 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1123 gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1125 // autoscrolldistance is in screen pixels, but the display area is in document units
1126 autoscrolldistance /= _d2w.descrim();
1127 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1128 Geom::Rect dbox = get_display_area();
1129 dbox.expandBy(-autoscrolldistance);
1131 if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1132 !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y]) ) {
1134 Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1136 gdouble x_to;
1137 if (p[X] < dbox.min()[X])
1138 x_to = dbox.min()[X];
1139 else if (p[X] > dbox.max()[X])
1140 x_to = dbox.max()[X];
1141 else
1142 x_to = p[X];
1144 gdouble y_to;
1145 if (p[Y] < dbox.min()[Y])
1146 y_to = dbox.min()[Y];
1147 else if (p[Y] > dbox.max()[Y])
1148 y_to = dbox.max()[Y];
1149 else
1150 y_to = p[Y];
1152 Geom::Point const d_dt(x_to, y_to);
1153 Geom::Point const d_w( d_dt * _d2w );
1154 Geom::Point const moved_w( d_w - s_w );
1156 if (autoscrollspeed == 0)
1157 autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1159 if (autoscrollspeed != 0)
1160 scroll_world (autoscrollspeed * moved_w);
1162 return true;
1163 }
1164 return false;
1165 }
1167 bool
1168 SPDesktop::is_iconified()
1169 {
1170 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1171 }
1173 void
1174 SPDesktop::iconify()
1175 {
1176 _widget->setIconified();
1177 }
1179 bool
1180 SPDesktop::is_maximized()
1181 {
1182 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1183 }
1185 void
1186 SPDesktop::maximize()
1187 {
1188 _widget->setMaximized();
1189 }
1191 bool
1192 SPDesktop::is_fullscreen()
1193 {
1194 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1195 }
1197 void
1198 SPDesktop::fullscreen()
1199 {
1200 _widget->setFullscreen();
1201 }
1203 /** \brief Checks to see if the user is working in focused mode
1205 Returns the value of \c _focusMode
1206 */
1207 bool
1208 SPDesktop::is_focusMode()
1209 {
1210 return _focusMode;
1211 }
1213 /** \brief Changes whether the user is in focus mode or not
1214 \param mode Which mode the view should be in
1216 */
1217 void
1218 SPDesktop::focusMode (bool mode)
1219 {
1220 if (mode == _focusMode) { return; }
1222 _focusMode = mode;
1224 layoutWidget();
1225 //sp_desktop_widget_layout(SPDesktopWidget);
1227 return;
1228 }
1230 void
1231 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1232 {
1233 _widget->getGeometry (x, y, w, h);
1234 }
1236 void
1237 SPDesktop::setWindowPosition (Geom::Point p)
1238 {
1239 _widget->setPosition (p);
1240 }
1242 void
1243 SPDesktop::setWindowSize (gint w, gint h)
1244 {
1245 _widget->setSize (w, h);
1246 }
1248 void
1249 SPDesktop::setWindowTransient (void *p, int transient_policy)
1250 {
1251 _widget->setTransient (p, transient_policy);
1252 }
1254 Gtk::Window*
1255 SPDesktop::getToplevel( )
1256 {
1257 return _widget->getWindow();
1258 }
1260 void
1261 SPDesktop::presentWindow()
1262 {
1263 _widget->present();
1264 }
1266 bool
1267 SPDesktop::warnDialog (gchar *text)
1268 {
1269 return _widget->warnDialog (text);
1270 }
1272 void
1273 SPDesktop::toggleRulers()
1274 {
1275 _widget->toggleRulers();
1276 }
1278 void
1279 SPDesktop::toggleScrollbars()
1280 {
1281 _widget->toggleScrollbars();
1282 }
1284 void
1285 SPDesktop::layoutWidget()
1286 {
1287 _widget->layout();
1288 }
1290 void
1291 SPDesktop::destroyWidget()
1292 {
1293 _widget->destroy();
1294 }
1296 bool
1297 SPDesktop::shutdown()
1298 {
1299 return _widget->shutdown();
1300 }
1302 bool SPDesktop::onDeleteUI (GdkEventAny*)
1303 {
1304 if(shutdown())
1305 return true;
1307 destroyWidget();
1308 return false;
1309 }
1311 /**
1312 * onWindowStateEvent
1313 *
1314 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1315 * Since GTK doesn't have a way to query this state information directly, we
1316 * record it for the desktop here, and also possibly trigger a layout.
1317 */
1318 bool
1319 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1320 {
1321 // Record the desktop window's state
1322 window_state = event->new_window_state;
1324 // Layout may differ depending on full-screen mode or not
1325 GdkWindowState changed = event->changed_mask;
1326 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1327 layoutWidget();
1328 }
1330 return false;
1331 }
1333 void
1334 SPDesktop::setToolboxFocusTo (gchar const *label)
1335 {
1336 _widget->setToolboxFocusTo (label);
1337 }
1339 void
1340 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1341 {
1342 _widget->setToolboxAdjustmentValue (id, val);
1343 }
1345 void
1346 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1347 {
1348 _widget->setToolboxSelectOneValue (id, val);
1349 }
1351 bool
1352 SPDesktop::isToolboxButtonActive (gchar const *id)
1353 {
1354 return _widget->isToolboxButtonActive (id);
1355 }
1357 void
1358 SPDesktop::emitToolSubselectionChanged(gpointer data)
1359 {
1360 _tool_subselection_changed.emit(data);
1361 inkscape_subselection_changed (this);
1362 }
1364 void
1365 SPDesktop::updateNow()
1366 {
1367 sp_canvas_update_now(canvas);
1368 }
1370 void
1371 SPDesktop::enableInteraction()
1372 {
1373 _widget->enableInteraction();
1374 }
1376 void SPDesktop::disableInteraction()
1377 {
1378 _widget->disableInteraction();
1379 }
1381 void SPDesktop::setWaitingCursor()
1382 {
1383 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1384 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1385 gdk_cursor_unref(waiting);
1386 // GDK needs the flush for the cursor change to take effect
1387 gdk_flush();
1388 waiting_cursor = true;
1389 }
1391 void SPDesktop::clearWaitingCursor()
1392 {
1393 if (waiting_cursor)
1394 sp_event_context_update_cursor(sp_desktop_event_context(this));
1395 }
1397 void SPDesktop::toggleColorProfAdjust()
1398 {
1399 _widget->toggleColorProfAdjust();
1400 }
1402 void SPDesktop::toggleGrids()
1403 {
1404 if (namedview->grids) {
1405 if(gridgroup) {
1406 showGrids(!grids_visible);
1407 }
1408 } else {
1409 //there is no grid present at the moment. add a rectangular grid and make it visible
1410 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1411 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1412 showGrids(true);
1413 }
1414 }
1416 void SPDesktop::showGrids(bool show, bool dirty_document)
1417 {
1418 grids_visible = show;
1419 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1420 if (show) {
1421 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1422 } else {
1423 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1424 }
1425 }
1427 void SPDesktop::toggleSnapGlobal()
1428 {
1429 bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1430 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1431 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1432 }
1434 //----------------------------------------------------------------------
1435 // Callback implementations. The virtual ones are connected by the view.
1437 void
1438 SPDesktop::onPositionSet (double x, double y)
1439 {
1440 _widget->viewSetPosition (Geom::Point(x,y));
1441 }
1443 void
1444 SPDesktop::onResized (double /*x*/, double /*y*/)
1445 {
1446 // Nothing called here
1447 }
1449 /**
1450 * Redraw callback; queues Gtk redraw; connected by View.
1451 */
1452 void
1453 SPDesktop::onRedrawRequested ()
1454 {
1455 if (main) {
1456 _widget->requestCanvasUpdate();
1457 }
1458 }
1460 void
1461 SPDesktop::updateCanvasNow()
1462 {
1463 _widget->requestCanvasUpdateAndWait();
1464 }
1466 /**
1467 * Associate document with desktop.
1468 */
1469 void
1470 SPDesktop::setDocument (SPDocument *doc)
1471 {
1472 if (this->doc() && doc) {
1473 namedview->hide(this);
1474 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1475 }
1477 if (_layer_hierarchy) {
1478 _layer_hierarchy->clear();
1479 delete _layer_hierarchy;
1480 }
1481 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1482 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1483 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1484 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1485 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1487 /* setup EventLog */
1488 event_log = new Inkscape::EventLog(doc);
1489 doc->addUndoObserver(*event_log);
1491 _commit_connection.disconnect();
1492 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1494 /// \todo fixme: This condition exists to make sure the code
1495 /// inside is NOT called on initialization, only on replacement. But there
1496 /// are surely more safe methods to accomplish this.
1497 // TODO since the comment had reversed logic, check the intent of this block of code:
1498 if (drawing) {
1499 NRArenaItem *ai = 0;
1501 namedview = sp_document_namedview (doc, NULL);
1502 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1503 number = namedview->getViewCount();
1505 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1506 SP_CANVAS_ARENA (drawing)->arena,
1507 dkey,
1508 SP_ITEM_SHOW_DISPLAY);
1509 if (ai) {
1510 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1511 }
1512 namedview->show(this);
1513 /* Ugly hack */
1514 activate_guides (true);
1515 /* Ugly hack */
1516 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1517 }
1519 _document_replaced_signal.emit (this, doc);
1521 View::setDocument (doc);
1522 }
1524 void
1525 SPDesktop::onStatusMessage
1526 (Inkscape::MessageType type, gchar const *message)
1527 {
1528 if (_widget) {
1529 _widget->setMessage(type, message);
1530 }
1531 }
1533 void
1534 SPDesktop::onDocumentURISet (gchar const* uri)
1535 {
1536 _widget->setTitle(uri);
1537 }
1539 /**
1540 * Resized callback.
1541 */
1542 void
1543 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1544 {
1545 _doc2dt[5] = height;
1546 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1547 Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1548 SP_CTRLRECT(page)->setRectangle(a);
1549 SP_CTRLRECT(page_border)->setRectangle(a);
1550 }
1553 void
1554 SPDesktop::_onActivate (SPDesktop* dt)
1555 {
1556 if (!dt->_widget) return;
1557 dt->_widget->activateDesktop();
1558 }
1560 void
1561 SPDesktop::_onDeactivate (SPDesktop* dt)
1562 {
1563 if (!dt->_widget) return;
1564 dt->_widget->deactivateDesktop();
1565 }
1567 void
1568 SPDesktop::_onSelectionModified
1569 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1570 {
1571 if (!dt->_widget) return;
1572 dt->_widget->updateScrollbars (dt->_d2w.descrim());
1573 }
1575 static void
1576 _onSelectionChanged
1577 (Inkscape::Selection *selection, SPDesktop *desktop)
1578 {
1579 /** \todo
1580 * only change the layer for single selections, or what?
1581 * This seems reasonable -- for multiple selections there can be many
1582 * different layers involved.
1583 */
1584 SPItem *item=selection->singleItem();
1585 if (item) {
1586 SPObject *layer=desktop->layerForObject(item);
1587 if ( layer && layer != desktop->currentLayer() ) {
1588 desktop->setCurrentLayer(layer);
1589 }
1590 }
1591 }
1593 /**
1594 * Calls event handler of current event context.
1595 * \param arena Unused
1596 * \todo fixme
1597 */
1598 static gint
1599 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1600 {
1601 if (ai) {
1602 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1603 return sp_event_context_item_handler (desktop->event_context, spi, event);
1604 } else {
1605 return sp_event_context_root_handler (desktop->event_context, event);
1606 }
1607 }
1609 static void
1610 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1611 g_return_if_fail(SP_IS_GROUP(layer));
1612 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1613 }
1615 /// Callback
1616 static void
1617 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1618 g_return_if_fail(SP_IS_GROUP(layer));
1619 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1620 }
1622 /// Callback
1623 static void
1624 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1625 SPDesktop *desktop)
1626 {
1627 desktop->_layer_changed_signal.emit (bottom);
1628 }
1630 /// Called when document is starting to be rebuilt.
1631 static void
1632 _reconstruction_start (SPDesktop * desktop)
1633 {
1634 // printf("Desktop, starting reconstruction\n");
1635 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1636 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1638 /*
1639 GSList const * selection_objs = desktop->selection->list();
1640 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1642 }
1643 */
1644 desktop->selection->clear();
1646 // printf("Desktop, starting reconstruction end\n");
1647 }
1649 /// Called when document rebuild is finished.
1650 static void
1651 _reconstruction_finish (SPDesktop * desktop)
1652 {
1653 // printf("Desktop, finishing reconstruction\n");
1654 if (desktop->_reconstruction_old_layer_id == NULL)
1655 return;
1657 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1658 if (newLayer != NULL)
1659 desktop->setCurrentLayer(newLayer);
1661 g_free(desktop->_reconstruction_old_layer_id);
1662 desktop->_reconstruction_old_layer_id = NULL;
1663 // printf("Desktop, finishing reconstruction end\n");
1664 return;
1665 }
1667 /**
1668 * Namedview_modified callback.
1669 */
1670 static void
1671 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1672 {
1673 SPNamedView *nv=SP_NAMEDVIEW(obj);
1675 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1677 /* Show/hide page background */
1678 if (nv->pagecolor & 0xff) {
1679 sp_canvas_item_show (desktop->table);
1680 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1681 sp_canvas_item_move_to_z (desktop->table, 0);
1682 } else {
1683 sp_canvas_item_hide (desktop->table);
1684 }
1686 /* Show/hide page border */
1687 if (nv->showborder) {
1688 // show
1689 sp_canvas_item_show (desktop->page_border);
1690 // set color and shadow
1691 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1692 if (nv->pageshadow) {
1693 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1694 }
1695 // place in the z-order stack
1696 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1697 sp_canvas_item_move_to_z (desktop->page_border, 2);
1698 } else {
1699 int order = sp_canvas_item_order (desktop->page_border);
1700 int morder = sp_canvas_item_order (desktop->drawing);
1701 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1702 morder - order);
1703 }
1704 } else {
1705 sp_canvas_item_hide (desktop->page_border);
1706 if (nv->pageshadow) {
1707 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1708 }
1709 }
1711 /* Show/hide page shadow */
1712 if (nv->showpageshadow && nv->pageshadow) {
1713 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1714 } else {
1715 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1716 }
1718 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1719 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1720 (SP_RGBA32_R_U(nv->pagecolor) +
1721 SP_RGBA32_G_U(nv->pagecolor) +
1722 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1723 // the background color is light or transparent, use black outline
1724 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1725 } else { // use white outline
1726 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1727 }
1728 }
1729 }
1731 Geom::Matrix SPDesktop::w2d() const
1732 {
1733 return _w2d;
1734 }
1736 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1737 {
1738 return p * _w2d;
1739 }
1741 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1742 {
1743 return p * _d2w;
1744 }
1746 Geom::Matrix SPDesktop::doc2dt() const
1747 {
1748 return _doc2dt;
1749 }
1751 Geom::Matrix SPDesktop::dt2doc() const
1752 {
1753 // doc2dt is its own inverse
1754 return _doc2dt;
1755 }
1757 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1758 {
1759 return p * _doc2dt;
1760 }
1762 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1763 {
1764 return p * dt2doc();
1765 }
1768 /*
1769 * Pop event context from desktop's context stack. Never used.
1770 */
1771 // void
1772 // SPDesktop::pop_event_context (unsigned int key)
1773 // {
1774 // SPEventContext *ec = NULL;
1775 //
1776 // if (event_context && event_context->key == key) {
1777 // g_return_if_fail (event_context);
1778 // g_return_if_fail (event_context->next);
1779 // ec = event_context;
1780 // sp_event_context_deactivate (ec);
1781 // event_context = ec->next;
1782 // sp_event_context_activate (event_context);
1783 // _event_context_changed_signal.emit (this, ec);
1784 // }
1785 //
1786 // SPEventContext *ref = event_context;
1787 // while (ref && ref->next && ref->next->key != key)
1788 // ref = ref->next;
1789 //
1790 // if (ref && ref->next) {
1791 // ec = ref->next;
1792 // ref->next = ec->next;
1793 // }
1794 //
1795 // if (ec) {
1796 // sp_event_context_finish (ec);
1797 // g_object_unref (G_OBJECT (ec));
1798 // }
1799 // }
1801 /*
1802 Local Variables:
1803 mode:c++
1804 c-file-style:"stroustrup"
1805 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1806 indent-tabs-mode:nil
1807 fill-column:99
1808 End:
1809 */
1810 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :