004a0116eed4971c277fe942a7883ad810e18493
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 "prefs-utils.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( NR::Matrix(NR::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();
174 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
176 current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
178 namedview = nv;
179 canvas = aCanvas;
181 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
182 /* Kill flicker */
183 sp_document_ensure_up_to_date (document);
185 /* Setup Dialog Manager */
186 _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
188 dkey = sp_item_display_key_new (1);
190 /* Connect document */
191 setDocument (document);
193 number = namedview->getViewCount();
196 /* Setup Canvas */
197 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
199 SPCanvasGroup *root = sp_canvas_root (canvas);
201 /* Setup adminstrative layers */
202 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
203 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
204 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
205 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
207 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
208 SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
209 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
210 sp_canvas_item_move_to_z (table, 0);
212 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
213 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
214 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
216 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
217 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
219 SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
221 if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
222 // Start in outline mode
223 setDisplayModeOutline();
224 } else {
225 // Start in normal mode, default
226 setDisplayModeNormal();
227 }
229 gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
230 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
231 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
232 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
233 tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
235 /* Push select tool to the bottom of stack */
236 /** \todo
237 * FIXME: this is the only call to this. Everything else seems to just
238 * call "set" instead of "push". Can we assume that there is only one
239 * context ever?
240 */
241 push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
243 // display rect and zoom are now handled in sp_desktop_widget_realize()
245 Geom::Rect const d(Geom::Point(0.0, 0.0),
246 Geom::Point(sp_document_width(document), sp_document_height(document)));
248 SP_CTRLRECT(page)->setRectangle(d);
249 SP_CTRLRECT(page_border)->setRectangle(d);
251 /* the following sets the page shadow on the canvas
252 It was originally set to 5, which is really cheesy!
253 It now is an attribute in the document's namedview. If a value of
254 0 is used, then the constructor for a shadow is not initialized.
255 */
257 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
258 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
259 }
262 /* Connect event for page resize */
263 _doc2dt[5] = sp_document_height (document);
264 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
266 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
268 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
269 SP_CANVAS_ARENA (drawing)->arena,
270 dkey,
271 SP_ITEM_SHOW_DISPLAY);
272 if (ai) {
273 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
274 }
276 namedview->show(this);
277 /* Ugly hack */
278 activate_guides (true);
279 /* Ugly hack */
280 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
282 /* Set up notification of rebuilding the document, this allows
283 for saving object related settings in the document. */
284 _reconstruction_start_connection =
285 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
286 _reconstruction_finish_connection =
287 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
288 _reconstruction_old_layer_id = NULL;
290 // ?
291 // sp_active_desktop_set (desktop);
292 _inkscape = INKSCAPE;
294 _activate_connection = _activate_signal.connect(
295 sigc::bind(
296 sigc::ptr_fun(_onActivate),
297 this
298 )
299 );
300 _deactivate_connection = _deactivate_signal.connect(
301 sigc::bind(
302 sigc::ptr_fun(_onDeactivate),
303 this
304 )
305 );
307 _sel_modified_connection = selection->connectModified(
308 sigc::bind(
309 sigc::ptr_fun(&_onSelectionModified),
310 this
311 )
312 );
313 _sel_changed_connection = selection->connectChanged(
314 sigc::bind(
315 sigc::ptr_fun(&_onSelectionChanged),
316 this
317 )
318 );
321 /* setup LayerManager */
322 // (Setting up after the connections are all in place, as it may use some of them)
323 layer_manager = new Inkscape::LayerManager( this );
325 showGrids(namedview->grids_visible, false);
327 temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
328 snapindicator = new Inkscape::Display::SnapIndicator ( this );
329 }
332 void SPDesktop::destroy()
333 {
334 if (snapindicator) {
335 delete snapindicator;
336 snapindicator = NULL;
337 }
338 if (temporary_item_list) {
339 delete temporary_item_list;
340 temporary_item_list = NULL;
341 }
343 if (selection) {
344 delete selection;
345 selection = NULL;
346 }
348 namedview->hide(this);
350 _activate_connection.disconnect();
351 _deactivate_connection.disconnect();
352 _sel_modified_connection.disconnect();
353 _sel_changed_connection.disconnect();
354 _modified_connection.disconnect();
355 _commit_connection.disconnect();
356 _reconstruction_start_connection.disconnect();
357 _reconstruction_finish_connection.disconnect();
359 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
360 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
361 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
363 while (event_context) {
364 SPEventContext *ec = event_context;
365 event_context = ec->next;
366 sp_event_context_finish (ec);
367 g_object_unref (G_OBJECT (ec));
368 }
370 if (_layer_hierarchy) {
371 delete _layer_hierarchy;
372 // _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
373 }
375 if (layer_manager) {
376 delete layer_manager;
377 layer_manager = NULL;
378 }
380 if (_inkscape) {
381 _inkscape = NULL;
382 }
384 if (drawing) {
385 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
386 drawing = NULL;
387 }
389 delete _guides_message_context;
390 _guides_message_context = NULL;
392 g_list_free (zooms_past);
393 g_list_free (zooms_future);
394 }
396 SPDesktop::~SPDesktop() {}
398 //--------------------------------------------------------------------
399 /* Public methods */
402 /* These methods help for temporarily showing things on-canvas.
403 * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
404 * is when you want to prematurely remove the item from the canvas, by calling
405 * desktop->remove_temporary_canvasitem(tempitem).
406 */
407 /** Note that lifetime is measured in milliseconds
408 * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
409 * delete the object for you and the reference will become invalid without you knowing it.
410 * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
411 * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
412 * because the object might be deleted already without you knowing it.
413 * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
414 */
415 Inkscape::Display::TemporaryItem *
416 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
417 {
418 if (move_to_bottom) {
419 sp_canvas_item_move_to_z(item, 0);
420 }
422 return temporary_item_list->add_item(item, lifetime);
423 }
425 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
426 */
427 void
428 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
429 {
430 // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
431 if (tempitem && temporary_item_list) {
432 temporary_item_list->delete_item(tempitem);
433 }
434 }
436 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
437 SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
438 canvas->rendermode = mode;
439 _display_mode = mode;
440 if (mode != Inkscape::RENDERMODE_OUTLINE) {
441 _saved_display_mode = _display_mode;
442 }
443 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
444 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
445 }
447 void SPDesktop::displayModeToggle() {
448 if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
449 _setDisplayMode(_saved_display_mode);
450 } else {
451 _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
452 }
453 }
455 /**
456 * Returns current root (=bottom) layer.
457 */
458 SPObject *SPDesktop::currentRoot() const
459 {
460 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
461 }
463 /**
464 * Returns current top layer.
465 */
466 SPObject *SPDesktop::currentLayer() const
467 {
468 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
469 }
471 /**
472 * Sets the current layer of the desktop.
473 *
474 * Make \a object the top layer.
475 */
476 void SPDesktop::setCurrentLayer(SPObject *object) {
477 g_return_if_fail(SP_IS_GROUP(object));
478 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
479 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
480 _layer_hierarchy->setBottom(object);
481 }
483 void SPDesktop::toggleLayerSolo(SPObject *object) {
484 g_return_if_fail(SP_IS_GROUP(object));
485 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
487 bool othersShowing = false;
488 std::vector<SPObject*> layers;
489 for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
490 layers.push_back(obj);
491 othersShowing |= !SP_ITEM(obj)->isHidden();
492 }
493 for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
494 layers.push_back(obj);
495 othersShowing |= !SP_ITEM(obj)->isHidden();
496 }
499 if ( SP_ITEM(object)->isHidden() ) {
500 SP_ITEM(object)->setHidden(false);
501 }
503 for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
504 SP_ITEM(*it)->setHidden(othersShowing);
505 }
506 }
508 /**
509 * Return layer that contains \a object.
510 */
511 SPObject *SPDesktop::layerForObject(SPObject *object) {
512 g_return_val_if_fail(object != NULL, NULL);
514 SPObject *root=currentRoot();
515 object = SP_OBJECT_PARENT(object);
516 while ( object && object != root && !isLayer(object) ) {
517 object = SP_OBJECT_PARENT(object);
518 }
519 return object;
520 }
522 /**
523 * True if object is a layer.
524 */
525 bool SPDesktop::isLayer(SPObject *object) const {
526 return ( SP_IS_GROUP(object)
527 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
528 == SPGroup::LAYER ) );
529 }
531 /**
532 * True if desktop viewport fully contains \a item's bbox.
533 */
534 bool SPDesktop::isWithinViewport (SPItem *item) const
535 {
536 Geom::Rect const viewport = get_display_area();
537 boost::optional<NR::Rect> const bbox = sp_item_bbox_desktop(item);
538 if (bbox) {
539 return viewport.contains(to_2geom(*bbox));
540 } else {
541 return true;
542 }
543 }
545 ///
546 bool SPDesktop::itemIsHidden(SPItem const *item) const {
547 return item->isHidden(this->dkey);
548 }
550 /**
551 * Set activate property of desktop; emit signal if changed.
552 */
553 void
554 SPDesktop::set_active (bool new_active)
555 {
556 if (new_active != _active) {
557 _active = new_active;
558 if (new_active) {
559 _activate_signal.emit();
560 } else {
561 _deactivate_signal.emit();
562 }
563 }
564 }
566 /**
567 * Set activate status of current desktop's named view.
568 */
569 void
570 SPDesktop::activate_guides(bool activate)
571 {
572 guides_active = activate;
573 namedview->activateGuides(this, activate);
574 }
576 /**
577 * Make desktop switch documents.
578 */
579 void
580 SPDesktop::change_document (SPDocument *theDocument)
581 {
582 g_return_if_fail (theDocument != NULL);
584 /* unselect everything before switching documents */
585 selection->clear();
587 setDocument (theDocument);
589 /* update the rulers, connect the desktop widget's signal to the new namedview etc.
590 (this can probably be done in a better way) */
591 Gtk::Window *parent = this->getToplevel();
592 g_assert(parent != NULL);
593 SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
594 if (dtw) dtw->desktop = this;
595 sp_desktop_widget_update_namedview(dtw);
597 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
598 _document_replaced_signal.emit (this, theDocument);
599 }
601 /**
602 * Make desktop switch event contexts.
603 */
604 void
605 SPDesktop::set_event_context (GtkType type, const gchar *config)
606 {
607 SPEventContext *ec;
608 while (event_context) {
609 ec = event_context;
610 sp_event_context_deactivate (ec);
611 event_context = ec->next;
612 sp_event_context_finish (ec);
613 g_object_unref (G_OBJECT (ec));
614 }
616 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
617 ec = sp_event_context_new (type, this, repr, 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;
631 Inkscape::XML::Node *repr;
633 if (event_context && event_context->key == key) return;
634 ref = event_context;
635 while (ref && ref->next && ref->next->key != key) ref = ref->next;
636 if (ref && ref->next) {
637 ec = ref->next;
638 ref->next = ec->next;
639 sp_event_context_finish (ec);
640 g_object_unref (G_OBJECT (ec));
641 }
643 if (event_context) sp_event_context_deactivate (event_context);
644 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
645 ec = sp_event_context_new (type, this, repr, key);
646 ec->next = event_context;
647 event_context = ec;
648 sp_event_context_activate (ec);
649 _event_context_changed_signal.emit (this, ec);
650 }
652 /**
653 * Sets the coordinate status to a given point
654 */
655 void
656 SPDesktop::set_coordinate_status (Geom::Point p) {
657 _widget->setCoordinateStatus(p);
658 }
660 /**
661 * \see sp_document_item_from_list_at_point_bottom()
662 */
663 SPItem *
664 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
665 {
666 g_return_val_if_fail (doc() != NULL, NULL);
667 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
668 }
670 /**
671 * \see sp_document_item_at_point()
672 */
673 SPItem *
674 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
675 {
676 g_return_val_if_fail (doc() != NULL, NULL);
677 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
678 }
680 /**
681 * \see sp_document_group_at_point()
682 */
683 SPItem *
684 SPDesktop::group_at_point (Geom::Point const p) const
685 {
686 g_return_val_if_fail (doc() != NULL, NULL);
687 return sp_document_group_at_point (doc(), dkey, p);
688 }
690 /**
691 * \brief Returns the mouse point in document coordinates; if mouse is
692 * outside the canvas, returns the center of canvas viewpoint
693 */
694 Geom::Point
695 SPDesktop::point() const
696 {
697 Geom::Point p = _widget->getPointer();
698 Geom::Point pw = sp_canvas_window_to_world (canvas, p);
699 p = w2d(pw);
701 Geom::Rect const r = canvas->getViewbox();
703 Geom::Point r0 = w2d(r.min());
704 Geom::Point r1 = w2d(r.max());
706 if (p[Geom::X] >= r0[Geom::X] &&
707 p[Geom::X] <= r1[Geom::X] &&
708 p[Geom::Y] >= r1[Geom::Y] &&
709 p[Geom::Y] <= r0[Geom::Y])
710 {
711 return p;
712 } else {
713 return (r0 + r1) / 2;
714 }
715 }
717 /**
718 * Put current zoom data in history list.
719 */
720 void
721 SPDesktop::push_current_zoom (GList **history)
722 {
723 Geom::Rect const area = get_display_area();
725 NRRect *old_zoom = g_new(NRRect, 1);
726 old_zoom->x0 = area.min()[NR::X];
727 old_zoom->x1 = area.max()[NR::X];
728 old_zoom->y0 = area.min()[NR::Y];
729 old_zoom->y1 = area.max()[NR::Y];
730 if ( *history == NULL
731 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
732 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
733 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
734 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
735 {
736 *history = g_list_prepend (*history, old_zoom);
737 }
738 }
740 /**
741 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
742 */
743 void
744 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
745 {
746 g_assert(_widget);
748 // save the zoom
749 if (log) {
750 push_current_zoom(&zooms_past);
751 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
752 g_list_free (zooms_future);
753 zooms_future = NULL;
754 }
756 double const cx = 0.5 * (x0 + x1);
757 double const cy = 0.5 * (y0 + y1);
759 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
760 Geom::Rect viewbox = canvas->getViewbox();
761 viewbox.expandBy(border);
763 double scale = _d2w.descrim();
764 double newscale;
765 if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
766 newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
767 } else {
768 newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
769 }
771 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
773 int clear = FALSE;
774 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
775 /* Set zoom factors */
776 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
777 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
778 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
779 clear = TRUE;
780 }
782 /* Calculate top left corner (in document pixels) */
783 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
784 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
786 /* Scroll */
787 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
789 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
790 sp_box3d_context_update_lines(event_context);
792 _widget->updateRulers();
793 _widget->updateScrollbars(_d2w.descrim());
794 _widget->updateZoom();
795 }
797 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
798 {
799 set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
800 }
802 /**
803 * Return viewbox dimensions.
804 */
805 Geom::Rect SPDesktop::get_display_area() const
806 {
807 Geom::Rect const viewbox = canvas->getViewbox();
809 double const scale = _d2w[0];
811 return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
812 Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
813 }
815 /**
816 * Revert back to previous zoom if possible.
817 */
818 void
819 SPDesktop::prev_zoom()
820 {
821 if (zooms_past == NULL) {
822 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
823 return;
824 }
826 // push current zoom into forward zooms list
827 push_current_zoom (&zooms_future);
829 // restore previous zoom
830 set_display_area (((NRRect *) zooms_past->data)->x0,
831 ((NRRect *) zooms_past->data)->y0,
832 ((NRRect *) zooms_past->data)->x1,
833 ((NRRect *) zooms_past->data)->y1,
834 0, false);
836 // remove the just-added zoom from the past zooms list
837 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
838 }
840 /**
841 * Set zoom to next in list.
842 */
843 void
844 SPDesktop::next_zoom()
845 {
846 if (zooms_future == NULL) {
847 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
848 return;
849 }
851 // push current zoom into past zooms list
852 push_current_zoom (&zooms_past);
854 // restore next zoom
855 set_display_area (((NRRect *) zooms_future->data)->x0,
856 ((NRRect *) zooms_future->data)->y0,
857 ((NRRect *) zooms_future->data)->x1,
858 ((NRRect *) zooms_future->data)->y1,
859 0, false);
861 // remove the just-used zoom from the zooms_future list
862 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
863 }
865 #include "tools-switch.h"
866 #include "node-context.h"
867 #include "shape-editor.h"
868 #include "nodepath.h"
870 /** \brief Performs a quick zoom into what the user is working on
871 \param enable Whether we're going in or out of quick zoom
873 */
874 void
875 SPDesktop::zoom_quick (bool enable)
876 {
877 if (enable == _quick_zoom_enabled) {
878 return;
879 }
881 if (enable == true) {
882 _quick_zoom_stored_area = get_display_area();
883 bool zoomed = false;
885 if (!zoomed) {
886 SPItem * singleItem = selection->singleItem();
887 if (singleItem != NULL && tools_isactive(this, TOOLS_NODES)) {
888 SPNodeContext * ncontext = SP_NODE_CONTEXT(event_context);
890 Inkscape::NodePath::Path * nodepath = ncontext->shape_editor->get_nodepath();
891 // printf("I've got a nodepath, crazy\n");
893 Geom::Rect nodes;
894 bool firstnode = true;
896 if (nodepath->selected) {
897 for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
898 Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
899 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
900 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
901 if (node->selected) {
902 // printf("\tSelected node\n");
903 if (firstnode) {
904 nodes = Geom::Rect(node->pos, node->pos);
905 firstnode = false;
906 } else {
907 nodes.expandTo(node->pos);
908 }
910 if (node->p.other != NULL) {
911 /* Include previous node pos */
912 nodes.expandTo(node->p.other->pos);
914 /* Include previous handle */
915 if (!sp_node_side_is_line(node, &node->p)) {
916 nodes.expandTo(node->p.pos);
917 }
918 }
920 if (node->n.other != NULL) {
921 /* Include previous node pos */
922 nodes.expandTo(node->n.other->pos);
924 /* Include previous handle */
925 if (!sp_node_side_is_line(node, &node->n)) {
926 nodes.expandTo(node->n.pos);
927 }
928 }
929 }
930 }
931 }
933 if (!firstnode && nodes.area() * 2.0 < _quick_zoom_stored_area.area()) {
934 set_display_area(nodes, 10);
935 zoomed = true;
936 }
937 }
938 }
939 }
941 if (!zoomed) {
942 boost::optional<Geom::Rect> const d = selection->bounds_2geom();
943 if (d && !d->isEmpty() && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
944 set_display_area(*d, 10);
945 zoomed = true;
946 }
947 }
949 if (!zoomed) {
950 zoom_relative(_quick_zoom_stored_area.midpoint()[NR::X], _quick_zoom_stored_area.midpoint()[NR::Y], 2.0);
951 zoomed = true;
952 }
953 } else {
954 set_display_area(_quick_zoom_stored_area, 0);
955 }
957 _quick_zoom_enabled = enable;
958 return;
959 }
961 /**
962 * Zoom to point with absolute zoom factor.
963 */
964 void
965 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
966 {
967 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
969 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
970 // this check prevents "sliding" when trying to zoom in at maximum zoom;
971 /// \todo someone please fix calculations properly and remove this hack
972 if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
973 return;
975 Geom::Rect const viewbox = canvas->getViewbox();
977 double const width2 = viewbox.dimensions()[NR::X] / zoom;
978 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
980 set_display_area(cx - px * width2,
981 cy - py * height2,
982 cx + (1 - px) * width2,
983 cy + (1 - py) * height2,
984 0.0);
985 }
987 /**
988 * Zoom to center with absolute zoom factor.
989 */
990 void
991 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
992 {
993 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
994 }
996 /**
997 * Zoom to point with relative zoom factor.
998 */
999 void
1000 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
1001 {
1002 Geom::Rect const area = get_display_area();
1004 if (cx < area.min()[NR::X]) {
1005 cx = area.min()[NR::X];
1006 }
1007 if (cx > area.max()[NR::X]) {
1008 cx = area.max()[NR::X];
1009 }
1010 if (cy < area.min()[NR::Y]) {
1011 cy = area.min()[NR::Y];
1012 }
1013 if (cy > area.max()[NR::Y]) {
1014 cy = area.max()[NR::Y];
1015 }
1017 gdouble const scale = _d2w.descrim() * zoom;
1018 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
1019 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
1021 zoom_absolute_keep_point(cx, cy, px, py, scale);
1022 }
1024 /**
1025 * Zoom to center with relative zoom factor.
1026 */
1027 void
1028 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1029 {
1030 gdouble scale = _d2w.descrim() * zoom;
1031 zoom_absolute (cx, cy, scale);
1032 }
1034 /**
1035 * Set display area to origin and current document dimensions.
1036 */
1037 void
1038 SPDesktop::zoom_page()
1039 {
1040 Geom::Rect d(Geom::Point(0, 0),
1041 Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1043 // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 1.0; is it safe to ignore it?
1044 if (d.isEmpty()) {
1045 return;
1046 }
1048 set_display_area(d, 10);
1049 }
1051 /**
1052 * Set display area to current document width.
1053 */
1054 void
1055 SPDesktop::zoom_page_width()
1056 {
1057 Geom::Rect const a = get_display_area();
1059 if (sp_document_width(doc()) < 1.0) {
1060 return;
1061 }
1063 Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1064 Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1066 set_display_area(d, 10);
1067 }
1069 /**
1070 * Zoom to selection.
1071 */
1072 void
1073 SPDesktop::zoom_selection()
1074 {
1075 boost::optional<Geom::Rect> const d = to_2geom(selection->bounds());
1077 // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 0.1; is it safe to ignore it?
1078 if ( !d || d->isEmpty() ) {
1079 return;
1080 }
1082 set_display_area(*d, 10);
1083 }
1085 /**
1086 * Tell widget to let zoom widget grab keyboard focus.
1087 */
1088 void
1089 SPDesktop::zoom_grab_focus()
1090 {
1091 _widget->letZoomGrabFocus();
1092 }
1094 /**
1095 * Zoom to whole drawing.
1096 */
1097 void
1098 SPDesktop::zoom_drawing()
1099 {
1100 g_return_if_fail (doc() != NULL);
1101 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1102 g_return_if_fail (docitem != NULL);
1104 boost::optional<Geom::Rect> d = to_2geom(sp_item_bbox_desktop(docitem));
1106 /* Note that the second condition here indicates that
1107 ** there are no items in the drawing.
1108 */
1109 // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 1.0; is it safe to ignore it?
1110 if ( !d || d->isEmpty() ) {
1111 return;
1112 }
1114 set_display_area(*d, 10);
1115 }
1117 /**
1118 * Scroll canvas by specific coordinate amount in svg coordinates.
1119 */
1120 void
1121 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1122 {
1123 double scale = _d2w.descrim();
1124 scroll_world(dx*scale, dy*scale, is_scrolling);
1125 }
1127 /**
1128 * Scroll canvas by specific coordinate amount.
1129 */
1130 void
1131 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1132 {
1133 g_assert(_widget);
1135 Geom::Rect const viewbox = canvas->getViewbox();
1137 sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1139 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1140 sp_box3d_context_update_lines(event_context);
1142 _widget->updateRulers();
1143 _widget->updateScrollbars(_d2w.descrim());
1144 }
1146 bool
1147 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1148 {
1149 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
1151 // autoscrolldistance is in screen pixels, but the display area is in document units
1152 autoscrolldistance /= _d2w.descrim();
1153 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1154 Geom::Rect dbox = get_display_area();
1155 dbox.expandBy(-autoscrolldistance);
1157 if (!(p[NR::X] > dbox.min()[NR::X] && p[NR::X] < dbox.max()[NR::X]) ||
1158 !(p[NR::Y] > dbox.min()[NR::Y] && p[NR::Y] < dbox.max()[NR::Y]) ) {
1160 Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1162 gdouble x_to;
1163 if (p[NR::X] < dbox.min()[NR::X])
1164 x_to = dbox.min()[NR::X];
1165 else if (p[NR::X] > dbox.max()[NR::X])
1166 x_to = dbox.max()[NR::X];
1167 else
1168 x_to = p[NR::X];
1170 gdouble y_to;
1171 if (p[NR::Y] < dbox.min()[NR::Y])
1172 y_to = dbox.min()[NR::Y];
1173 else if (p[NR::Y] > dbox.max()[NR::Y])
1174 y_to = dbox.max()[NR::Y];
1175 else
1176 y_to = p[NR::Y];
1178 Geom::Point const d_dt(x_to, y_to);
1179 Geom::Point const d_w( d_dt * _d2w );
1180 Geom::Point const moved_w( d_w - s_w );
1182 if (autoscrollspeed == 0)
1183 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
1185 if (autoscrollspeed != 0)
1186 scroll_world (autoscrollspeed * moved_w);
1188 return true;
1189 }
1190 return false;
1191 }
1193 bool
1194 SPDesktop::is_iconified()
1195 {
1196 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1197 }
1199 void
1200 SPDesktop::iconify()
1201 {
1202 _widget->setIconified();
1203 }
1205 bool
1206 SPDesktop::is_maximized()
1207 {
1208 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1209 }
1211 void
1212 SPDesktop::maximize()
1213 {
1214 _widget->setMaximized();
1215 }
1217 bool
1218 SPDesktop::is_fullscreen()
1219 {
1220 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1221 }
1223 void
1224 SPDesktop::fullscreen()
1225 {
1226 _widget->setFullscreen();
1227 }
1229 /** \brief Checks to see if the user is working in focused mode
1231 Returns the value of \c _focusMode
1232 */
1233 bool
1234 SPDesktop::is_focusMode()
1235 {
1236 return _focusMode;
1237 }
1239 /** \brief Changes whether the user is in focus mode or not
1240 \param mode Which mode the view should be in
1242 */
1243 void
1244 SPDesktop::focusMode (bool mode)
1245 {
1246 if (mode == _focusMode) { return; }
1248 _focusMode = mode;
1250 layoutWidget();
1251 //sp_desktop_widget_layout(SPDesktopWidget);
1253 return;
1254 }
1256 void
1257 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1258 {
1259 _widget->getGeometry (x, y, w, h);
1260 }
1262 void
1263 SPDesktop::setWindowPosition (Geom::Point p)
1264 {
1265 _widget->setPosition (p);
1266 }
1268 void
1269 SPDesktop::setWindowSize (gint w, gint h)
1270 {
1271 _widget->setSize (w, h);
1272 }
1274 void
1275 SPDesktop::setWindowTransient (void *p, int transient_policy)
1276 {
1277 _widget->setTransient (p, transient_policy);
1278 }
1280 Gtk::Window*
1281 SPDesktop::getToplevel( )
1282 {
1283 return _widget->getWindow();
1284 }
1286 void
1287 SPDesktop::presentWindow()
1288 {
1289 _widget->present();
1290 }
1292 bool
1293 SPDesktop::warnDialog (gchar *text)
1294 {
1295 return _widget->warnDialog (text);
1296 }
1298 void
1299 SPDesktop::toggleRulers()
1300 {
1301 _widget->toggleRulers();
1302 }
1304 void
1305 SPDesktop::toggleScrollbars()
1306 {
1307 _widget->toggleScrollbars();
1308 }
1310 void
1311 SPDesktop::layoutWidget()
1312 {
1313 _widget->layout();
1314 }
1316 void
1317 SPDesktop::destroyWidget()
1318 {
1319 _widget->destroy();
1320 }
1322 bool
1323 SPDesktop::shutdown()
1324 {
1325 return _widget->shutdown();
1326 }
1328 bool SPDesktop::onDeleteUI (GdkEventAny*)
1329 {
1330 if(shutdown())
1331 return true;
1333 destroyWidget();
1334 return false;
1335 }
1337 /**
1338 * onWindowStateEvent
1339 *
1340 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1341 * Since GTK doesn't have a way to query this state information directly, we
1342 * record it for the desktop here, and also possibly trigger a layout.
1343 */
1344 bool
1345 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1346 {
1347 // Record the desktop window's state
1348 window_state = event->new_window_state;
1350 // Layout may differ depending on full-screen mode or not
1351 GdkWindowState changed = event->changed_mask;
1352 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1353 layoutWidget();
1354 }
1356 return false;
1357 }
1359 void
1360 SPDesktop::setToolboxFocusTo (gchar const *label)
1361 {
1362 _widget->setToolboxFocusTo (label);
1363 }
1365 void
1366 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1367 {
1368 _widget->setToolboxAdjustmentValue (id, val);
1369 }
1371 void
1372 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1373 {
1374 _widget->setToolboxSelectOneValue (id, val);
1375 }
1377 bool
1378 SPDesktop::isToolboxButtonActive (gchar const *id)
1379 {
1380 return _widget->isToolboxButtonActive (id);
1381 }
1383 void
1384 SPDesktop::emitToolSubselectionChanged(gpointer data)
1385 {
1386 _tool_subselection_changed.emit(data);
1387 inkscape_subselection_changed (this);
1388 }
1390 void
1391 SPDesktop::updateNow()
1392 {
1393 sp_canvas_update_now(canvas);
1394 }
1396 void
1397 SPDesktop::enableInteraction()
1398 {
1399 _widget->enableInteraction();
1400 }
1402 void SPDesktop::disableInteraction()
1403 {
1404 _widget->disableInteraction();
1405 }
1407 void SPDesktop::setWaitingCursor()
1408 {
1409 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1410 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1411 gdk_cursor_unref(waiting);
1412 // GDK needs the flush for the cursor change to take effect
1413 gdk_flush();
1414 waiting_cursor = true;
1415 }
1417 void SPDesktop::clearWaitingCursor()
1418 {
1419 if (waiting_cursor)
1420 sp_event_context_update_cursor(sp_desktop_event_context(this));
1421 }
1423 void SPDesktop::toggleColorProfAdjust()
1424 {
1425 _widget->toggleColorProfAdjust();
1426 }
1428 void SPDesktop::toggleGrids()
1429 {
1430 if (namedview->grids) {
1431 if(gridgroup) {
1432 showGrids(!grids_visible);
1433 }
1434 } else {
1435 //there is no grid present at the moment. add a rectangular grid and make it visible
1436 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1437 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1438 showGrids(true);
1439 }
1440 }
1442 void SPDesktop::showGrids(bool show, bool dirty_document)
1443 {
1444 grids_visible = show;
1445 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1446 if (show) {
1447 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1448 } else {
1449 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1450 }
1451 }
1453 void SPDesktop::toggleSnapping()
1454 {
1455 bool v = namedview->snap_manager.getSnapEnabledGlobally();
1456 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1457 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1458 }
1460 //----------------------------------------------------------------------
1461 // Callback implementations. The virtual ones are connected by the view.
1463 void
1464 SPDesktop::onPositionSet (double x, double y)
1465 {
1466 _widget->viewSetPosition (Geom::Point(x,y));
1467 }
1469 void
1470 SPDesktop::onResized (double /*x*/, double /*y*/)
1471 {
1472 // Nothing called here
1473 }
1475 /**
1476 * Redraw callback; queues Gtk redraw; connected by View.
1477 */
1478 void
1479 SPDesktop::onRedrawRequested ()
1480 {
1481 if (main) {
1482 _widget->requestCanvasUpdate();
1483 }
1484 }
1486 void
1487 SPDesktop::updateCanvasNow()
1488 {
1489 _widget->requestCanvasUpdateAndWait();
1490 }
1492 /**
1493 * Associate document with desktop.
1494 */
1495 void
1496 SPDesktop::setDocument (SPDocument *doc)
1497 {
1498 if (this->doc() && doc) {
1499 namedview->hide(this);
1500 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1501 }
1503 if (_layer_hierarchy) {
1504 _layer_hierarchy->clear();
1505 delete _layer_hierarchy;
1506 }
1507 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1508 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1509 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1510 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1511 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1513 /* setup EventLog */
1514 event_log = new Inkscape::EventLog(doc);
1515 doc->addUndoObserver(*event_log);
1517 _commit_connection.disconnect();
1518 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1520 /// \todo fixme: This condition exists to make sure the code
1521 /// inside is NOT called on initialization, only on replacement. But there
1522 /// are surely more safe methods to accomplish this.
1523 // TODO since the comment had reversed logic, check the intent of this block of code:
1524 if (drawing) {
1525 NRArenaItem *ai = 0;
1527 namedview = sp_document_namedview (doc, NULL);
1528 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1529 number = namedview->getViewCount();
1531 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1532 SP_CANVAS_ARENA (drawing)->arena,
1533 dkey,
1534 SP_ITEM_SHOW_DISPLAY);
1535 if (ai) {
1536 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1537 }
1538 namedview->show(this);
1539 /* Ugly hack */
1540 activate_guides (true);
1541 /* Ugly hack */
1542 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1543 }
1545 _document_replaced_signal.emit (this, doc);
1547 View::setDocument (doc);
1548 }
1550 void
1551 SPDesktop::onStatusMessage
1552 (Inkscape::MessageType type, gchar const *message)
1553 {
1554 if (_widget) {
1555 _widget->setMessage(type, message);
1556 }
1557 }
1559 void
1560 SPDesktop::onDocumentURISet (gchar const* uri)
1561 {
1562 _widget->setTitle(uri);
1563 }
1565 /**
1566 * Resized callback.
1567 */
1568 void
1569 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1570 {
1571 _doc2dt[5] = height;
1572 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1573 Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1574 SP_CTRLRECT(page)->setRectangle(a);
1575 SP_CTRLRECT(page_border)->setRectangle(a);
1576 }
1579 void
1580 SPDesktop::_onActivate (SPDesktop* dt)
1581 {
1582 if (!dt->_widget) return;
1583 dt->_widget->activateDesktop();
1584 }
1586 void
1587 SPDesktop::_onDeactivate (SPDesktop* dt)
1588 {
1589 if (!dt->_widget) return;
1590 dt->_widget->deactivateDesktop();
1591 }
1593 void
1594 SPDesktop::_onSelectionModified
1595 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1596 {
1597 if (!dt->_widget) return;
1598 dt->_widget->updateScrollbars (dt->_d2w.descrim());
1599 }
1601 static void
1602 _onSelectionChanged
1603 (Inkscape::Selection *selection, SPDesktop *desktop)
1604 {
1605 /** \todo
1606 * only change the layer for single selections, or what?
1607 * This seems reasonable -- for multiple selections there can be many
1608 * different layers involved.
1609 */
1610 SPItem *item=selection->singleItem();
1611 if (item) {
1612 SPObject *layer=desktop->layerForObject(item);
1613 if ( layer && layer != desktop->currentLayer() ) {
1614 desktop->setCurrentLayer(layer);
1615 }
1616 }
1617 }
1619 /**
1620 * Calls event handler of current event context.
1621 * \param arena Unused
1622 * \todo fixme
1623 */
1624 static gint
1625 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1626 {
1627 if (ai) {
1628 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1629 return sp_event_context_item_handler (desktop->event_context, spi, event);
1630 } else {
1631 return sp_event_context_root_handler (desktop->event_context, event);
1632 }
1633 }
1635 static void
1636 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1637 g_return_if_fail(SP_IS_GROUP(layer));
1638 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1639 }
1641 /// Callback
1642 static void
1643 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1644 g_return_if_fail(SP_IS_GROUP(layer));
1645 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1646 }
1648 /// Callback
1649 static void
1650 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1651 SPDesktop *desktop)
1652 {
1653 desktop->_layer_changed_signal.emit (bottom);
1654 }
1656 /// Called when document is starting to be rebuilt.
1657 static void
1658 _reconstruction_start (SPDesktop * desktop)
1659 {
1660 // printf("Desktop, starting reconstruction\n");
1661 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1662 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1664 /*
1665 GSList const * selection_objs = desktop->selection->list();
1666 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1668 }
1669 */
1670 desktop->selection->clear();
1672 // printf("Desktop, starting reconstruction end\n");
1673 }
1675 /// Called when document rebuild is finished.
1676 static void
1677 _reconstruction_finish (SPDesktop * desktop)
1678 {
1679 // printf("Desktop, finishing reconstruction\n");
1680 if (desktop->_reconstruction_old_layer_id == NULL)
1681 return;
1683 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1684 if (newLayer != NULL)
1685 desktop->setCurrentLayer(newLayer);
1687 g_free(desktop->_reconstruction_old_layer_id);
1688 desktop->_reconstruction_old_layer_id = NULL;
1689 // printf("Desktop, finishing reconstruction end\n");
1690 return;
1691 }
1693 /**
1694 * Namedview_modified callback.
1695 */
1696 static void
1697 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1698 {
1699 SPNamedView *nv=SP_NAMEDVIEW(obj);
1701 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1703 /* Recalculate snap distances */
1704 /* FIXME: why is the desktop getting involved in setting up something
1705 ** that is entirely to do with the namedview?
1706 */
1707 _update_snap_distances (desktop);
1709 /* Show/hide page background */
1710 if (nv->pagecolor & 0xff) {
1711 sp_canvas_item_show (desktop->table);
1712 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1713 sp_canvas_item_move_to_z (desktop->table, 0);
1714 } else {
1715 sp_canvas_item_hide (desktop->table);
1716 }
1718 /* Show/hide page border */
1719 if (nv->showborder) {
1720 // show
1721 sp_canvas_item_show (desktop->page_border);
1722 // set color and shadow
1723 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1724 if (nv->pageshadow) {
1725 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1726 }
1727 // place in the z-order stack
1728 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1729 sp_canvas_item_move_to_z (desktop->page_border, 2);
1730 } else {
1731 int order = sp_canvas_item_order (desktop->page_border);
1732 int morder = sp_canvas_item_order (desktop->drawing);
1733 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1734 morder - order);
1735 }
1736 } else {
1737 sp_canvas_item_hide (desktop->page_border);
1738 if (nv->pageshadow) {
1739 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1740 }
1741 }
1743 /* Show/hide page shadow */
1744 if (nv->showpageshadow && nv->pageshadow) {
1745 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1746 } else {
1747 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1748 }
1750 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1751 (SP_RGBA32_R_U(nv->pagecolor) +
1752 SP_RGBA32_G_U(nv->pagecolor) +
1753 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1754 // the background color is light or transparent, use black outline
1755 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1756 } else { // use white outline
1757 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1758 }
1759 }
1760 }
1762 /**
1763 * Callback to reset snapper's distances.
1764 */
1765 static void
1766 _update_snap_distances (SPDesktop *desktop)
1767 {
1768 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1770 SPNamedView &nv = *desktop->namedview;
1772 //tell all grid snappers
1773 for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1774 Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1775 grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1776 *nv.gridtoleranceunit,
1777 px));
1778 }
1780 nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1781 *nv.guidetoleranceunit,
1782 px));
1783 nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1784 *nv.objecttoleranceunit,
1785 px));
1786 }
1789 Geom::Matrix SPDesktop::w2d() const
1790 {
1791 return _w2d;
1792 }
1794 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1795 {
1796 return p * _w2d;
1797 }
1799 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1800 {
1801 return p * _d2w;
1802 }
1804 Geom::Matrix SPDesktop::doc2dt() const
1805 {
1806 return _doc2dt;
1807 }
1809 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1810 {
1811 return p * _doc2dt;
1812 }
1814 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1815 {
1816 return p * _doc2dt.inverse();
1817 }
1820 /**
1821 * Pop event context from desktop's context stack. Never used.
1822 */
1823 // void
1824 // SPDesktop::pop_event_context (unsigned int key)
1825 // {
1826 // SPEventContext *ec = NULL;
1827 //
1828 // if (event_context && event_context->key == key) {
1829 // g_return_if_fail (event_context);
1830 // g_return_if_fail (event_context->next);
1831 // ec = event_context;
1832 // sp_event_context_deactivate (ec);
1833 // event_context = ec->next;
1834 // sp_event_context_activate (event_context);
1835 // _event_context_changed_signal.emit (this, ec);
1836 // }
1837 //
1838 // SPEventContext *ref = event_context;
1839 // while (ref && ref->next && ref->next->key != key)
1840 // ref = ref->next;
1841 //
1842 // if (ref && ref->next) {
1843 // ec = ref->next;
1844 // ref->next = ec->next;
1845 // }
1846 //
1847 // if (ec) {
1848 // sp_event_context_finish (ec);
1849 // g_object_unref (G_OBJECT (ec));
1850 // }
1851 // }
1853 /*
1854 Local Variables:
1855 mode:c++
1856 c-file-style:"stroustrup"
1857 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1858 indent-tabs-mode:nil
1859 fill-column:99
1860 End:
1861 */
1862 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :