1 #define __SP_DESKTOP_C__
3 /** \file
4 * Editable view implementation
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * MenTaLguY <mental@rydia.net>
9 * bulia byak <buliabyak@users.sf.net>
10 * Ralf Stephan <ralf@ark.in-berlin.de>
11 * John Bintz <jcoswell@coswellproductions.org>
12 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
13 *
14 * Copyright (C) 2007 Jon A. Cruz
15 * Copyright (C) 2006-2008 Johan Engelen
16 * Copyright (C) 2006 John Bintz
17 * Copyright (C) 2004 MenTaLguY
18 * Copyright (C) 1999-2002 Lauris Kaplinski
19 * Copyright (C) 2000-2001 Ximian, Inc.
20 *
21 * Released under GNU GPL, read the file 'COPYING' for more information
22 */
24 /** \class SPDesktop
25 * SPDesktop is a subclass of View, implementing an editable document
26 * canvas. It is extensively used by many UI controls that need certain
27 * visual representations of their own.
28 *
29 * SPDesktop provides a certain set of SPCanvasItems, serving as GUI
30 * layers of different control objects. The one containing the whole
31 * document is the drawing layer. In addition to it, there are grid,
32 * guide, sketch and control layers. The sketch layer is used for
33 * temporary drawing objects, before the real objects in document are
34 * created. The control layer contains editing knots, rubberband and
35 * similar non-document UI objects.
36 *
37 * Each SPDesktop is associated with a SPNamedView node of the document
38 * tree. Currently, all desktops are created from a single main named
39 * view, but in the future there may be support for different ones.
40 * SPNamedView serves as an in-document container for desktop-related
41 * data, like grid and guideline placement, snapping options and so on.
42 *
43 * Associated with each SPDesktop are the two most important editing
44 * related objects - SPSelection and SPEventContext.
45 *
46 * Sodipodi keeps track of the active desktop and invokes notification
47 * signals whenever it changes. UI elements can use these to update their
48 * display to the selection of the currently active editing window.
49 * (Lauris Kaplinski)
50 */
52 #ifdef HAVE_CONFIG_H
53 # include "config.h"
54 #endif
56 #include <glibmm/i18n.h>
57 #include <sigc++/functors/mem_fun.h>
58 #include <gtkmm.h>
60 #include <2geom/rect.h>
61 #include "macros.h"
62 #include "inkscape-private.h"
63 #include "desktop.h"
64 #include "desktop-events.h"
65 #include "desktop-handles.h"
66 #include "document.h"
67 #include "message-stack.h"
68 #include "selection.h"
69 #include "select-context.h"
70 #include "sp-namedview.h"
71 #include "color.h"
72 #include "sp-item-group.h"
73 #include "preferences.h"
74 #include "object-hierarchy.h"
75 #include "helper/units.h"
76 #include "display/canvas-arena.h"
77 #include "display/nr-arena.h"
78 #include "display/gnome-canvas-acetate.h"
79 #include "display/sodipodi-ctrlrect.h"
80 #include "display/sp-canvas-util.h"
81 #include "display/canvas-temporary-item-list.h"
82 #include "display/snap-indicator.h"
83 #include "ui/dialog/dialog-manager.h"
84 #include "xml/repr.h"
85 #include "message-context.h"
86 #include "device-manager.h"
87 #include "layer-fns.h"
88 #include "layer-manager.h"
89 #include "event-log.h"
90 #include "display/canvas-grid.h"
91 #include "widgets/desktop-widget.h"
92 #include "box3d-context.h"
94 #include "display/sp-canvas.h"
96 namespace Inkscape { namespace XML { class Node; }}
98 // Callback declarations
99 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
100 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
101 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
102 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
103 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
104 static void _reconstruction_start(SPDesktop * desktop);
105 static void _reconstruction_finish(SPDesktop * desktop);
106 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
108 /**
109 * Return new desktop object.
110 * \pre namedview != NULL.
111 * \pre canvas != NULL.
112 */
113 SPDesktop::SPDesktop() :
114 _dlg_mgr( 0 ),
115 namedview( 0 ),
116 canvas( 0 ),
117 selection( 0 ),
118 event_context( 0 ),
119 layer_manager( 0 ),
120 event_log( 0 ),
121 temporary_item_list( 0 ),
122 snapindicator( 0 ),
123 acetate( 0 ),
124 main( 0 ),
125 gridgroup( 0 ),
126 guides( 0 ),
127 drawing( 0 ),
128 sketch( 0 ),
129 controls( 0 ),
130 tempgroup ( 0 ),
131 table( 0 ),
132 page( 0 ),
133 page_border( 0 ),
134 current( 0 ),
135 _focusMode(false),
136 zooms_past( 0 ),
137 zooms_future( 0 ),
138 dkey( 0 ),
139 number( 0 ),
140 window_state(0),
141 interaction_disabled_counter( 0 ),
142 waiting_cursor( false ),
143 guides_active( false ),
144 gr_item( 0 ),
145 gr_point_type( 0 ),
146 gr_point_i( 0 ),
147 gr_fill_or_stroke( true ),
148 _layer_hierarchy( 0 ),
149 _reconstruction_old_layer_id( 0 ),
150 _display_mode(Inkscape::RENDERMODE_NORMAL),
151 _saved_display_mode(Inkscape::RENDERMODE_NORMAL),
152 _widget( 0 ),
153 _inkscape( 0 ),
154 _guides_message_context( 0 ),
155 _active( false ),
156 _w2d(),
157 _d2w(),
158 _doc2dt( Geom::Scale(1, -1) ),
159 grids_visible( false )
160 {
161 _d2w.setIdentity();
162 _w2d.setIdentity();
164 selection = Inkscape::GC::release( new Inkscape::Selection(this) );
165 }
167 void
168 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
169 {
170 // Temporary workaround for link order issues:
171 Inkscape::DeviceManager::getManager().getDevices();
172 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
174 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
176 current = prefs->getStyle("/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->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
221 if (prefs->getBool("/options/startmode/outline")) {
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 Geom::OptRect const bbox = sp_item_bbox_desktop(item);
538 if (bbox) {
539 return viewport.contains(*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 ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
617 ec->next = event_context;
618 event_context = ec;
619 sp_event_context_activate (ec);
620 _event_context_changed_signal.emit (this, ec);
621 }
623 /**
624 * Push event context onto desktop's context stack.
625 */
626 void
627 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
628 {
629 SPEventContext *ref, *ec;
631 if (event_context && event_context->key == key) return;
632 ref = event_context;
633 while (ref && ref->next && ref->next->key != key) ref = ref->next;
634 if (ref && ref->next) {
635 ec = ref->next;
636 ref->next = ec->next;
637 sp_event_context_finish (ec);
638 g_object_unref (G_OBJECT (ec));
639 }
641 if (event_context) sp_event_context_deactivate (event_context);
642 ec = sp_event_context_new (type, this, config, key);
643 ec->next = event_context;
644 event_context = ec;
645 sp_event_context_activate (ec);
646 _event_context_changed_signal.emit (this, ec);
647 }
649 /**
650 * Sets the coordinate status to a given point
651 */
652 void
653 SPDesktop::set_coordinate_status (Geom::Point p) {
654 _widget->setCoordinateStatus(p);
655 }
657 /**
658 * \see sp_document_item_from_list_at_point_bottom()
659 */
660 SPItem *
661 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
662 {
663 g_return_val_if_fail (doc() != NULL, NULL);
664 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
665 }
667 /**
668 * \see sp_document_item_at_point()
669 */
670 SPItem *
671 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
672 {
673 g_return_val_if_fail (doc() != NULL, NULL);
674 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
675 }
677 /**
678 * \see sp_document_group_at_point()
679 */
680 SPItem *
681 SPDesktop::group_at_point (Geom::Point const p) const
682 {
683 g_return_val_if_fail (doc() != NULL, NULL);
684 return sp_document_group_at_point (doc(), dkey, p);
685 }
687 /**
688 * \brief Returns the mouse point in document coordinates; if mouse is
689 * outside the canvas, returns the center of canvas viewpoint
690 */
691 Geom::Point
692 SPDesktop::point() const
693 {
694 Geom::Point p = _widget->getPointer();
695 Geom::Point pw = sp_canvas_window_to_world (canvas, p);
696 p = w2d(pw);
698 Geom::Rect const r = canvas->getViewbox();
700 Geom::Point r0 = w2d(r.min());
701 Geom::Point r1 = w2d(r.max());
703 if (p[Geom::X] >= r0[Geom::X] &&
704 p[Geom::X] <= r1[Geom::X] &&
705 p[Geom::Y] >= r1[Geom::Y] &&
706 p[Geom::Y] <= r0[Geom::Y])
707 {
708 return p;
709 } else {
710 return (r0 + r1) / 2;
711 }
712 }
714 /**
715 * Put current zoom data in history list.
716 */
717 void
718 SPDesktop::push_current_zoom (GList **history)
719 {
720 Geom::Rect const area = get_display_area();
722 NRRect *old_zoom = g_new(NRRect, 1);
723 old_zoom->x0 = area.min()[Geom::X];
724 old_zoom->x1 = area.max()[Geom::X];
725 old_zoom->y0 = area.min()[Geom::Y];
726 old_zoom->y1 = area.max()[Geom::Y];
727 if ( *history == NULL
728 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
729 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
730 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
731 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
732 {
733 *history = g_list_prepend (*history, old_zoom);
734 }
735 }
737 /**
738 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
739 */
740 void
741 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
742 {
743 g_assert(_widget);
745 // save the zoom
746 if (log) {
747 push_current_zoom(&zooms_past);
748 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
749 g_list_free (zooms_future);
750 zooms_future = NULL;
751 }
753 double const cx = 0.5 * (x0 + x1);
754 double const cy = 0.5 * (y0 + y1);
756 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
757 Geom::Rect viewbox = canvas->getViewbox();
758 viewbox.expandBy(-border);
760 double scale = _d2w.descrim();
761 double newscale;
762 if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
763 newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
764 } else {
765 newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
766 }
768 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
770 int clear = FALSE;
771 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
772 /* Set zoom factors */
773 _d2w = Geom::Scale(newscale, -newscale);
774 _w2d = Geom::Scale(1/newscale, 1/-newscale);
775 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
776 clear = TRUE;
777 }
779 /* Calculate top left corner (in document pixels) */
780 x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
781 y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
783 /* Scroll */
784 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
786 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
787 sp_box3d_context_update_lines(event_context);
789 _widget->updateRulers();
790 _widget->updateScrollbars(_d2w.descrim());
791 _widget->updateZoom();
792 }
794 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
795 {
796 set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
797 }
799 /**
800 * Return viewbox dimensions.
801 */
802 Geom::Rect SPDesktop::get_display_area() const
803 {
804 Geom::Rect const viewbox = canvas->getViewbox();
806 double const scale = _d2w[0];
808 return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
809 Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
810 }
812 /**
813 * Revert back to previous zoom if possible.
814 */
815 void
816 SPDesktop::prev_zoom()
817 {
818 if (zooms_past == NULL) {
819 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
820 return;
821 }
823 // push current zoom into forward zooms list
824 push_current_zoom (&zooms_future);
826 // restore previous zoom
827 set_display_area (((NRRect *) zooms_past->data)->x0,
828 ((NRRect *) zooms_past->data)->y0,
829 ((NRRect *) zooms_past->data)->x1,
830 ((NRRect *) zooms_past->data)->y1,
831 0, false);
833 // remove the just-added zoom from the past zooms list
834 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
835 }
837 /**
838 * Set zoom to next in list.
839 */
840 void
841 SPDesktop::next_zoom()
842 {
843 if (zooms_future == NULL) {
844 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
845 return;
846 }
848 // push current zoom into past zooms list
849 push_current_zoom (&zooms_past);
851 // restore next zoom
852 set_display_area (((NRRect *) zooms_future->data)->x0,
853 ((NRRect *) zooms_future->data)->y0,
854 ((NRRect *) zooms_future->data)->x1,
855 ((NRRect *) zooms_future->data)->y1,
856 0, false);
858 // remove the just-used zoom from the zooms_future list
859 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
860 }
862 #include "tools-switch.h"
863 #include "node-context.h"
864 #include "shape-editor.h"
865 #include "nodepath.h"
867 /** \brief Performs a quick zoom into what the user is working on
868 \param enable Whether we're going in or out of quick zoom
870 */
871 void
872 SPDesktop::zoom_quick (bool enable)
873 {
874 if (enable == _quick_zoom_enabled) {
875 return;
876 }
878 if (enable == true) {
879 _quick_zoom_stored_area = get_display_area();
880 bool zoomed = false;
882 if (!zoomed) {
883 SPItem * singleItem = selection->singleItem();
884 if (singleItem != NULL && tools_isactive(this, TOOLS_NODES)) {
886 Inkscape::NodePath::Path * nodepath = event_context->shape_editor->get_nodepath();
887 // printf("I've got a nodepath, crazy\n");
889 Geom::Rect nodes;
890 bool firstnode = true;
892 if (nodepath->selected) {
893 for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
894 Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
895 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
896 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
897 if (node->selected) {
898 // printf("\tSelected node\n");
899 if (firstnode) {
900 nodes = Geom::Rect(node->pos, node->pos);
901 firstnode = false;
902 } else {
903 nodes.expandTo(node->pos);
904 }
906 if (node->p.other != NULL) {
907 /* Include previous node pos */
908 nodes.expandTo(node->p.other->pos);
910 /* Include previous handle */
911 if (!sp_node_side_is_line(node, &node->p)) {
912 nodes.expandTo(node->p.pos);
913 }
914 }
916 if (node->n.other != NULL) {
917 /* Include previous node pos */
918 nodes.expandTo(node->n.other->pos);
920 /* Include previous handle */
921 if (!sp_node_side_is_line(node, &node->n)) {
922 nodes.expandTo(node->n.pos);
923 }
924 }
925 }
926 }
927 }
929 if (!firstnode && nodes.area() * 2.0 < _quick_zoom_stored_area.area()) {
930 set_display_area(nodes, 10);
931 zoomed = true;
932 }
933 }
934 }
935 }
937 if (!zoomed) {
938 Geom::OptRect const d = selection->bounds();
939 if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
940 set_display_area(*d, 10);
941 zoomed = true;
942 }
943 }
945 if (!zoomed) {
946 zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
947 zoomed = true;
948 }
949 } else {
950 set_display_area(_quick_zoom_stored_area, 0);
951 }
953 _quick_zoom_enabled = enable;
954 return;
955 }
957 /**
958 * Zoom to point with absolute zoom factor.
959 */
960 void
961 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
962 {
963 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
965 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
966 // this check prevents "sliding" when trying to zoom in at maximum zoom;
967 /// \todo someone please fix calculations properly and remove this hack
968 if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
969 return;
971 Geom::Rect const viewbox = canvas->getViewbox();
973 double const width2 = viewbox.dimensions()[Geom::X] / zoom;
974 double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
976 set_display_area(cx - px * width2,
977 cy - py * height2,
978 cx + (1 - px) * width2,
979 cy + (1 - py) * height2,
980 0.0);
981 }
983 /**
984 * Zoom to center with absolute zoom factor.
985 */
986 void
987 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
988 {
989 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
990 }
992 /**
993 * Zoom to point with relative zoom factor.
994 */
995 void
996 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
997 {
998 Geom::Rect const area = get_display_area();
1000 if (cx < area.min()[Geom::X]) {
1001 cx = area.min()[Geom::X];
1002 }
1003 if (cx > area.max()[Geom::X]) {
1004 cx = area.max()[Geom::X];
1005 }
1006 if (cy < area.min()[Geom::Y]) {
1007 cy = area.min()[Geom::Y];
1008 }
1009 if (cy > area.max()[Geom::Y]) {
1010 cy = area.max()[Geom::Y];
1011 }
1013 gdouble const scale = _d2w.descrim() * zoom;
1014 double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1015 double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
1017 zoom_absolute_keep_point(cx, cy, px, py, scale);
1018 }
1020 /**
1021 * Zoom to center with relative zoom factor.
1022 */
1023 void
1024 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1025 {
1026 gdouble scale = _d2w.descrim() * zoom;
1027 zoom_absolute (cx, cy, scale);
1028 }
1030 /**
1031 * Set display area to origin and current document dimensions.
1032 */
1033 void
1034 SPDesktop::zoom_page()
1035 {
1036 Geom::Rect d(Geom::Point(0, 0),
1037 Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1039 if (d.minExtent() < 1.0) {
1040 return;
1041 }
1043 set_display_area(d, 10);
1044 }
1046 /**
1047 * Set display area to current document width.
1048 */
1049 void
1050 SPDesktop::zoom_page_width()
1051 {
1052 Geom::Rect const a = get_display_area();
1054 if (sp_document_width(doc()) < 1.0) {
1055 return;
1056 }
1058 Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1059 Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1061 set_display_area(d, 10);
1062 }
1064 /**
1065 * Zoom to selection.
1066 */
1067 void
1068 SPDesktop::zoom_selection()
1069 {
1070 Geom::OptRect const d = selection->bounds();
1072 if ( !d || d->minExtent() < 0.1 ) {
1073 return;
1074 }
1076 set_display_area(*d, 10);
1077 }
1079 /**
1080 * Tell widget to let zoom widget grab keyboard focus.
1081 */
1082 void
1083 SPDesktop::zoom_grab_focus()
1084 {
1085 _widget->letZoomGrabFocus();
1086 }
1088 /**
1089 * Zoom to whole drawing.
1090 */
1091 void
1092 SPDesktop::zoom_drawing()
1093 {
1094 g_return_if_fail (doc() != NULL);
1095 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1096 g_return_if_fail (docitem != NULL);
1098 Geom::OptRect d = sp_item_bbox_desktop(docitem);
1100 /* Note that the second condition here indicates that
1101 ** there are no items in the drawing.
1102 */
1103 if ( !d || d->minExtent() < 0.1 ) {
1104 return;
1105 }
1107 set_display_area(*d, 10);
1108 }
1110 /**
1111 * Scroll canvas by specific coordinate amount in svg coordinates.
1112 */
1113 void
1114 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1115 {
1116 double scale = _d2w.descrim();
1117 scroll_world(dx*scale, dy*scale, is_scrolling);
1118 }
1120 /**
1121 * Scroll canvas by specific coordinate amount.
1122 */
1123 void
1124 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1125 {
1126 g_assert(_widget);
1128 Geom::Rect const viewbox = canvas->getViewbox();
1130 sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1132 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1133 sp_box3d_context_update_lines(event_context);
1135 _widget->updateRulers();
1136 _widget->updateScrollbars(_d2w.descrim());
1137 }
1139 bool
1140 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1141 {
1142 using Geom::X;
1143 using Geom::Y;
1145 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1146 gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1148 // autoscrolldistance is in screen pixels, but the display area is in document units
1149 autoscrolldistance /= _d2w.descrim();
1150 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1151 Geom::Rect dbox = get_display_area();
1152 dbox.expandBy(-autoscrolldistance);
1154 if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1155 !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y]) ) {
1157 Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1159 gdouble x_to;
1160 if (p[X] < dbox.min()[X])
1161 x_to = dbox.min()[X];
1162 else if (p[X] > dbox.max()[X])
1163 x_to = dbox.max()[X];
1164 else
1165 x_to = p[X];
1167 gdouble y_to;
1168 if (p[Y] < dbox.min()[Y])
1169 y_to = dbox.min()[Y];
1170 else if (p[Y] > dbox.max()[Y])
1171 y_to = dbox.max()[Y];
1172 else
1173 y_to = p[Y];
1175 Geom::Point const d_dt(x_to, y_to);
1176 Geom::Point const d_w( d_dt * _d2w );
1177 Geom::Point const moved_w( d_w - s_w );
1179 if (autoscrollspeed == 0)
1180 autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1182 if (autoscrollspeed != 0)
1183 scroll_world (autoscrollspeed * moved_w);
1185 return true;
1186 }
1187 return false;
1188 }
1190 bool
1191 SPDesktop::is_iconified()
1192 {
1193 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1194 }
1196 void
1197 SPDesktop::iconify()
1198 {
1199 _widget->setIconified();
1200 }
1202 bool
1203 SPDesktop::is_maximized()
1204 {
1205 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1206 }
1208 void
1209 SPDesktop::maximize()
1210 {
1211 _widget->setMaximized();
1212 }
1214 bool
1215 SPDesktop::is_fullscreen()
1216 {
1217 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1218 }
1220 void
1221 SPDesktop::fullscreen()
1222 {
1223 _widget->setFullscreen();
1224 }
1226 /** \brief Checks to see if the user is working in focused mode
1228 Returns the value of \c _focusMode
1229 */
1230 bool
1231 SPDesktop::is_focusMode()
1232 {
1233 return _focusMode;
1234 }
1236 /** \brief Changes whether the user is in focus mode or not
1237 \param mode Which mode the view should be in
1239 */
1240 void
1241 SPDesktop::focusMode (bool mode)
1242 {
1243 if (mode == _focusMode) { return; }
1245 _focusMode = mode;
1247 layoutWidget();
1248 //sp_desktop_widget_layout(SPDesktopWidget);
1250 return;
1251 }
1253 void
1254 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1255 {
1256 _widget->getGeometry (x, y, w, h);
1257 }
1259 void
1260 SPDesktop::setWindowPosition (Geom::Point p)
1261 {
1262 _widget->setPosition (p);
1263 }
1265 void
1266 SPDesktop::setWindowSize (gint w, gint h)
1267 {
1268 _widget->setSize (w, h);
1269 }
1271 void
1272 SPDesktop::setWindowTransient (void *p, int transient_policy)
1273 {
1274 _widget->setTransient (p, transient_policy);
1275 }
1277 Gtk::Window*
1278 SPDesktop::getToplevel( )
1279 {
1280 return _widget->getWindow();
1281 }
1283 void
1284 SPDesktop::presentWindow()
1285 {
1286 _widget->present();
1287 }
1289 bool
1290 SPDesktop::warnDialog (gchar *text)
1291 {
1292 return _widget->warnDialog (text);
1293 }
1295 void
1296 SPDesktop::toggleRulers()
1297 {
1298 _widget->toggleRulers();
1299 }
1301 void
1302 SPDesktop::toggleScrollbars()
1303 {
1304 _widget->toggleScrollbars();
1305 }
1307 void
1308 SPDesktop::layoutWidget()
1309 {
1310 _widget->layout();
1311 }
1313 void
1314 SPDesktop::destroyWidget()
1315 {
1316 _widget->destroy();
1317 }
1319 bool
1320 SPDesktop::shutdown()
1321 {
1322 return _widget->shutdown();
1323 }
1325 bool SPDesktop::onDeleteUI (GdkEventAny*)
1326 {
1327 if(shutdown())
1328 return true;
1330 destroyWidget();
1331 return false;
1332 }
1334 /**
1335 * onWindowStateEvent
1336 *
1337 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1338 * Since GTK doesn't have a way to query this state information directly, we
1339 * record it for the desktop here, and also possibly trigger a layout.
1340 */
1341 bool
1342 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1343 {
1344 // Record the desktop window's state
1345 window_state = event->new_window_state;
1347 // Layout may differ depending on full-screen mode or not
1348 GdkWindowState changed = event->changed_mask;
1349 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1350 layoutWidget();
1351 }
1353 return false;
1354 }
1356 void
1357 SPDesktop::setToolboxFocusTo (gchar const *label)
1358 {
1359 _widget->setToolboxFocusTo (label);
1360 }
1362 void
1363 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1364 {
1365 _widget->setToolboxAdjustmentValue (id, val);
1366 }
1368 void
1369 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1370 {
1371 _widget->setToolboxSelectOneValue (id, val);
1372 }
1374 bool
1375 SPDesktop::isToolboxButtonActive (gchar const *id)
1376 {
1377 return _widget->isToolboxButtonActive (id);
1378 }
1380 void
1381 SPDesktop::emitToolSubselectionChanged(gpointer data)
1382 {
1383 _tool_subselection_changed.emit(data);
1384 inkscape_subselection_changed (this);
1385 }
1387 void
1388 SPDesktop::updateNow()
1389 {
1390 sp_canvas_update_now(canvas);
1391 }
1393 void
1394 SPDesktop::enableInteraction()
1395 {
1396 _widget->enableInteraction();
1397 }
1399 void SPDesktop::disableInteraction()
1400 {
1401 _widget->disableInteraction();
1402 }
1404 void SPDesktop::setWaitingCursor()
1405 {
1406 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1407 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1408 gdk_cursor_unref(waiting);
1409 // GDK needs the flush for the cursor change to take effect
1410 gdk_flush();
1411 waiting_cursor = true;
1412 }
1414 void SPDesktop::clearWaitingCursor()
1415 {
1416 if (waiting_cursor)
1417 sp_event_context_update_cursor(sp_desktop_event_context(this));
1418 }
1420 void SPDesktop::toggleColorProfAdjust()
1421 {
1422 _widget->toggleColorProfAdjust();
1423 }
1425 void SPDesktop::toggleGrids()
1426 {
1427 if (namedview->grids) {
1428 if(gridgroup) {
1429 showGrids(!grids_visible);
1430 }
1431 } else {
1432 //there is no grid present at the moment. add a rectangular grid and make it visible
1433 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1434 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1435 showGrids(true);
1436 }
1437 }
1439 void SPDesktop::showGrids(bool show, bool dirty_document)
1440 {
1441 grids_visible = show;
1442 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1443 if (show) {
1444 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1445 } else {
1446 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1447 }
1448 }
1450 void SPDesktop::toggleSnapGlobal()
1451 {
1452 bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1453 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1454 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1455 }
1457 //----------------------------------------------------------------------
1458 // Callback implementations. The virtual ones are connected by the view.
1460 void
1461 SPDesktop::onPositionSet (double x, double y)
1462 {
1463 _widget->viewSetPosition (Geom::Point(x,y));
1464 }
1466 void
1467 SPDesktop::onResized (double /*x*/, double /*y*/)
1468 {
1469 // Nothing called here
1470 }
1472 /**
1473 * Redraw callback; queues Gtk redraw; connected by View.
1474 */
1475 void
1476 SPDesktop::onRedrawRequested ()
1477 {
1478 if (main) {
1479 _widget->requestCanvasUpdate();
1480 }
1481 }
1483 void
1484 SPDesktop::updateCanvasNow()
1485 {
1486 _widget->requestCanvasUpdateAndWait();
1487 }
1489 /**
1490 * Associate document with desktop.
1491 */
1492 void
1493 SPDesktop::setDocument (SPDocument *doc)
1494 {
1495 if (this->doc() && doc) {
1496 namedview->hide(this);
1497 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1498 }
1500 if (_layer_hierarchy) {
1501 _layer_hierarchy->clear();
1502 delete _layer_hierarchy;
1503 }
1504 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1505 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1506 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1507 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1508 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1510 /* setup EventLog */
1511 event_log = new Inkscape::EventLog(doc);
1512 doc->addUndoObserver(*event_log);
1514 _commit_connection.disconnect();
1515 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1517 /// \todo fixme: This condition exists to make sure the code
1518 /// inside is NOT called on initialization, only on replacement. But there
1519 /// are surely more safe methods to accomplish this.
1520 // TODO since the comment had reversed logic, check the intent of this block of code:
1521 if (drawing) {
1522 NRArenaItem *ai = 0;
1524 namedview = sp_document_namedview (doc, NULL);
1525 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1526 number = namedview->getViewCount();
1528 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1529 SP_CANVAS_ARENA (drawing)->arena,
1530 dkey,
1531 SP_ITEM_SHOW_DISPLAY);
1532 if (ai) {
1533 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1534 }
1535 namedview->show(this);
1536 /* Ugly hack */
1537 activate_guides (true);
1538 /* Ugly hack */
1539 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1540 }
1542 _document_replaced_signal.emit (this, doc);
1544 View::setDocument (doc);
1545 }
1547 void
1548 SPDesktop::onStatusMessage
1549 (Inkscape::MessageType type, gchar const *message)
1550 {
1551 if (_widget) {
1552 _widget->setMessage(type, message);
1553 }
1554 }
1556 void
1557 SPDesktop::onDocumentURISet (gchar const* uri)
1558 {
1559 _widget->setTitle(uri);
1560 }
1562 /**
1563 * Resized callback.
1564 */
1565 void
1566 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1567 {
1568 _doc2dt[5] = height;
1569 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1570 Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1571 SP_CTRLRECT(page)->setRectangle(a);
1572 SP_CTRLRECT(page_border)->setRectangle(a);
1573 }
1576 void
1577 SPDesktop::_onActivate (SPDesktop* dt)
1578 {
1579 if (!dt->_widget) return;
1580 dt->_widget->activateDesktop();
1581 }
1583 void
1584 SPDesktop::_onDeactivate (SPDesktop* dt)
1585 {
1586 if (!dt->_widget) return;
1587 dt->_widget->deactivateDesktop();
1588 }
1590 void
1591 SPDesktop::_onSelectionModified
1592 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1593 {
1594 if (!dt->_widget) return;
1595 dt->_widget->updateScrollbars (dt->_d2w.descrim());
1596 }
1598 static void
1599 _onSelectionChanged
1600 (Inkscape::Selection *selection, SPDesktop *desktop)
1601 {
1602 /** \todo
1603 * only change the layer for single selections, or what?
1604 * This seems reasonable -- for multiple selections there can be many
1605 * different layers involved.
1606 */
1607 SPItem *item=selection->singleItem();
1608 if (item) {
1609 SPObject *layer=desktop->layerForObject(item);
1610 if ( layer && layer != desktop->currentLayer() ) {
1611 desktop->setCurrentLayer(layer);
1612 }
1613 }
1614 }
1616 /**
1617 * Calls event handler of current event context.
1618 * \param arena Unused
1619 * \todo fixme
1620 */
1621 static gint
1622 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1623 {
1624 if (ai) {
1625 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1626 return sp_event_context_item_handler (desktop->event_context, spi, event);
1627 } else {
1628 return sp_event_context_root_handler (desktop->event_context, event);
1629 }
1630 }
1632 static void
1633 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1634 g_return_if_fail(SP_IS_GROUP(layer));
1635 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1636 }
1638 /// Callback
1639 static void
1640 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1641 g_return_if_fail(SP_IS_GROUP(layer));
1642 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1643 }
1645 /// Callback
1646 static void
1647 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1648 SPDesktop *desktop)
1649 {
1650 desktop->_layer_changed_signal.emit (bottom);
1651 }
1653 /// Called when document is starting to be rebuilt.
1654 static void
1655 _reconstruction_start (SPDesktop * desktop)
1656 {
1657 // printf("Desktop, starting reconstruction\n");
1658 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1659 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1661 /*
1662 GSList const * selection_objs = desktop->selection->list();
1663 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1665 }
1666 */
1667 desktop->selection->clear();
1669 // printf("Desktop, starting reconstruction end\n");
1670 }
1672 /// Called when document rebuild is finished.
1673 static void
1674 _reconstruction_finish (SPDesktop * desktop)
1675 {
1676 // printf("Desktop, finishing reconstruction\n");
1677 if (desktop->_reconstruction_old_layer_id == NULL)
1678 return;
1680 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1681 if (newLayer != NULL)
1682 desktop->setCurrentLayer(newLayer);
1684 g_free(desktop->_reconstruction_old_layer_id);
1685 desktop->_reconstruction_old_layer_id = NULL;
1686 // printf("Desktop, finishing reconstruction end\n");
1687 return;
1688 }
1690 /**
1691 * Namedview_modified callback.
1692 */
1693 static void
1694 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1695 {
1696 SPNamedView *nv=SP_NAMEDVIEW(obj);
1698 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1700 /* Show/hide page background */
1701 if (nv->pagecolor & 0xff) {
1702 sp_canvas_item_show (desktop->table);
1703 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1704 sp_canvas_item_move_to_z (desktop->table, 0);
1705 } else {
1706 sp_canvas_item_hide (desktop->table);
1707 }
1709 /* Show/hide page border */
1710 if (nv->showborder) {
1711 // show
1712 sp_canvas_item_show (desktop->page_border);
1713 // set color and shadow
1714 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1715 if (nv->pageshadow) {
1716 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1717 }
1718 // place in the z-order stack
1719 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1720 sp_canvas_item_move_to_z (desktop->page_border, 2);
1721 } else {
1722 int order = sp_canvas_item_order (desktop->page_border);
1723 int morder = sp_canvas_item_order (desktop->drawing);
1724 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1725 morder - order);
1726 }
1727 } else {
1728 sp_canvas_item_hide (desktop->page_border);
1729 if (nv->pageshadow) {
1730 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1731 }
1732 }
1734 /* Show/hide page shadow */
1735 if (nv->showpageshadow && nv->pageshadow) {
1736 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1737 } else {
1738 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1739 }
1741 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1742 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1743 (SP_RGBA32_R_U(nv->pagecolor) +
1744 SP_RGBA32_G_U(nv->pagecolor) +
1745 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1746 // the background color is light or transparent, use black outline
1747 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1748 } else { // use white outline
1749 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1750 }
1751 }
1752 }
1754 Geom::Matrix SPDesktop::w2d() const
1755 {
1756 return _w2d;
1757 }
1759 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1760 {
1761 return p * _w2d;
1762 }
1764 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1765 {
1766 return p * _d2w;
1767 }
1769 Geom::Matrix SPDesktop::doc2dt() const
1770 {
1771 return _doc2dt;
1772 }
1774 Geom::Matrix SPDesktop::dt2doc() const
1775 {
1776 // doc2dt is its own inverse
1777 return _doc2dt;
1778 }
1780 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1781 {
1782 return p * _doc2dt;
1783 }
1785 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1786 {
1787 return p * dt2doc();
1788 }
1791 /**
1792 * Pop event context from desktop's context stack. Never used.
1793 */
1794 // void
1795 // SPDesktop::pop_event_context (unsigned int key)
1796 // {
1797 // SPEventContext *ec = NULL;
1798 //
1799 // if (event_context && event_context->key == key) {
1800 // g_return_if_fail (event_context);
1801 // g_return_if_fail (event_context->next);
1802 // ec = event_context;
1803 // sp_event_context_deactivate (ec);
1804 // event_context = ec->next;
1805 // sp_event_context_activate (event_context);
1806 // _event_context_changed_signal.emit (this, ec);
1807 // }
1808 //
1809 // SPEventContext *ref = event_context;
1810 // while (ref && ref->next && ref->next->key != key)
1811 // ref = ref->next;
1812 //
1813 // if (ref && ref->next) {
1814 // ec = ref->next;
1815 // ref->next = ec->next;
1816 // }
1817 //
1818 // if (ec) {
1819 // sp_event_context_finish (ec);
1820 // g_object_unref (G_OBJECT (ec));
1821 // }
1822 // }
1824 /*
1825 Local Variables:
1826 mode:c++
1827 c-file-style:"stroustrup"
1828 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1829 indent-tabs-mode:nil
1830 fill-column:99
1831 End:
1832 */
1833 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :