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 node tool quick zoom. Remove them after fixing it.
95 #include "ui/tool/node-tool.h"
96 #include "ui/tool/control-point-selection.h"
98 #include "display/sp-canvas.h"
100 namespace Inkscape { namespace XML { class Node; }}
102 // Callback declarations
103 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
104 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
105 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
106 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
107 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
108 static void _reconstruction_start(SPDesktop * desktop);
109 static void _reconstruction_finish(SPDesktop * desktop);
110 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
112 /**
113 * Return new desktop object.
114 * \pre namedview != NULL.
115 * \pre canvas != NULL.
116 */
117 SPDesktop::SPDesktop() :
118 _dlg_mgr( 0 ),
119 namedview( 0 ),
120 canvas( 0 ),
121 selection( 0 ),
122 event_context( 0 ),
123 layer_manager( 0 ),
124 event_log( 0 ),
125 temporary_item_list( 0 ),
126 snapindicator( 0 ),
127 acetate( 0 ),
128 main( 0 ),
129 gridgroup( 0 ),
130 guides( 0 ),
131 drawing( 0 ),
132 sketch( 0 ),
133 controls( 0 ),
134 tempgroup ( 0 ),
135 table( 0 ),
136 page( 0 ),
137 page_border( 0 ),
138 current( 0 ),
139 _focusMode(false),
140 zooms_past( 0 ),
141 zooms_future( 0 ),
142 dkey( 0 ),
143 number( 0 ),
144 window_state(0),
145 interaction_disabled_counter( 0 ),
146 waiting_cursor( false ),
147 guides_active( false ),
148 gr_item( 0 ),
149 gr_point_type( 0 ),
150 gr_point_i( 0 ),
151 gr_fill_or_stroke( true ),
152 _layer_hierarchy( 0 ),
153 _reconstruction_old_layer_id( 0 ),
154 _display_mode(Inkscape::RENDERMODE_NORMAL),
155 _widget( 0 ),
156 _inkscape( 0 ),
157 _guides_message_context( 0 ),
158 _active( false ),
159 _w2d(),
160 _d2w(),
161 _doc2dt( Geom::identity() ),
162 grids_visible( false )
163 {
164 _d2w.setIdentity();
165 _w2d.setIdentity();
167 selection = Inkscape::GC::release( new Inkscape::Selection(this) );
168 }
170 void
171 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWidgetInterface *widget)
172 {
173 _widget = widget;
175 // Temporary workaround for link order issues:
176 Inkscape::DeviceManager::getManager().getDevices();
177 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
179 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
181 current = prefs->getStyle("/desktop/style");
183 namedview = nv;
184 canvas = aCanvas;
186 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
187 /* Kill flicker */
188 sp_document_ensure_up_to_date (document);
190 /* Setup Dialog Manager */
191 _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
193 dkey = sp_item_display_key_new (1);
195 /* Connect document */
196 setDocument (document);
198 number = namedview->getViewCount();
201 /* Setup Canvas */
202 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
204 SPCanvasGroup *root = sp_canvas_root (canvas);
206 /* Setup adminstrative layers */
207 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
208 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
209 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
210 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
212 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
213 SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
214 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
215 sp_canvas_item_move_to_z (table, 0);
217 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
218 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
219 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
221 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
222 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
224 SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
226 if (prefs->getBool("/options/startmode/outline")) {
227 // Start in outline mode
228 setDisplayModeOutline();
229 } else {
230 // Start in normal mode, default
231 setDisplayModeNormal();
232 }
234 // The order in which these canvas items are added determines the z-order. It's therefore
235 // important to add the tempgroup (which will contain the snapindicator) before adding the
236 // controls. Only this way one will be able to quickly (before the snap indicator has
237 // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
238 // will not work (the snap indicator is on top of the node handler; is the snapindicator
239 // being selected? or does it intercept some of the events that should have gone to the
240 // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
241 gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
242 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
243 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
244 tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
245 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
247 /* Push select tool to the bottom of stack */
248 /** \todo
249 * FIXME: this is the only call to this. Everything else seems to just
250 * call "set" instead of "push". Can we assume that there is only one
251 * context ever?
252 */
253 push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
255 // display rect and zoom are now handled in sp_desktop_widget_realize()
257 Geom::Rect const d(Geom::Point(0.0, 0.0),
258 Geom::Point(sp_document_width(document), sp_document_height(document)));
260 SP_CTRLRECT(page)->setRectangle(d);
261 SP_CTRLRECT(page_border)->setRectangle(d);
263 /* the following sets the page shadow on the canvas
264 It was originally set to 5, which is really cheesy!
265 It now is an attribute in the document's namedview. If a value of
266 0 is used, then the constructor for a shadow is not initialized.
267 */
269 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
270 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
271 }
274 /* Connect event for page resize */
275 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
277 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
279 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
280 SP_CANVAS_ARENA (drawing)->arena,
281 dkey,
282 SP_ITEM_SHOW_DISPLAY);
283 if (ai) {
284 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
285 }
287 namedview->show(this);
288 /* Ugly hack */
289 activate_guides (true);
290 /* Ugly hack */
291 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
293 /* Set up notification of rebuilding the document, this allows
294 for saving object related settings in the document. */
295 _reconstruction_start_connection =
296 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
297 _reconstruction_finish_connection =
298 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
299 _reconstruction_old_layer_id = NULL;
301 // ?
302 // sp_active_desktop_set (desktop);
303 _inkscape = INKSCAPE;
305 _activate_connection = _activate_signal.connect(
306 sigc::bind(
307 sigc::ptr_fun(_onActivate),
308 this
309 )
310 );
311 _deactivate_connection = _deactivate_signal.connect(
312 sigc::bind(
313 sigc::ptr_fun(_onDeactivate),
314 this
315 )
316 );
318 _sel_modified_connection = selection->connectModified(
319 sigc::bind(
320 sigc::ptr_fun(&_onSelectionModified),
321 this
322 )
323 );
324 _sel_changed_connection = selection->connectChanged(
325 sigc::bind(
326 sigc::ptr_fun(&_onSelectionChanged),
327 this
328 )
329 );
332 /* setup LayerManager */
333 // (Setting up after the connections are all in place, as it may use some of them)
334 layer_manager = new Inkscape::LayerManager( this );
336 showGrids(namedview->grids_visible, false);
338 temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
339 snapindicator = new Inkscape::Display::SnapIndicator ( this );
340 }
343 void SPDesktop::destroy()
344 {
345 if (snapindicator) {
346 delete snapindicator;
347 snapindicator = NULL;
348 }
349 if (temporary_item_list) {
350 delete temporary_item_list;
351 temporary_item_list = NULL;
352 }
354 if (selection) {
355 delete selection;
356 selection = NULL;
357 }
359 namedview->hide(this);
361 _activate_connection.disconnect();
362 _deactivate_connection.disconnect();
363 _sel_modified_connection.disconnect();
364 _sel_changed_connection.disconnect();
365 _modified_connection.disconnect();
366 _commit_connection.disconnect();
367 _reconstruction_start_connection.disconnect();
368 _reconstruction_finish_connection.disconnect();
370 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
371 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
372 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
374 while (event_context) {
375 SPEventContext *ec = event_context;
376 event_context = ec->next;
377 sp_event_context_finish (ec);
378 g_object_unref (G_OBJECT (ec));
379 }
381 if (_layer_hierarchy) {
382 delete _layer_hierarchy;
383 // _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
384 }
386 if (layer_manager) {
387 delete layer_manager;
388 layer_manager = NULL;
389 }
391 if (_inkscape) {
392 _inkscape = NULL;
393 }
395 if (drawing) {
396 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
397 drawing = NULL;
398 }
400 delete _guides_message_context;
401 _guides_message_context = NULL;
403 g_list_free (zooms_past);
404 g_list_free (zooms_future);
405 }
407 SPDesktop::~SPDesktop() {}
409 //--------------------------------------------------------------------
410 /* Public methods */
413 /* These methods help for temporarily showing things on-canvas.
414 * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
415 * is when you want to prematurely remove the item from the canvas, by calling
416 * desktop->remove_temporary_canvasitem(tempitem).
417 */
418 /** Note that lifetime is measured in milliseconds
419 * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
420 * delete the object for you and the reference will become invalid without you knowing it.
421 * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
422 * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
423 * because the object might be deleted already without you knowing it.
424 * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
425 */
426 Inkscape::Display::TemporaryItem *
427 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
428 {
429 if (move_to_bottom) {
430 sp_canvas_item_move_to_z(item, 0);
431 }
433 return temporary_item_list->add_item(item, lifetime);
434 }
436 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
437 */
438 void
439 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
440 {
441 // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
442 if (tempitem && temporary_item_list) {
443 temporary_item_list->delete_item(tempitem);
444 }
445 }
447 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
448 SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
449 canvas->rendermode = mode;
450 _display_mode = mode;
451 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
452 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
453 }
455 void SPDesktop::displayModeToggle() {
456 switch (_display_mode) {
457 case Inkscape::RENDERMODE_NORMAL:
458 _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
459 break;
460 case Inkscape::RENDERMODE_NO_FILTERS:
461 _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
462 break;
463 case Inkscape::RENDERMODE_OUTLINE:
464 _setDisplayMode(Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW);
465 break;
466 case Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW:
467 default:
468 _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
469 }
470 }
472 /**
473 * Returns current root (=bottom) layer.
474 */
475 SPObject *SPDesktop::currentRoot() const
476 {
477 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
478 }
480 /**
481 * Returns current top layer.
482 */
483 SPObject *SPDesktop::currentLayer() const
484 {
485 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
486 }
488 /**
489 * Sets the current layer of the desktop.
490 *
491 * Make \a object the top layer.
492 */
493 void SPDesktop::setCurrentLayer(SPObject *object) {
494 g_return_if_fail(SP_IS_GROUP(object));
495 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
496 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
497 _layer_hierarchy->setBottom(object);
498 }
500 void SPDesktop::toggleLayerSolo(SPObject *object) {
501 g_return_if_fail(SP_IS_GROUP(object));
502 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
504 bool othersShowing = false;
505 std::vector<SPObject*> layers;
506 for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
507 layers.push_back(obj);
508 othersShowing |= !SP_ITEM(obj)->isHidden();
509 }
510 for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
511 layers.push_back(obj);
512 othersShowing |= !SP_ITEM(obj)->isHidden();
513 }
516 if ( SP_ITEM(object)->isHidden() ) {
517 SP_ITEM(object)->setHidden(false);
518 }
520 for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
521 SP_ITEM(*it)->setHidden(othersShowing);
522 }
523 }
525 /**
526 * Return layer that contains \a object.
527 */
528 SPObject *SPDesktop::layerForObject(SPObject *object) {
529 g_return_val_if_fail(object != NULL, NULL);
531 SPObject *root=currentRoot();
532 object = SP_OBJECT_PARENT(object);
533 while ( object && object != root && !isLayer(object) ) {
534 object = SP_OBJECT_PARENT(object);
535 }
536 return object;
537 }
539 /**
540 * True if object is a layer.
541 */
542 bool SPDesktop::isLayer(SPObject *object) const {
543 return ( SP_IS_GROUP(object)
544 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
545 == SPGroup::LAYER ) );
546 }
548 /**
549 * True if desktop viewport fully contains \a item's bbox.
550 */
551 bool SPDesktop::isWithinViewport (SPItem *item) const
552 {
553 Geom::Rect const viewport = get_display_area();
554 Geom::OptRect const bbox = sp_item_bbox_desktop(item);
555 if (bbox) {
556 return viewport.contains(*bbox);
557 } else {
558 return true;
559 }
560 }
562 ///
563 bool SPDesktop::itemIsHidden(SPItem const *item) const {
564 return item->isHidden(this->dkey);
565 }
567 /**
568 * Set activate property of desktop; emit signal if changed.
569 */
570 void
571 SPDesktop::set_active (bool new_active)
572 {
573 if (new_active != _active) {
574 _active = new_active;
575 if (new_active) {
576 _activate_signal.emit();
577 } else {
578 _deactivate_signal.emit();
579 }
580 }
581 }
583 /**
584 * Set activate status of current desktop's named view.
585 */
586 void
587 SPDesktop::activate_guides(bool activate)
588 {
589 guides_active = activate;
590 namedview->activateGuides(this, activate);
591 }
593 /**
594 * Make desktop switch documents.
595 */
596 void
597 SPDesktop::change_document (SPDocument *theDocument)
598 {
599 g_return_if_fail (theDocument != NULL);
601 /* unselect everything before switching documents */
602 selection->clear();
604 setDocument (theDocument);
606 /* update the rulers, connect the desktop widget's signal to the new namedview etc.
607 (this can probably be done in a better way) */
608 Gtk::Window *parent = this->getToplevel();
609 g_assert(parent != NULL);
610 SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
611 if (dtw) {
612 dtw->desktop = this;
613 }
614 dtw->updateNamedview();
616 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
617 _document_replaced_signal.emit (this, theDocument);
618 }
620 /**
621 * Make desktop switch event contexts.
622 */
623 void
624 SPDesktop::set_event_context (GtkType type, const gchar *config)
625 {
626 SPEventContext *ec;
627 while (event_context) {
628 ec = event_context;
629 sp_event_context_deactivate (ec);
630 // we have to keep event_context valid during destruction - otherwise writing
631 // destructors is next to impossible
632 SPEventContext *next = ec->next;
633 sp_event_context_finish (ec);
634 g_object_unref (G_OBJECT (ec));
635 event_context = next;
636 }
638 ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
639 ec->next = event_context;
640 event_context = ec;
641 sp_event_context_activate (ec);
642 _event_context_changed_signal.emit (this, ec);
643 }
645 /**
646 * Push event context onto desktop's context stack.
647 */
648 void
649 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
650 {
651 SPEventContext *ref, *ec;
653 if (event_context && event_context->key == key) return;
654 ref = event_context;
655 while (ref && ref->next && ref->next->key != key) ref = ref->next;
656 if (ref && ref->next) {
657 ec = ref->next;
658 ref->next = ec->next;
659 sp_event_context_finish (ec);
660 g_object_unref (G_OBJECT (ec));
661 }
663 if (event_context) sp_event_context_deactivate (event_context);
664 ec = sp_event_context_new (type, this, config, key);
665 ec->next = event_context;
666 event_context = ec;
667 sp_event_context_activate (ec);
668 _event_context_changed_signal.emit (this, ec);
669 }
671 /**
672 * Sets the coordinate status to a given point
673 */
674 void
675 SPDesktop::set_coordinate_status (Geom::Point p) {
676 _widget->setCoordinateStatus(p);
677 }
679 /**
680 * \see sp_document_item_from_list_at_point_bottom()
681 */
682 SPItem *
683 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
684 {
685 g_return_val_if_fail (doc() != NULL, NULL);
686 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
687 }
689 /**
690 * \see sp_document_item_at_point()
691 */
692 SPItem *
693 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
694 {
695 g_return_val_if_fail (doc() != NULL, NULL);
696 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
697 }
699 /**
700 * \see sp_document_group_at_point()
701 */
702 SPItem *
703 SPDesktop::group_at_point (Geom::Point const p) const
704 {
705 g_return_val_if_fail (doc() != NULL, NULL);
706 return sp_document_group_at_point (doc(), dkey, p);
707 }
709 /**
710 * \brief Returns the mouse point in document coordinates; if mouse is
711 * outside the canvas, returns the center of canvas viewpoint
712 */
713 Geom::Point
714 SPDesktop::point() const
715 {
716 Geom::Point p = _widget->getPointer();
717 Geom::Point pw = sp_canvas_window_to_world (canvas, p);
718 p = w2d(pw);
720 Geom::Rect const r = canvas->getViewbox();
722 Geom::Point r0 = w2d(r.min());
723 Geom::Point r1 = w2d(r.max());
725 if (p[Geom::X] >= r0[Geom::X] &&
726 p[Geom::X] <= r1[Geom::X] &&
727 p[Geom::Y] >= r0[Geom::Y] &&
728 p[Geom::Y] <= r1[Geom::Y])
729 {
730 return p;
731 } else {
732 return (r0 + r1) / 2;
733 }
734 }
736 /**
737 * Put current zoom data in history list.
738 */
739 void
740 SPDesktop::push_current_zoom (GList **history)
741 {
742 Geom::Rect const area = get_display_area();
744 NRRect *old_zoom = g_new(NRRect, 1);
745 old_zoom->x0 = area.min()[Geom::X];
746 old_zoom->x1 = area.max()[Geom::X];
747 old_zoom->y0 = area.min()[Geom::Y];
748 old_zoom->y1 = area.max()[Geom::Y];
749 if ( *history == NULL
750 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
751 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
752 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
753 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
754 {
755 *history = g_list_prepend (*history, old_zoom);
756 }
757 }
759 /**
760 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
761 */
762 void
763 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
764 {
765 g_assert(_widget);
767 // save the zoom
768 if (log) {
769 push_current_zoom(&zooms_past);
770 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
771 g_list_free (zooms_future);
772 zooms_future = NULL;
773 }
775 double const cx = 0.5 * (x0 + x1);
776 double const cy = 0.5 * (y0 + y1);
778 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
779 Geom::Rect viewbox = canvas->getViewbox();
780 viewbox.expandBy(-border);
782 double scale = _d2w.descrim();
783 double newscale;
784 if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
785 newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
786 } else {
787 newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
788 }
790 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
792 int clear = FALSE;
793 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
794 // zoom changed - set new zoom factors
795 _d2w = Geom::Scale(newscale);
796 _w2d = Geom::Scale(1/newscale);
797 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
798 clear = TRUE;
799 signal_zoom_changed.emit(_d2w.descrim());
800 }
802 /* Calculate top left corner (in document pixels) */
803 x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
804 y0 = cy - 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
806 /* Scroll */
807 sp_canvas_scroll_to (canvas, x0 * newscale - border, y0 * newscale - border, clear);
809 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
810 sp_box3d_context_update_lines(event_context);
812 _widget->updateRulers();
813 _widget->updateScrollbars(_d2w.descrim());
814 _widget->updateZoom();
815 }
817 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
818 {
819 set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
820 }
822 /**
823 * Return viewbox dimensions.
824 */
825 Geom::Rect SPDesktop::get_display_area() const
826 {
827 Geom::Rect const viewbox = canvas->getViewbox();
829 double const scale = _d2w[0];
831 return viewbox * (1./scale);
832 }
834 /**
835 * Revert back to previous zoom if possible.
836 */
837 void
838 SPDesktop::prev_zoom()
839 {
840 if (zooms_past == NULL) {
841 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
842 return;
843 }
845 // push current zoom into forward zooms list
846 push_current_zoom (&zooms_future);
848 // restore previous zoom
849 set_display_area (((NRRect *) zooms_past->data)->x0,
850 ((NRRect *) zooms_past->data)->y0,
851 ((NRRect *) zooms_past->data)->x1,
852 ((NRRect *) zooms_past->data)->y1,
853 0, false);
855 // remove the just-added zoom from the past zooms list
856 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
857 }
859 /**
860 * Set zoom to next in list.
861 */
862 void
863 SPDesktop::next_zoom()
864 {
865 if (zooms_future == NULL) {
866 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
867 return;
868 }
870 // push current zoom into past zooms list
871 push_current_zoom (&zooms_past);
873 // restore next zoom
874 set_display_area (((NRRect *) zooms_future->data)->x0,
875 ((NRRect *) zooms_future->data)->y0,
876 ((NRRect *) zooms_future->data)->x1,
877 ((NRRect *) zooms_future->data)->y1,
878 0, false);
880 // remove the just-used zoom from the zooms_future list
881 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
882 }
884 /** \brief Performs a quick zoom into what the user is working on
885 \param enable Whether we're going in or out of quick zoom
887 */
888 void
889 SPDesktop::zoom_quick (bool enable)
890 {
891 if (enable == _quick_zoom_enabled) {
892 return;
893 }
895 if (enable == true) {
896 _quick_zoom_stored_area = get_display_area();
897 bool zoomed = false;
899 // TODO This needs to migrate into the node tool, but currently the design
900 // of this method is sufficiently wrong to prevent this.
901 if (!zoomed && INK_IS_NODE_TOOL(event_context)) {
902 InkNodeTool *nt = static_cast<InkNodeTool*>(event_context);
903 if (!nt->_selected_nodes->empty()) {
904 Geom::Rect nodes = *nt->_selected_nodes->bounds();
905 double area = nodes.area();
906 // do not zoom if a single cusp node is selected aand the bounds
907 // have zero area.
908 if (!Geom::are_near(area, 0) && area * 2.0 < _quick_zoom_stored_area.area()) {
909 set_display_area(nodes, true);
910 zoomed = true;
911 }
912 }
913 }
915 if (!zoomed) {
916 Geom::OptRect const d = selection->bounds();
917 if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
918 set_display_area(*d, true);
919 zoomed = true;
920 }
921 }
923 if (!zoomed) {
924 zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
925 zoomed = true;
926 }
927 } else {
928 set_display_area(_quick_zoom_stored_area, false);
929 }
931 _quick_zoom_enabled = enable;
932 return;
933 }
935 /**
936 * Zoom to point with absolute zoom factor.
937 */
938 void
939 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
940 {
941 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
943 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
944 // this check prevents "sliding" when trying to zoom in at maximum zoom;
945 /// \todo someone please fix calculations properly and remove this hack
946 if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
947 return;
949 Geom::Rect const viewbox = canvas->getViewbox();
951 double const width2 = viewbox.dimensions()[Geom::X] / zoom;
952 double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
954 set_display_area(cx - px * width2,
955 cy - py * height2,
956 cx + (1 - px) * width2,
957 cy + (1 - py) * height2,
958 0.0);
959 }
961 /**
962 * Zoom to center with absolute zoom factor.
963 */
964 void
965 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
966 {
967 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
968 }
970 /**
971 * Zoom to point with relative zoom factor.
972 */
973 void
974 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
975 {
976 Geom::Rect const area = get_display_area();
978 if (cx < area.min()[Geom::X]) {
979 cx = area.min()[Geom::X];
980 }
981 if (cx > area.max()[Geom::X]) {
982 cx = area.max()[Geom::X];
983 }
984 if (cy < area.min()[Geom::Y]) {
985 cy = area.min()[Geom::Y];
986 }
987 if (cy > area.max()[Geom::Y]) {
988 cy = area.max()[Geom::Y];
989 }
991 gdouble const scale = _d2w.descrim() * zoom;
992 double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
993 double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
995 zoom_absolute_keep_point(cx, cy, px, py, scale);
996 }
998 /**
999 * Zoom to center with relative zoom factor.
1000 */
1001 void
1002 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1003 {
1004 gdouble scale = _d2w.descrim() * zoom;
1005 zoom_absolute (cx, cy, scale);
1006 }
1008 /**
1009 * Set display area to origin and current document dimensions.
1010 */
1011 void
1012 SPDesktop::zoom_page()
1013 {
1014 Geom::Rect d(Geom::Point(0, 0),
1015 Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1017 if (d.minExtent() < 1.0) {
1018 return;
1019 }
1021 set_display_area(d, 10);
1022 }
1024 /**
1025 * Set display area to current document width.
1026 */
1027 void
1028 SPDesktop::zoom_page_width()
1029 {
1030 Geom::Rect const a = get_display_area();
1032 if (sp_document_width(doc()) < 1.0) {
1033 return;
1034 }
1036 Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1037 Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1039 set_display_area(d, 10);
1040 }
1042 /**
1043 * Zoom to selection.
1044 */
1045 void
1046 SPDesktop::zoom_selection()
1047 {
1048 Geom::OptRect const d = selection->bounds();
1050 if ( !d || d->minExtent() < 0.1 ) {
1051 return;
1052 }
1054 set_display_area(*d, 10);
1055 }
1057 /**
1058 * Tell widget to let zoom widget grab keyboard focus.
1059 */
1060 void
1061 SPDesktop::zoom_grab_focus()
1062 {
1063 _widget->letZoomGrabFocus();
1064 }
1066 /**
1067 * Zoom to whole drawing.
1068 */
1069 void
1070 SPDesktop::zoom_drawing()
1071 {
1072 g_return_if_fail (doc() != NULL);
1073 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1074 g_return_if_fail (docitem != NULL);
1076 Geom::OptRect d = sp_item_bbox_desktop(docitem);
1078 /* Note that the second condition here indicates that
1079 ** there are no items in the drawing.
1080 */
1081 if ( !d || d->minExtent() < 0.1 ) {
1082 return;
1083 }
1085 set_display_area(*d, 10);
1086 }
1088 /**
1089 * Scroll canvas by specific coordinate amount in svg coordinates.
1090 */
1091 void
1092 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1093 {
1094 double scale = _d2w.descrim();
1095 scroll_world(dx*scale, dy*scale, is_scrolling);
1096 }
1098 /**
1099 * Scroll canvas by specific coordinate amount.
1100 */
1101 void
1102 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1103 {
1104 g_assert(_widget);
1106 Geom::Rect const viewbox = canvas->getViewbox();
1108 sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1110 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1111 sp_box3d_context_update_lines(event_context);
1113 _widget->updateRulers();
1114 _widget->updateScrollbars(_d2w.descrim());
1115 }
1117 bool
1118 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1119 {
1120 using Geom::X;
1121 using Geom::Y;
1123 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1124 gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1126 // autoscrolldistance is in screen pixels, but the display area is in document units
1127 autoscrolldistance /= _d2w.descrim();
1128 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1129 Geom::Rect dbox = get_display_area();
1130 dbox.expandBy(-autoscrolldistance);
1132 if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1133 !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y]) ) {
1135 Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1137 gdouble x_to;
1138 if (p[X] < dbox.min()[X])
1139 x_to = dbox.min()[X];
1140 else if (p[X] > dbox.max()[X])
1141 x_to = dbox.max()[X];
1142 else
1143 x_to = p[X];
1145 gdouble y_to;
1146 if (p[Y] < dbox.min()[Y])
1147 y_to = dbox.min()[Y];
1148 else if (p[Y] > dbox.max()[Y])
1149 y_to = dbox.max()[Y];
1150 else
1151 y_to = p[Y];
1153 Geom::Point const d_dt(x_to, y_to);
1154 Geom::Point const d_w( d_dt * _d2w );
1155 Geom::Point const moved_w( d_w - s_w );
1157 if (autoscrollspeed == 0)
1158 autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1160 if (autoscrollspeed != 0)
1161 scroll_world (autoscrollspeed * moved_w);
1163 return true;
1164 }
1165 return false;
1166 }
1168 bool
1169 SPDesktop::is_iconified()
1170 {
1171 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1172 }
1174 void
1175 SPDesktop::iconify()
1176 {
1177 _widget->setIconified();
1178 }
1180 bool
1181 SPDesktop::is_maximized()
1182 {
1183 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1184 }
1186 void
1187 SPDesktop::maximize()
1188 {
1189 _widget->setMaximized();
1190 }
1192 bool
1193 SPDesktop::is_fullscreen()
1194 {
1195 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1196 }
1198 void
1199 SPDesktop::fullscreen()
1200 {
1201 _widget->setFullscreen();
1202 }
1204 /** \brief Checks to see if the user is working in focused mode
1206 Returns the value of \c _focusMode
1207 */
1208 bool
1209 SPDesktop::is_focusMode()
1210 {
1211 return _focusMode;
1212 }
1214 /** \brief Changes whether the user is in focus mode or not
1215 \param mode Which mode the view should be in
1217 */
1218 void
1219 SPDesktop::focusMode (bool mode)
1220 {
1221 if (mode == _focusMode) { return; }
1223 _focusMode = mode;
1225 layoutWidget();
1226 //sp_desktop_widget_layout(SPDesktopWidget);
1228 return;
1229 }
1231 void
1232 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1233 {
1234 _widget->getGeometry (x, y, w, h);
1235 }
1237 void
1238 SPDesktop::setWindowPosition (Geom::Point p)
1239 {
1240 _widget->setPosition (p);
1241 }
1243 void
1244 SPDesktop::setWindowSize (gint w, gint h)
1245 {
1246 _widget->setSize (w, h);
1247 }
1249 void
1250 SPDesktop::setWindowTransient (void *p, int transient_policy)
1251 {
1252 _widget->setTransient (p, transient_policy);
1253 }
1255 Gtk::Window*
1256 SPDesktop::getToplevel( )
1257 {
1258 return _widget->getWindow();
1259 }
1261 void
1262 SPDesktop::presentWindow()
1263 {
1264 _widget->present();
1265 }
1267 bool
1268 SPDesktop::warnDialog (gchar *text)
1269 {
1270 return _widget->warnDialog (text);
1271 }
1273 void
1274 SPDesktop::toggleRulers()
1275 {
1276 _widget->toggleRulers();
1277 }
1279 void
1280 SPDesktop::toggleScrollbars()
1281 {
1282 _widget->toggleScrollbars();
1283 }
1285 void
1286 SPDesktop::layoutWidget()
1287 {
1288 _widget->layout();
1289 }
1291 void
1292 SPDesktop::destroyWidget()
1293 {
1294 _widget->destroy();
1295 }
1297 bool
1298 SPDesktop::shutdown()
1299 {
1300 return _widget->shutdown();
1301 }
1303 bool SPDesktop::onDeleteUI (GdkEventAny*)
1304 {
1305 if(shutdown())
1306 return true;
1308 destroyWidget();
1309 return false;
1310 }
1312 /**
1313 * onWindowStateEvent
1314 *
1315 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1316 * Since GTK doesn't have a way to query this state information directly, we
1317 * record it for the desktop here, and also possibly trigger a layout.
1318 */
1319 bool
1320 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1321 {
1322 // Record the desktop window's state
1323 window_state = event->new_window_state;
1325 // Layout may differ depending on full-screen mode or not
1326 GdkWindowState changed = event->changed_mask;
1327 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1328 layoutWidget();
1329 }
1331 return false;
1332 }
1334 void
1335 SPDesktop::setToolboxFocusTo (gchar const *label)
1336 {
1337 _widget->setToolboxFocusTo (label);
1338 }
1340 void
1341 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1342 {
1343 _widget->setToolboxAdjustmentValue (id, val);
1344 }
1346 void
1347 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1348 {
1349 _widget->setToolboxSelectOneValue (id, val);
1350 }
1352 bool
1353 SPDesktop::isToolboxButtonActive (gchar const *id)
1354 {
1355 return _widget->isToolboxButtonActive (id);
1356 }
1358 void
1359 SPDesktop::emitToolSubselectionChanged(gpointer data)
1360 {
1361 _tool_subselection_changed.emit(data);
1362 inkscape_subselection_changed (this);
1363 }
1365 void
1366 SPDesktop::updateNow()
1367 {
1368 sp_canvas_update_now(canvas);
1369 }
1371 void
1372 SPDesktop::enableInteraction()
1373 {
1374 _widget->enableInteraction();
1375 }
1377 void SPDesktop::disableInteraction()
1378 {
1379 _widget->disableInteraction();
1380 }
1382 void SPDesktop::setWaitingCursor()
1383 {
1384 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1385 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1386 gdk_cursor_unref(waiting);
1387 // GDK needs the flush for the cursor change to take effect
1388 gdk_flush();
1389 waiting_cursor = true;
1390 }
1392 void SPDesktop::clearWaitingCursor()
1393 {
1394 if (waiting_cursor)
1395 sp_event_context_update_cursor(sp_desktop_event_context(this));
1396 }
1398 void SPDesktop::toggleColorProfAdjust()
1399 {
1400 _widget->toggleColorProfAdjust();
1401 }
1403 void SPDesktop::toggleGrids()
1404 {
1405 if (namedview->grids) {
1406 if(gridgroup) {
1407 showGrids(!grids_visible);
1408 }
1409 } else {
1410 //there is no grid present at the moment. add a rectangular grid and make it visible
1411 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1412 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1413 showGrids(true);
1414 }
1415 }
1417 void SPDesktop::showGrids(bool show, bool dirty_document)
1418 {
1419 grids_visible = show;
1420 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1421 if (show) {
1422 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1423 } else {
1424 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1425 }
1426 }
1428 void SPDesktop::toggleSnapGlobal()
1429 {
1430 bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1431 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1432 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1433 }
1435 //----------------------------------------------------------------------
1436 // Callback implementations. The virtual ones are connected by the view.
1438 void
1439 SPDesktop::onPositionSet (double x, double y)
1440 {
1441 _widget->viewSetPosition (Geom::Point(x,y));
1442 }
1444 void
1445 SPDesktop::onResized (double /*x*/, double /*y*/)
1446 {
1447 // Nothing called here
1448 }
1450 /**
1451 * Redraw callback; queues Gtk redraw; connected by View.
1452 */
1453 void
1454 SPDesktop::onRedrawRequested ()
1455 {
1456 if (main) {
1457 _widget->requestCanvasUpdate();
1458 }
1459 }
1461 void
1462 SPDesktop::updateCanvasNow()
1463 {
1464 _widget->requestCanvasUpdateAndWait();
1465 }
1467 /**
1468 * Associate document with desktop.
1469 */
1470 void
1471 SPDesktop::setDocument (SPDocument *doc)
1472 {
1473 if (this->doc() && doc) {
1474 namedview->hide(this);
1475 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1476 }
1478 if (_layer_hierarchy) {
1479 _layer_hierarchy->clear();
1480 delete _layer_hierarchy;
1481 }
1482 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1483 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1484 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1485 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1486 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1488 /* setup EventLog */
1489 event_log = new Inkscape::EventLog(doc);
1490 doc->addUndoObserver(*event_log);
1492 _commit_connection.disconnect();
1493 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1495 /// \todo fixme: This condition exists to make sure the code
1496 /// inside is NOT called on initialization, only on replacement. But there
1497 /// are surely more safe methods to accomplish this.
1498 // TODO since the comment had reversed logic, check the intent of this block of code:
1499 if (drawing) {
1500 NRArenaItem *ai = 0;
1502 namedview = sp_document_namedview (doc, NULL);
1503 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1504 number = namedview->getViewCount();
1506 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1507 SP_CANVAS_ARENA (drawing)->arena,
1508 dkey,
1509 SP_ITEM_SHOW_DISPLAY);
1510 if (ai) {
1511 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1512 }
1513 namedview->show(this);
1514 /* Ugly hack */
1515 activate_guides (true);
1516 /* Ugly hack */
1517 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1518 }
1520 _document_replaced_signal.emit (this, doc);
1522 View::setDocument (doc);
1523 }
1525 void
1526 SPDesktop::onStatusMessage
1527 (Inkscape::MessageType type, gchar const *message)
1528 {
1529 if (_widget) {
1530 _widget->setMessage(type, message);
1531 }
1532 }
1534 void
1535 SPDesktop::onDocumentURISet (gchar const* uri)
1536 {
1537 _widget->setTitle(uri);
1538 }
1540 /**
1541 * Resized callback.
1542 */
1543 void
1544 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1545 {
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(desktop->currentLayer()->getId());
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 :