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