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 #include "display/sp-canvas.h"
96 namespace Inkscape { namespace XML { class Node; }}
98 // Callback declarations
99 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
100 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
101 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
102 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
103 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
104 static void _reconstruction_start(SPDesktop * desktop);
105 static void _reconstruction_finish(SPDesktop * desktop);
106 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
108 /**
109 * Return new desktop object.
110 * \pre namedview != NULL.
111 * \pre canvas != NULL.
112 */
113 SPDesktop::SPDesktop() :
114 _dlg_mgr( 0 ),
115 namedview( 0 ),
116 canvas( 0 ),
117 selection( 0 ),
118 event_context( 0 ),
119 layer_manager( 0 ),
120 event_log( 0 ),
121 temporary_item_list( 0 ),
122 snapindicator( 0 ),
123 acetate( 0 ),
124 main( 0 ),
125 gridgroup( 0 ),
126 guides( 0 ),
127 drawing( 0 ),
128 sketch( 0 ),
129 controls( 0 ),
130 tempgroup ( 0 ),
131 table( 0 ),
132 page( 0 ),
133 page_border( 0 ),
134 current( 0 ),
135 _focusMode(false),
136 zooms_past( 0 ),
137 zooms_future( 0 ),
138 dkey( 0 ),
139 number( 0 ),
140 window_state(0),
141 interaction_disabled_counter( 0 ),
142 waiting_cursor( false ),
143 guides_active( false ),
144 gr_item( 0 ),
145 gr_point_type( 0 ),
146 gr_point_i( 0 ),
147 gr_fill_or_stroke( true ),
148 _layer_hierarchy( 0 ),
149 _reconstruction_old_layer_id( 0 ),
150 _display_mode(Inkscape::RENDERMODE_NORMAL),
151 _widget( 0 ),
152 _inkscape( 0 ),
153 _guides_message_context( 0 ),
154 _active( false ),
155 _w2d(),
156 _d2w(),
157 _doc2dt( Geom::Scale(1, -1) ),
158 grids_visible( false )
159 {
160 _d2w.setIdentity();
161 _w2d.setIdentity();
163 selection = Inkscape::GC::release( new Inkscape::Selection(this) );
164 }
166 void
167 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
168 {
169 // Temporary workaround for link order issues:
170 Inkscape::DeviceManager::getManager().getDevices();
171 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
173 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
175 current = prefs->getStyle("/desktop/style");
177 namedview = nv;
178 canvas = aCanvas;
180 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
181 /* Kill flicker */
182 sp_document_ensure_up_to_date (document);
184 /* Setup Dialog Manager */
185 _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
187 dkey = sp_item_display_key_new (1);
189 /* Connect document */
190 setDocument (document);
192 number = namedview->getViewCount();
195 /* Setup Canvas */
196 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
198 SPCanvasGroup *root = sp_canvas_root (canvas);
200 /* Setup adminstrative layers */
201 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
202 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
203 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
204 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
206 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
207 SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
208 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
209 sp_canvas_item_move_to_z (table, 0);
211 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
212 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
213 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
215 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
216 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
218 SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
220 if (prefs->getBool("/options/startmode/outline")) {
221 // Start in outline mode
222 setDisplayModeOutline();
223 } else {
224 // Start in normal mode, default
225 setDisplayModeNormal();
226 }
228 // The order in which these canvas items are added determines the z-order. It's therefore
229 // important to add the tempgroup (which will contain the snapindicator) before adding the
230 // controls. Only this way one will be able to quickly (before the snap indicator has
231 // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
232 // will not work (the snap indicator is on top of the node handler; is the snapindicator
233 // being selected? or does it intercept some of the events that should have gone to the
234 // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
235 gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
236 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
237 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
238 tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
239 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
241 /* Push select tool to the bottom of stack */
242 /** \todo
243 * FIXME: this is the only call to this. Everything else seems to just
244 * call "set" instead of "push". Can we assume that there is only one
245 * context ever?
246 */
247 push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
249 // display rect and zoom are now handled in sp_desktop_widget_realize()
251 Geom::Rect const d(Geom::Point(0.0, 0.0),
252 Geom::Point(sp_document_width(document), sp_document_height(document)));
254 SP_CTRLRECT(page)->setRectangle(d);
255 SP_CTRLRECT(page_border)->setRectangle(d);
257 /* the following sets the page shadow on the canvas
258 It was originally set to 5, which is really cheesy!
259 It now is an attribute in the document's namedview. If a value of
260 0 is used, then the constructor for a shadow is not initialized.
261 */
263 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
264 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
265 }
268 /* Connect event for page resize */
269 _doc2dt[5] = sp_document_height (document);
270 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
272 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
274 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
275 SP_CANVAS_ARENA (drawing)->arena,
276 dkey,
277 SP_ITEM_SHOW_DISPLAY);
278 if (ai) {
279 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
280 }
282 namedview->show(this);
283 /* Ugly hack */
284 activate_guides (true);
285 /* Ugly hack */
286 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
288 /* Set up notification of rebuilding the document, this allows
289 for saving object related settings in the document. */
290 _reconstruction_start_connection =
291 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
292 _reconstruction_finish_connection =
293 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
294 _reconstruction_old_layer_id = NULL;
296 // ?
297 // sp_active_desktop_set (desktop);
298 _inkscape = INKSCAPE;
300 _activate_connection = _activate_signal.connect(
301 sigc::bind(
302 sigc::ptr_fun(_onActivate),
303 this
304 )
305 );
306 _deactivate_connection = _deactivate_signal.connect(
307 sigc::bind(
308 sigc::ptr_fun(_onDeactivate),
309 this
310 )
311 );
313 _sel_modified_connection = selection->connectModified(
314 sigc::bind(
315 sigc::ptr_fun(&_onSelectionModified),
316 this
317 )
318 );
319 _sel_changed_connection = selection->connectChanged(
320 sigc::bind(
321 sigc::ptr_fun(&_onSelectionChanged),
322 this
323 )
324 );
327 /* setup LayerManager */
328 // (Setting up after the connections are all in place, as it may use some of them)
329 layer_manager = new Inkscape::LayerManager( this );
331 showGrids(namedview->grids_visible, false);
333 temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
334 snapindicator = new Inkscape::Display::SnapIndicator ( this );
335 }
338 void SPDesktop::destroy()
339 {
340 if (snapindicator) {
341 delete snapindicator;
342 snapindicator = NULL;
343 }
344 if (temporary_item_list) {
345 delete temporary_item_list;
346 temporary_item_list = NULL;
347 }
349 if (selection) {
350 delete selection;
351 selection = NULL;
352 }
354 namedview->hide(this);
356 _activate_connection.disconnect();
357 _deactivate_connection.disconnect();
358 _sel_modified_connection.disconnect();
359 _sel_changed_connection.disconnect();
360 _modified_connection.disconnect();
361 _commit_connection.disconnect();
362 _reconstruction_start_connection.disconnect();
363 _reconstruction_finish_connection.disconnect();
365 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
366 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
367 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
369 while (event_context) {
370 SPEventContext *ec = event_context;
371 event_context = ec->next;
372 sp_event_context_finish (ec);
373 g_object_unref (G_OBJECT (ec));
374 }
376 if (_layer_hierarchy) {
377 delete _layer_hierarchy;
378 // _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
379 }
381 if (layer_manager) {
382 delete layer_manager;
383 layer_manager = NULL;
384 }
386 if (_inkscape) {
387 _inkscape = NULL;
388 }
390 if (drawing) {
391 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
392 drawing = NULL;
393 }
395 delete _guides_message_context;
396 _guides_message_context = NULL;
398 g_list_free (zooms_past);
399 g_list_free (zooms_future);
400 }
402 SPDesktop::~SPDesktop() {}
404 //--------------------------------------------------------------------
405 /* Public methods */
408 /* These methods help for temporarily showing things on-canvas.
409 * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
410 * is when you want to prematurely remove the item from the canvas, by calling
411 * desktop->remove_temporary_canvasitem(tempitem).
412 */
413 /** Note that lifetime is measured in milliseconds
414 * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
415 * delete the object for you and the reference will become invalid without you knowing it.
416 * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
417 * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
418 * because the object might be deleted already without you knowing it.
419 * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
420 */
421 Inkscape::Display::TemporaryItem *
422 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
423 {
424 if (move_to_bottom) {
425 sp_canvas_item_move_to_z(item, 0);
426 }
428 return temporary_item_list->add_item(item, lifetime);
429 }
431 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
432 */
433 void
434 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
435 {
436 // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
437 if (tempitem && temporary_item_list) {
438 temporary_item_list->delete_item(tempitem);
439 }
440 }
442 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
443 SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
444 canvas->rendermode = mode;
445 _display_mode = mode;
446 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
447 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
448 }
450 void SPDesktop::displayModeToggle() {
451 switch (_display_mode) {
452 case Inkscape::RENDERMODE_NORMAL:
453 _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
454 break;
455 case Inkscape::RENDERMODE_NO_FILTERS:
456 _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
457 break;
458 case Inkscape::RENDERMODE_OUTLINE:
459 default:
460 _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
461 }
462 }
464 /**
465 * Returns current root (=bottom) layer.
466 */
467 SPObject *SPDesktop::currentRoot() const
468 {
469 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
470 }
472 /**
473 * Returns current top layer.
474 */
475 SPObject *SPDesktop::currentLayer() const
476 {
477 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
478 }
480 /**
481 * Sets the current layer of the desktop.
482 *
483 * Make \a object the top layer.
484 */
485 void SPDesktop::setCurrentLayer(SPObject *object) {
486 g_return_if_fail(SP_IS_GROUP(object));
487 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
488 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
489 _layer_hierarchy->setBottom(object);
490 }
492 void SPDesktop::toggleLayerSolo(SPObject *object) {
493 g_return_if_fail(SP_IS_GROUP(object));
494 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
496 bool othersShowing = false;
497 std::vector<SPObject*> layers;
498 for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
499 layers.push_back(obj);
500 othersShowing |= !SP_ITEM(obj)->isHidden();
501 }
502 for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
503 layers.push_back(obj);
504 othersShowing |= !SP_ITEM(obj)->isHidden();
505 }
508 if ( SP_ITEM(object)->isHidden() ) {
509 SP_ITEM(object)->setHidden(false);
510 }
512 for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
513 SP_ITEM(*it)->setHidden(othersShowing);
514 }
515 }
517 /**
518 * Return layer that contains \a object.
519 */
520 SPObject *SPDesktop::layerForObject(SPObject *object) {
521 g_return_val_if_fail(object != NULL, NULL);
523 SPObject *root=currentRoot();
524 object = SP_OBJECT_PARENT(object);
525 while ( object && object != root && !isLayer(object) ) {
526 object = SP_OBJECT_PARENT(object);
527 }
528 return object;
529 }
531 /**
532 * True if object is a layer.
533 */
534 bool SPDesktop::isLayer(SPObject *object) const {
535 return ( SP_IS_GROUP(object)
536 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
537 == SPGroup::LAYER ) );
538 }
540 /**
541 * True if desktop viewport fully contains \a item's bbox.
542 */
543 bool SPDesktop::isWithinViewport (SPItem *item) const
544 {
545 Geom::Rect const viewport = get_display_area();
546 Geom::OptRect const bbox = sp_item_bbox_desktop(item);
547 if (bbox) {
548 return viewport.contains(*bbox);
549 } else {
550 return true;
551 }
552 }
554 ///
555 bool SPDesktop::itemIsHidden(SPItem const *item) const {
556 return item->isHidden(this->dkey);
557 }
559 /**
560 * Set activate property of desktop; emit signal if changed.
561 */
562 void
563 SPDesktop::set_active (bool new_active)
564 {
565 if (new_active != _active) {
566 _active = new_active;
567 if (new_active) {
568 _activate_signal.emit();
569 } else {
570 _deactivate_signal.emit();
571 }
572 }
573 }
575 /**
576 * Set activate status of current desktop's named view.
577 */
578 void
579 SPDesktop::activate_guides(bool activate)
580 {
581 guides_active = activate;
582 namedview->activateGuides(this, activate);
583 }
585 /**
586 * Make desktop switch documents.
587 */
588 void
589 SPDesktop::change_document (SPDocument *theDocument)
590 {
591 g_return_if_fail (theDocument != NULL);
593 /* unselect everything before switching documents */
594 selection->clear();
596 setDocument (theDocument);
598 /* update the rulers, connect the desktop widget's signal to the new namedview etc.
599 (this can probably be done in a better way) */
600 Gtk::Window *parent = this->getToplevel();
601 g_assert(parent != NULL);
602 SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
603 if (dtw) dtw->desktop = this;
604 sp_desktop_widget_update_namedview(dtw);
606 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
607 _document_replaced_signal.emit (this, theDocument);
608 }
610 /**
611 * Make desktop switch event contexts.
612 */
613 void
614 SPDesktop::set_event_context (GtkType type, const gchar *config)
615 {
616 SPEventContext *ec;
617 while (event_context) {
618 ec = event_context;
619 sp_event_context_deactivate (ec);
620 event_context = ec->next;
621 sp_event_context_finish (ec);
622 g_object_unref (G_OBJECT (ec));
623 }
625 ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
626 ec->next = event_context;
627 event_context = ec;
628 sp_event_context_activate (ec);
629 _event_context_changed_signal.emit (this, ec);
630 }
632 /**
633 * Push event context onto desktop's context stack.
634 */
635 void
636 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
637 {
638 SPEventContext *ref, *ec;
640 if (event_context && event_context->key == key) return;
641 ref = event_context;
642 while (ref && ref->next && ref->next->key != key) ref = ref->next;
643 if (ref && ref->next) {
644 ec = ref->next;
645 ref->next = ec->next;
646 sp_event_context_finish (ec);
647 g_object_unref (G_OBJECT (ec));
648 }
650 if (event_context) sp_event_context_deactivate (event_context);
651 ec = sp_event_context_new (type, this, config, key);
652 ec->next = event_context;
653 event_context = ec;
654 sp_event_context_activate (ec);
655 _event_context_changed_signal.emit (this, ec);
656 }
658 /**
659 * Sets the coordinate status to a given point
660 */
661 void
662 SPDesktop::set_coordinate_status (Geom::Point p) {
663 _widget->setCoordinateStatus(p);
664 }
666 /**
667 * \see sp_document_item_from_list_at_point_bottom()
668 */
669 SPItem *
670 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
671 {
672 g_return_val_if_fail (doc() != NULL, NULL);
673 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
674 }
676 /**
677 * \see sp_document_item_at_point()
678 */
679 SPItem *
680 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
681 {
682 g_return_val_if_fail (doc() != NULL, NULL);
683 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
684 }
686 /**
687 * \see sp_document_group_at_point()
688 */
689 SPItem *
690 SPDesktop::group_at_point (Geom::Point const p) const
691 {
692 g_return_val_if_fail (doc() != NULL, NULL);
693 return sp_document_group_at_point (doc(), dkey, p);
694 }
696 /**
697 * \brief Returns the mouse point in document coordinates; if mouse is
698 * outside the canvas, returns the center of canvas viewpoint
699 */
700 Geom::Point
701 SPDesktop::point() const
702 {
703 Geom::Point p = _widget->getPointer();
704 Geom::Point pw = sp_canvas_window_to_world (canvas, p);
705 p = w2d(pw);
707 Geom::Rect const r = canvas->getViewbox();
709 Geom::Point r0 = w2d(r.min());
710 Geom::Point r1 = w2d(r.max());
712 if (p[Geom::X] >= r0[Geom::X] &&
713 p[Geom::X] <= r1[Geom::X] &&
714 p[Geom::Y] >= r1[Geom::Y] &&
715 p[Geom::Y] <= r0[Geom::Y])
716 {
717 return p;
718 } else {
719 return (r0 + r1) / 2;
720 }
721 }
723 /**
724 * Put current zoom data in history list.
725 */
726 void
727 SPDesktop::push_current_zoom (GList **history)
728 {
729 Geom::Rect const area = get_display_area();
731 NRRect *old_zoom = g_new(NRRect, 1);
732 old_zoom->x0 = area.min()[Geom::X];
733 old_zoom->x1 = area.max()[Geom::X];
734 old_zoom->y0 = area.min()[Geom::Y];
735 old_zoom->y1 = area.max()[Geom::Y];
736 if ( *history == NULL
737 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
738 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
739 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
740 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
741 {
742 *history = g_list_prepend (*history, old_zoom);
743 }
744 }
746 /**
747 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
748 */
749 void
750 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
751 {
752 g_assert(_widget);
754 // save the zoom
755 if (log) {
756 push_current_zoom(&zooms_past);
757 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
758 g_list_free (zooms_future);
759 zooms_future = NULL;
760 }
762 double const cx = 0.5 * (x0 + x1);
763 double const cy = 0.5 * (y0 + y1);
765 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
766 Geom::Rect viewbox = canvas->getViewbox();
767 viewbox.expandBy(-border);
769 double scale = _d2w.descrim();
770 double newscale;
771 if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
772 newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
773 } else {
774 newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
775 }
777 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
779 int clear = FALSE;
780 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
781 /* Set zoom factors */
782 _d2w = Geom::Scale(newscale, -newscale);
783 _w2d = Geom::Scale(1/newscale, 1/-newscale);
784 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
785 clear = TRUE;
786 }
788 /* Calculate top left corner (in document pixels) */
789 x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
790 y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
792 /* Scroll */
793 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
795 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
796 sp_box3d_context_update_lines(event_context);
798 _widget->updateRulers();
799 _widget->updateScrollbars(_d2w.descrim());
800 _widget->updateZoom();
801 }
803 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
804 {
805 set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
806 }
808 /**
809 * Return viewbox dimensions.
810 */
811 Geom::Rect SPDesktop::get_display_area() const
812 {
813 Geom::Rect const viewbox = canvas->getViewbox();
815 double const scale = _d2w[0];
817 return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
818 Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
819 }
821 /**
822 * Revert back to previous zoom if possible.
823 */
824 void
825 SPDesktop::prev_zoom()
826 {
827 if (zooms_past == NULL) {
828 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
829 return;
830 }
832 // push current zoom into forward zooms list
833 push_current_zoom (&zooms_future);
835 // restore previous zoom
836 set_display_area (((NRRect *) zooms_past->data)->x0,
837 ((NRRect *) zooms_past->data)->y0,
838 ((NRRect *) zooms_past->data)->x1,
839 ((NRRect *) zooms_past->data)->y1,
840 0, false);
842 // remove the just-added zoom from the past zooms list
843 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
844 }
846 /**
847 * Set zoom to next in list.
848 */
849 void
850 SPDesktop::next_zoom()
851 {
852 if (zooms_future == NULL) {
853 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
854 return;
855 }
857 // push current zoom into past zooms list
858 push_current_zoom (&zooms_past);
860 // restore next zoom
861 set_display_area (((NRRect *) zooms_future->data)->x0,
862 ((NRRect *) zooms_future->data)->y0,
863 ((NRRect *) zooms_future->data)->x1,
864 ((NRRect *) zooms_future->data)->y1,
865 0, false);
867 // remove the just-used zoom from the zooms_future list
868 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
869 }
871 #include "tools-switch.h"
872 #include "node-context.h"
873 #include "shape-editor.h"
874 #include "nodepath.h"
876 /** \brief Performs a quick zoom into what the user is working on
877 \param enable Whether we're going in or out of quick zoom
879 */
880 void
881 SPDesktop::zoom_quick (bool enable)
882 {
883 if (enable == _quick_zoom_enabled) {
884 return;
885 }
887 if (enable == true) {
888 _quick_zoom_stored_area = get_display_area();
889 bool zoomed = false;
891 if (!zoomed) {
892 SPItem * singleItem = selection->singleItem();
893 if (singleItem != NULL && tools_isactive(this, TOOLS_NODES)) {
895 Inkscape::NodePath::Path * nodepath = event_context->shape_editor->get_nodepath();
896 // printf("I've got a nodepath, crazy\n");
898 if (nodepath) {
899 Geom::Rect nodes;
900 bool firstnode = true;
902 if (nodepath->selected) {
903 for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
904 Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
905 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
906 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
907 if (node->selected) {
908 // printf("\tSelected node\n");
909 if (firstnode) {
910 nodes = Geom::Rect(node->pos, node->pos);
911 firstnode = false;
912 } else {
913 nodes.expandTo(node->pos);
914 }
916 if (node->p.other != NULL) {
917 /* Include previous node pos */
918 nodes.expandTo(node->p.other->pos);
920 /* Include previous handle */
921 if (!sp_node_side_is_line(node, &node->p)) {
922 nodes.expandTo(node->p.pos);
923 }
924 }
926 if (node->n.other != NULL) {
927 /* Include previous node pos */
928 nodes.expandTo(node->n.other->pos);
930 /* Include previous handle */
931 if (!sp_node_side_is_line(node, &node->n)) {
932 nodes.expandTo(node->n.pos);
933 }
934 }
935 }
936 }
937 }
939 if (!firstnode && nodes.area() * 2.0 < _quick_zoom_stored_area.area()) {
940 set_display_area(nodes, 10);
941 zoomed = true;
942 }
943 }
944 }
945 }
946 }
948 if (!zoomed) {
949 Geom::OptRect const d = selection->bounds();
950 if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
951 set_display_area(*d, 10);
952 zoomed = true;
953 }
954 }
956 if (!zoomed) {
957 zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
958 zoomed = true;
959 }
960 } else {
961 set_display_area(_quick_zoom_stored_area, 0);
962 }
964 _quick_zoom_enabled = enable;
965 return;
966 }
968 /**
969 * Zoom to point with absolute zoom factor.
970 */
971 void
972 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
973 {
974 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
976 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
977 // this check prevents "sliding" when trying to zoom in at maximum zoom;
978 /// \todo someone please fix calculations properly and remove this hack
979 if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
980 return;
982 Geom::Rect const viewbox = canvas->getViewbox();
984 double const width2 = viewbox.dimensions()[Geom::X] / zoom;
985 double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
987 set_display_area(cx - px * width2,
988 cy - py * height2,
989 cx + (1 - px) * width2,
990 cy + (1 - py) * height2,
991 0.0);
992 }
994 /**
995 * Zoom to center with absolute zoom factor.
996 */
997 void
998 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
999 {
1000 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
1001 }
1003 /**
1004 * Zoom to point with relative zoom factor.
1005 */
1006 void
1007 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
1008 {
1009 Geom::Rect const area = get_display_area();
1011 if (cx < area.min()[Geom::X]) {
1012 cx = area.min()[Geom::X];
1013 }
1014 if (cx > area.max()[Geom::X]) {
1015 cx = area.max()[Geom::X];
1016 }
1017 if (cy < area.min()[Geom::Y]) {
1018 cy = area.min()[Geom::Y];
1019 }
1020 if (cy > area.max()[Geom::Y]) {
1021 cy = area.max()[Geom::Y];
1022 }
1024 gdouble const scale = _d2w.descrim() * zoom;
1025 double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1026 double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
1028 zoom_absolute_keep_point(cx, cy, px, py, scale);
1029 }
1031 /**
1032 * Zoom to center with relative zoom factor.
1033 */
1034 void
1035 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1036 {
1037 gdouble scale = _d2w.descrim() * zoom;
1038 zoom_absolute (cx, cy, scale);
1039 }
1041 /**
1042 * Set display area to origin and current document dimensions.
1043 */
1044 void
1045 SPDesktop::zoom_page()
1046 {
1047 Geom::Rect d(Geom::Point(0, 0),
1048 Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1050 if (d.minExtent() < 1.0) {
1051 return;
1052 }
1054 set_display_area(d, 10);
1055 }
1057 /**
1058 * Set display area to current document width.
1059 */
1060 void
1061 SPDesktop::zoom_page_width()
1062 {
1063 Geom::Rect const a = get_display_area();
1065 if (sp_document_width(doc()) < 1.0) {
1066 return;
1067 }
1069 Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1070 Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1072 set_display_area(d, 10);
1073 }
1075 /**
1076 * Zoom to selection.
1077 */
1078 void
1079 SPDesktop::zoom_selection()
1080 {
1081 Geom::OptRect const d = selection->bounds();
1083 if ( !d || d->minExtent() < 0.1 ) {
1084 return;
1085 }
1087 set_display_area(*d, 10);
1088 }
1090 /**
1091 * Tell widget to let zoom widget grab keyboard focus.
1092 */
1093 void
1094 SPDesktop::zoom_grab_focus()
1095 {
1096 _widget->letZoomGrabFocus();
1097 }
1099 /**
1100 * Zoom to whole drawing.
1101 */
1102 void
1103 SPDesktop::zoom_drawing()
1104 {
1105 g_return_if_fail (doc() != NULL);
1106 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1107 g_return_if_fail (docitem != NULL);
1109 Geom::OptRect d = sp_item_bbox_desktop(docitem);
1111 /* Note that the second condition here indicates that
1112 ** there are no items in the drawing.
1113 */
1114 if ( !d || d->minExtent() < 0.1 ) {
1115 return;
1116 }
1118 set_display_area(*d, 10);
1119 }
1121 /**
1122 * Scroll canvas by specific coordinate amount in svg coordinates.
1123 */
1124 void
1125 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1126 {
1127 double scale = _d2w.descrim();
1128 scroll_world(dx*scale, dy*scale, is_scrolling);
1129 }
1131 /**
1132 * Scroll canvas by specific coordinate amount.
1133 */
1134 void
1135 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1136 {
1137 g_assert(_widget);
1139 Geom::Rect const viewbox = canvas->getViewbox();
1141 sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1143 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1144 sp_box3d_context_update_lines(event_context);
1146 _widget->updateRulers();
1147 _widget->updateScrollbars(_d2w.descrim());
1148 }
1150 bool
1151 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1152 {
1153 using Geom::X;
1154 using Geom::Y;
1156 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1157 gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1159 // autoscrolldistance is in screen pixels, but the display area is in document units
1160 autoscrolldistance /= _d2w.descrim();
1161 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1162 Geom::Rect dbox = get_display_area();
1163 dbox.expandBy(-autoscrolldistance);
1165 if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1166 !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y]) ) {
1168 Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1170 gdouble x_to;
1171 if (p[X] < dbox.min()[X])
1172 x_to = dbox.min()[X];
1173 else if (p[X] > dbox.max()[X])
1174 x_to = dbox.max()[X];
1175 else
1176 x_to = p[X];
1178 gdouble y_to;
1179 if (p[Y] < dbox.min()[Y])
1180 y_to = dbox.min()[Y];
1181 else if (p[Y] > dbox.max()[Y])
1182 y_to = dbox.max()[Y];
1183 else
1184 y_to = p[Y];
1186 Geom::Point const d_dt(x_to, y_to);
1187 Geom::Point const d_w( d_dt * _d2w );
1188 Geom::Point const moved_w( d_w - s_w );
1190 if (autoscrollspeed == 0)
1191 autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1193 if (autoscrollspeed != 0)
1194 scroll_world (autoscrollspeed * moved_w);
1196 return true;
1197 }
1198 return false;
1199 }
1201 bool
1202 SPDesktop::is_iconified()
1203 {
1204 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1205 }
1207 void
1208 SPDesktop::iconify()
1209 {
1210 _widget->setIconified();
1211 }
1213 bool
1214 SPDesktop::is_maximized()
1215 {
1216 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1217 }
1219 void
1220 SPDesktop::maximize()
1221 {
1222 _widget->setMaximized();
1223 }
1225 bool
1226 SPDesktop::is_fullscreen()
1227 {
1228 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1229 }
1231 void
1232 SPDesktop::fullscreen()
1233 {
1234 _widget->setFullscreen();
1235 }
1237 /** \brief Checks to see if the user is working in focused mode
1239 Returns the value of \c _focusMode
1240 */
1241 bool
1242 SPDesktop::is_focusMode()
1243 {
1244 return _focusMode;
1245 }
1247 /** \brief Changes whether the user is in focus mode or not
1248 \param mode Which mode the view should be in
1250 */
1251 void
1252 SPDesktop::focusMode (bool mode)
1253 {
1254 if (mode == _focusMode) { return; }
1256 _focusMode = mode;
1258 layoutWidget();
1259 //sp_desktop_widget_layout(SPDesktopWidget);
1261 return;
1262 }
1264 void
1265 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1266 {
1267 _widget->getGeometry (x, y, w, h);
1268 }
1270 void
1271 SPDesktop::setWindowPosition (Geom::Point p)
1272 {
1273 _widget->setPosition (p);
1274 }
1276 void
1277 SPDesktop::setWindowSize (gint w, gint h)
1278 {
1279 _widget->setSize (w, h);
1280 }
1282 void
1283 SPDesktop::setWindowTransient (void *p, int transient_policy)
1284 {
1285 _widget->setTransient (p, transient_policy);
1286 }
1288 Gtk::Window*
1289 SPDesktop::getToplevel( )
1290 {
1291 return _widget->getWindow();
1292 }
1294 void
1295 SPDesktop::presentWindow()
1296 {
1297 _widget->present();
1298 }
1300 bool
1301 SPDesktop::warnDialog (gchar *text)
1302 {
1303 return _widget->warnDialog (text);
1304 }
1306 void
1307 SPDesktop::toggleRulers()
1308 {
1309 _widget->toggleRulers();
1310 }
1312 void
1313 SPDesktop::toggleScrollbars()
1314 {
1315 _widget->toggleScrollbars();
1316 }
1318 void
1319 SPDesktop::layoutWidget()
1320 {
1321 _widget->layout();
1322 }
1324 void
1325 SPDesktop::destroyWidget()
1326 {
1327 _widget->destroy();
1328 }
1330 bool
1331 SPDesktop::shutdown()
1332 {
1333 return _widget->shutdown();
1334 }
1336 bool SPDesktop::onDeleteUI (GdkEventAny*)
1337 {
1338 if(shutdown())
1339 return true;
1341 destroyWidget();
1342 return false;
1343 }
1345 /**
1346 * onWindowStateEvent
1347 *
1348 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1349 * Since GTK doesn't have a way to query this state information directly, we
1350 * record it for the desktop here, and also possibly trigger a layout.
1351 */
1352 bool
1353 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1354 {
1355 // Record the desktop window's state
1356 window_state = event->new_window_state;
1358 // Layout may differ depending on full-screen mode or not
1359 GdkWindowState changed = event->changed_mask;
1360 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1361 layoutWidget();
1362 }
1364 return false;
1365 }
1367 void
1368 SPDesktop::setToolboxFocusTo (gchar const *label)
1369 {
1370 _widget->setToolboxFocusTo (label);
1371 }
1373 void
1374 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1375 {
1376 _widget->setToolboxAdjustmentValue (id, val);
1377 }
1379 void
1380 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1381 {
1382 _widget->setToolboxSelectOneValue (id, val);
1383 }
1385 bool
1386 SPDesktop::isToolboxButtonActive (gchar const *id)
1387 {
1388 return _widget->isToolboxButtonActive (id);
1389 }
1391 void
1392 SPDesktop::emitToolSubselectionChanged(gpointer data)
1393 {
1394 _tool_subselection_changed.emit(data);
1395 inkscape_subselection_changed (this);
1396 }
1398 void
1399 SPDesktop::updateNow()
1400 {
1401 sp_canvas_update_now(canvas);
1402 }
1404 void
1405 SPDesktop::enableInteraction()
1406 {
1407 _widget->enableInteraction();
1408 }
1410 void SPDesktop::disableInteraction()
1411 {
1412 _widget->disableInteraction();
1413 }
1415 void SPDesktop::setWaitingCursor()
1416 {
1417 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1418 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1419 gdk_cursor_unref(waiting);
1420 // GDK needs the flush for the cursor change to take effect
1421 gdk_flush();
1422 waiting_cursor = true;
1423 }
1425 void SPDesktop::clearWaitingCursor()
1426 {
1427 if (waiting_cursor)
1428 sp_event_context_update_cursor(sp_desktop_event_context(this));
1429 }
1431 void SPDesktop::toggleColorProfAdjust()
1432 {
1433 _widget->toggleColorProfAdjust();
1434 }
1436 void SPDesktop::toggleGrids()
1437 {
1438 if (namedview->grids) {
1439 if(gridgroup) {
1440 showGrids(!grids_visible);
1441 }
1442 } else {
1443 //there is no grid present at the moment. add a rectangular grid and make it visible
1444 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1445 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1446 showGrids(true);
1447 }
1448 }
1450 void SPDesktop::showGrids(bool show, bool dirty_document)
1451 {
1452 grids_visible = show;
1453 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1454 if (show) {
1455 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1456 } else {
1457 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1458 }
1459 }
1461 void SPDesktop::toggleSnapGlobal()
1462 {
1463 bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1464 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1465 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1466 }
1468 //----------------------------------------------------------------------
1469 // Callback implementations. The virtual ones are connected by the view.
1471 void
1472 SPDesktop::onPositionSet (double x, double y)
1473 {
1474 _widget->viewSetPosition (Geom::Point(x,y));
1475 }
1477 void
1478 SPDesktop::onResized (double /*x*/, double /*y*/)
1479 {
1480 // Nothing called here
1481 }
1483 /**
1484 * Redraw callback; queues Gtk redraw; connected by View.
1485 */
1486 void
1487 SPDesktop::onRedrawRequested ()
1488 {
1489 if (main) {
1490 _widget->requestCanvasUpdate();
1491 }
1492 }
1494 void
1495 SPDesktop::updateCanvasNow()
1496 {
1497 _widget->requestCanvasUpdateAndWait();
1498 }
1500 /**
1501 * Associate document with desktop.
1502 */
1503 void
1504 SPDesktop::setDocument (SPDocument *doc)
1505 {
1506 if (this->doc() && doc) {
1507 namedview->hide(this);
1508 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1509 }
1511 if (_layer_hierarchy) {
1512 _layer_hierarchy->clear();
1513 delete _layer_hierarchy;
1514 }
1515 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1516 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1517 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1518 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1519 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1521 /* setup EventLog */
1522 event_log = new Inkscape::EventLog(doc);
1523 doc->addUndoObserver(*event_log);
1525 _commit_connection.disconnect();
1526 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1528 /// \todo fixme: This condition exists to make sure the code
1529 /// inside is NOT called on initialization, only on replacement. But there
1530 /// are surely more safe methods to accomplish this.
1531 // TODO since the comment had reversed logic, check the intent of this block of code:
1532 if (drawing) {
1533 NRArenaItem *ai = 0;
1535 namedview = sp_document_namedview (doc, NULL);
1536 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1537 number = namedview->getViewCount();
1539 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1540 SP_CANVAS_ARENA (drawing)->arena,
1541 dkey,
1542 SP_ITEM_SHOW_DISPLAY);
1543 if (ai) {
1544 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1545 }
1546 namedview->show(this);
1547 /* Ugly hack */
1548 activate_guides (true);
1549 /* Ugly hack */
1550 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1551 }
1553 _document_replaced_signal.emit (this, doc);
1555 View::setDocument (doc);
1556 }
1558 void
1559 SPDesktop::onStatusMessage
1560 (Inkscape::MessageType type, gchar const *message)
1561 {
1562 if (_widget) {
1563 _widget->setMessage(type, message);
1564 }
1565 }
1567 void
1568 SPDesktop::onDocumentURISet (gchar const* uri)
1569 {
1570 _widget->setTitle(uri);
1571 }
1573 /**
1574 * Resized callback.
1575 */
1576 void
1577 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1578 {
1579 _doc2dt[5] = height;
1580 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1581 Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1582 SP_CTRLRECT(page)->setRectangle(a);
1583 SP_CTRLRECT(page_border)->setRectangle(a);
1584 }
1587 void
1588 SPDesktop::_onActivate (SPDesktop* dt)
1589 {
1590 if (!dt->_widget) return;
1591 dt->_widget->activateDesktop();
1592 }
1594 void
1595 SPDesktop::_onDeactivate (SPDesktop* dt)
1596 {
1597 if (!dt->_widget) return;
1598 dt->_widget->deactivateDesktop();
1599 }
1601 void
1602 SPDesktop::_onSelectionModified
1603 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1604 {
1605 if (!dt->_widget) return;
1606 dt->_widget->updateScrollbars (dt->_d2w.descrim());
1607 }
1609 static void
1610 _onSelectionChanged
1611 (Inkscape::Selection *selection, SPDesktop *desktop)
1612 {
1613 /** \todo
1614 * only change the layer for single selections, or what?
1615 * This seems reasonable -- for multiple selections there can be many
1616 * different layers involved.
1617 */
1618 SPItem *item=selection->singleItem();
1619 if (item) {
1620 SPObject *layer=desktop->layerForObject(item);
1621 if ( layer && layer != desktop->currentLayer() ) {
1622 desktop->setCurrentLayer(layer);
1623 }
1624 }
1625 }
1627 /**
1628 * Calls event handler of current event context.
1629 * \param arena Unused
1630 * \todo fixme
1631 */
1632 static gint
1633 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1634 {
1635 if (ai) {
1636 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1637 return sp_event_context_item_handler (desktop->event_context, spi, event);
1638 } else {
1639 return sp_event_context_root_handler (desktop->event_context, event);
1640 }
1641 }
1643 static void
1644 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1645 g_return_if_fail(SP_IS_GROUP(layer));
1646 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1647 }
1649 /// Callback
1650 static void
1651 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1652 g_return_if_fail(SP_IS_GROUP(layer));
1653 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1654 }
1656 /// Callback
1657 static void
1658 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1659 SPDesktop *desktop)
1660 {
1661 desktop->_layer_changed_signal.emit (bottom);
1662 }
1664 /// Called when document is starting to be rebuilt.
1665 static void
1666 _reconstruction_start (SPDesktop * desktop)
1667 {
1668 // printf("Desktop, starting reconstruction\n");
1669 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1670 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1672 /*
1673 GSList const * selection_objs = desktop->selection->list();
1674 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1676 }
1677 */
1678 desktop->selection->clear();
1680 // printf("Desktop, starting reconstruction end\n");
1681 }
1683 /// Called when document rebuild is finished.
1684 static void
1685 _reconstruction_finish (SPDesktop * desktop)
1686 {
1687 // printf("Desktop, finishing reconstruction\n");
1688 if (desktop->_reconstruction_old_layer_id == NULL)
1689 return;
1691 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1692 if (newLayer != NULL)
1693 desktop->setCurrentLayer(newLayer);
1695 g_free(desktop->_reconstruction_old_layer_id);
1696 desktop->_reconstruction_old_layer_id = NULL;
1697 // printf("Desktop, finishing reconstruction end\n");
1698 return;
1699 }
1701 /**
1702 * Namedview_modified callback.
1703 */
1704 static void
1705 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1706 {
1707 SPNamedView *nv=SP_NAMEDVIEW(obj);
1709 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1711 /* Show/hide page background */
1712 if (nv->pagecolor & 0xff) {
1713 sp_canvas_item_show (desktop->table);
1714 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1715 sp_canvas_item_move_to_z (desktop->table, 0);
1716 } else {
1717 sp_canvas_item_hide (desktop->table);
1718 }
1720 /* Show/hide page border */
1721 if (nv->showborder) {
1722 // show
1723 sp_canvas_item_show (desktop->page_border);
1724 // set color and shadow
1725 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1726 if (nv->pageshadow) {
1727 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1728 }
1729 // place in the z-order stack
1730 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1731 sp_canvas_item_move_to_z (desktop->page_border, 2);
1732 } else {
1733 int order = sp_canvas_item_order (desktop->page_border);
1734 int morder = sp_canvas_item_order (desktop->drawing);
1735 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1736 morder - order);
1737 }
1738 } else {
1739 sp_canvas_item_hide (desktop->page_border);
1740 if (nv->pageshadow) {
1741 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1742 }
1743 }
1745 /* Show/hide page shadow */
1746 if (nv->showpageshadow && nv->pageshadow) {
1747 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1748 } else {
1749 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1750 }
1752 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1753 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1754 (SP_RGBA32_R_U(nv->pagecolor) +
1755 SP_RGBA32_G_U(nv->pagecolor) +
1756 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1757 // the background color is light or transparent, use black outline
1758 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1759 } else { // use white outline
1760 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1761 }
1762 }
1763 }
1765 Geom::Matrix SPDesktop::w2d() const
1766 {
1767 return _w2d;
1768 }
1770 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1771 {
1772 return p * _w2d;
1773 }
1775 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1776 {
1777 return p * _d2w;
1778 }
1780 Geom::Matrix SPDesktop::doc2dt() const
1781 {
1782 return _doc2dt;
1783 }
1785 Geom::Matrix SPDesktop::dt2doc() const
1786 {
1787 // doc2dt is its own inverse
1788 return _doc2dt;
1789 }
1791 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1792 {
1793 return p * _doc2dt;
1794 }
1796 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1797 {
1798 return p * dt2doc();
1799 }
1802 /**
1803 * Pop event context from desktop's context stack. Never used.
1804 */
1805 // void
1806 // SPDesktop::pop_event_context (unsigned int key)
1807 // {
1808 // SPEventContext *ec = NULL;
1809 //
1810 // if (event_context && event_context->key == key) {
1811 // g_return_if_fail (event_context);
1812 // g_return_if_fail (event_context->next);
1813 // ec = event_context;
1814 // sp_event_context_deactivate (ec);
1815 // event_context = ec->next;
1816 // sp_event_context_activate (event_context);
1817 // _event_context_changed_signal.emit (this, ec);
1818 // }
1819 //
1820 // SPEventContext *ref = event_context;
1821 // while (ref && ref->next && ref->next->key != key)
1822 // ref = ref->next;
1823 //
1824 // if (ref && ref->next) {
1825 // ec = ref->next;
1826 // ref->next = ec->next;
1827 // }
1828 //
1829 // if (ec) {
1830 // sp_event_context_finish (ec);
1831 // g_object_unref (G_OBJECT (ec));
1832 // }
1833 // }
1835 /*
1836 Local Variables:
1837 mode:c++
1838 c-file-style:"stroustrup"
1839 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1840 indent-tabs-mode:nil
1841 fill-column:99
1842 End:
1843 */
1844 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :