1 #define __SP_DESKTOP_C__
3 /** \file
4 * Editable view implementation
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * MenTaLguY <mental@rydia.net>
9 * bulia byak <buliabyak@users.sf.net>
10 * Ralf Stephan <ralf@ark.in-berlin.de>
11 * John Bintz <jcoswell@coswellproductions.org>
12 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
13 *
14 * Copyright (C) 2007 Jon A. Cruz
15 * Copyright (C) 2006-2008 Johan Engelen
16 * Copyright (C) 2006 John Bintz
17 * Copyright (C) 2004 MenTaLguY
18 * Copyright (C) 1999-2002 Lauris Kaplinski
19 * Copyright (C) 2000-2001 Ximian, Inc.
20 *
21 * Released under GNU GPL, read the file 'COPYING' for more information
22 */
24 /** \class SPDesktop
25 * SPDesktop is a subclass of View, implementing an editable document
26 * canvas. It is extensively used by many UI controls that need certain
27 * visual representations of their own.
28 *
29 * SPDesktop provides a certain set of SPCanvasItems, serving as GUI
30 * layers of different control objects. The one containing the whole
31 * document is the drawing layer. In addition to it, there are grid,
32 * guide, sketch and control layers. The sketch layer is used for
33 * temporary drawing objects, before the real objects in document are
34 * created. The control layer contains editing knots, rubberband and
35 * similar non-document UI objects.
36 *
37 * Each SPDesktop is associated with a SPNamedView node of the document
38 * tree. Currently, all desktops are created from a single main named
39 * view, but in the future there may be support for different ones.
40 * SPNamedView serves as an in-document container for desktop-related
41 * data, like grid and guideline placement, snapping options and so on.
42 *
43 * Associated with each SPDesktop are the two most important editing
44 * related objects - SPSelection and SPEventContext.
45 *
46 * Sodipodi keeps track of the active desktop and invokes notification
47 * signals whenever it changes. UI elements can use these to update their
48 * display to the selection of the currently active editing window.
49 * (Lauris Kaplinski)
50 */
52 #ifdef HAVE_CONFIG_H
53 # include "config.h"
54 #endif
56 #include <glibmm/i18n.h>
57 #include <sigc++/functors/mem_fun.h>
58 #include <gtkmm.h>
60 #include <2geom/rect.h>
61 #include "macros.h"
62 #include "inkscape-private.h"
63 #include "desktop.h"
64 #include "desktop-events.h"
65 #include "desktop-handles.h"
66 #include "document.h"
67 #include "message-stack.h"
68 #include "selection.h"
69 #include "select-context.h"
70 #include "sp-namedview.h"
71 #include "color.h"
72 #include "sp-item-group.h"
73 #include "preferences.h"
74 #include "object-hierarchy.h"
75 #include "helper/units.h"
76 #include "display/canvas-arena.h"
77 #include "display/nr-arena.h"
78 #include "display/gnome-canvas-acetate.h"
79 #include "display/sodipodi-ctrlrect.h"
80 #include "display/sp-canvas-util.h"
81 #include "display/canvas-temporary-item-list.h"
82 #include "display/snap-indicator.h"
83 #include "ui/dialog/dialog-manager.h"
84 #include "xml/repr.h"
85 #include "message-context.h"
86 #include "device-manager.h"
87 #include "layer-fns.h"
88 #include "layer-manager.h"
89 #include "event-log.h"
90 #include "display/canvas-grid.h"
91 #include "widgets/desktop-widget.h"
92 #include "box3d-context.h"
94 // TODO those includes are only for node tool quick zoom. Remove them after fixing it.
95 #include "ui/tool/node-tool.h"
96 #include "ui/tool/control-point-selection.h"
98 #include "display/sp-canvas.h"
100 namespace Inkscape { namespace XML { class Node; }}
102 // Callback declarations
103 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
104 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
105 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
106 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
107 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
108 static void _reconstruction_start(SPDesktop * desktop);
109 static void _reconstruction_finish(SPDesktop * desktop);
110 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
112 /**
113 * Return new desktop object.
114 * \pre namedview != NULL.
115 * \pre canvas != NULL.
116 */
117 SPDesktop::SPDesktop() :
118 _dlg_mgr( 0 ),
119 namedview( 0 ),
120 canvas( 0 ),
121 selection( 0 ),
122 event_context( 0 ),
123 layer_manager( 0 ),
124 event_log( 0 ),
125 temporary_item_list( 0 ),
126 snapindicator( 0 ),
127 acetate( 0 ),
128 main( 0 ),
129 gridgroup( 0 ),
130 guides( 0 ),
131 drawing( 0 ),
132 sketch( 0 ),
133 controls( 0 ),
134 tempgroup ( 0 ),
135 table( 0 ),
136 page( 0 ),
137 page_border( 0 ),
138 current( 0 ),
139 _focusMode(false),
140 zooms_past( 0 ),
141 zooms_future( 0 ),
142 dkey( 0 ),
143 number( 0 ),
144 window_state(0),
145 interaction_disabled_counter( 0 ),
146 waiting_cursor( false ),
147 guides_active( false ),
148 gr_item( 0 ),
149 gr_point_type( 0 ),
150 gr_point_i( 0 ),
151 gr_fill_or_stroke( true ),
152 _layer_hierarchy( 0 ),
153 _reconstruction_old_layer_id( 0 ),
154 _display_mode(Inkscape::RENDERMODE_NORMAL),
155 _widget( 0 ),
156 _inkscape( 0 ),
157 _guides_message_context( 0 ),
158 _active( false ),
159 _w2d(),
160 _d2w(),
161 _doc2dt( Geom::Scale(1, -1) ),
162 grids_visible( false )
163 {
164 _d2w.setIdentity();
165 _w2d.setIdentity();
167 selection = Inkscape::GC::release( new Inkscape::Selection(this) );
168 }
170 void
171 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWidgetInterface *widget)
172 {
173 _widget = widget;
175 // Temporary workaround for link order issues:
176 Inkscape::DeviceManager::getManager().getDevices();
177 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
179 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
181 current = prefs->getStyle("/desktop/style");
183 namedview = nv;
184 canvas = aCanvas;
186 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
187 /* Kill flicker */
188 sp_document_ensure_up_to_date (document);
190 /* Setup Dialog Manager */
191 _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
193 dkey = sp_item_display_key_new (1);
195 /* Connect document */
196 setDocument (document);
198 number = namedview->getViewCount();
201 /* Setup Canvas */
202 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
204 SPCanvasGroup *root = sp_canvas_root (canvas);
206 /* Setup adminstrative layers */
207 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
208 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
209 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
210 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
212 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
213 SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
214 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
215 sp_canvas_item_move_to_z (table, 0);
217 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
218 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
219 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
221 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
222 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
224 SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
226 if (prefs->getBool("/options/startmode/outline")) {
227 // Start in outline mode
228 setDisplayModeOutline();
229 } else {
230 // Start in normal mode, default
231 setDisplayModeNormal();
232 }
234 // The order in which these canvas items are added determines the z-order. It's therefore
235 // important to add the tempgroup (which will contain the snapindicator) before adding the
236 // controls. Only this way one will be able to quickly (before the snap indicator has
237 // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
238 // will not work (the snap indicator is on top of the node handler; is the snapindicator
239 // being selected? or does it intercept some of the events that should have gone to the
240 // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
241 gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
242 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
243 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
244 tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
245 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
247 /* Push select tool to the bottom of stack */
248 /** \todo
249 * FIXME: this is the only call to this. Everything else seems to just
250 * call "set" instead of "push". Can we assume that there is only one
251 * context ever?
252 */
253 push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
255 // display rect and zoom are now handled in sp_desktop_widget_realize()
257 Geom::Rect const d(Geom::Point(0.0, 0.0),
258 Geom::Point(sp_document_width(document), sp_document_height(document)));
260 SP_CTRLRECT(page)->setRectangle(d);
261 SP_CTRLRECT(page_border)->setRectangle(d);
263 /* the following sets the page shadow on the canvas
264 It was originally set to 5, which is really cheesy!
265 It now is an attribute in the document's namedview. If a value of
266 0 is used, then the constructor for a shadow is not initialized.
267 */
269 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
270 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
271 }
274 /* Connect event for page resize */
275 _doc2dt[5] = sp_document_height (document);
276 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
278 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
280 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
281 SP_CANVAS_ARENA (drawing)->arena,
282 dkey,
283 SP_ITEM_SHOW_DISPLAY);
284 if (ai) {
285 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
286 }
288 namedview->show(this);
289 /* Ugly hack */
290 activate_guides (true);
291 /* Ugly hack */
292 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
294 /* Set up notification of rebuilding the document, this allows
295 for saving object related settings in the document. */
296 _reconstruction_start_connection =
297 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
298 _reconstruction_finish_connection =
299 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
300 _reconstruction_old_layer_id = NULL;
302 // ?
303 // sp_active_desktop_set (desktop);
304 _inkscape = INKSCAPE;
306 _activate_connection = _activate_signal.connect(
307 sigc::bind(
308 sigc::ptr_fun(_onActivate),
309 this
310 )
311 );
312 _deactivate_connection = _deactivate_signal.connect(
313 sigc::bind(
314 sigc::ptr_fun(_onDeactivate),
315 this
316 )
317 );
319 _sel_modified_connection = selection->connectModified(
320 sigc::bind(
321 sigc::ptr_fun(&_onSelectionModified),
322 this
323 )
324 );
325 _sel_changed_connection = selection->connectChanged(
326 sigc::bind(
327 sigc::ptr_fun(&_onSelectionChanged),
328 this
329 )
330 );
333 /* setup LayerManager */
334 // (Setting up after the connections are all in place, as it may use some of them)
335 layer_manager = new Inkscape::LayerManager( this );
337 showGrids(namedview->grids_visible, false);
339 temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
340 snapindicator = new Inkscape::Display::SnapIndicator ( this );
341 }
344 void SPDesktop::destroy()
345 {
346 if (snapindicator) {
347 delete snapindicator;
348 snapindicator = NULL;
349 }
350 if (temporary_item_list) {
351 delete temporary_item_list;
352 temporary_item_list = NULL;
353 }
355 if (selection) {
356 delete selection;
357 selection = NULL;
358 }
360 namedview->hide(this);
362 _activate_connection.disconnect();
363 _deactivate_connection.disconnect();
364 _sel_modified_connection.disconnect();
365 _sel_changed_connection.disconnect();
366 _modified_connection.disconnect();
367 _commit_connection.disconnect();
368 _reconstruction_start_connection.disconnect();
369 _reconstruction_finish_connection.disconnect();
371 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
372 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
373 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
375 while (event_context) {
376 SPEventContext *ec = event_context;
377 event_context = ec->next;
378 sp_event_context_finish (ec);
379 g_object_unref (G_OBJECT (ec));
380 }
382 if (_layer_hierarchy) {
383 delete _layer_hierarchy;
384 // _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
385 }
387 if (layer_manager) {
388 delete layer_manager;
389 layer_manager = NULL;
390 }
392 if (_inkscape) {
393 _inkscape = NULL;
394 }
396 if (drawing) {
397 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
398 drawing = NULL;
399 }
401 delete _guides_message_context;
402 _guides_message_context = NULL;
404 g_list_free (zooms_past);
405 g_list_free (zooms_future);
406 }
408 SPDesktop::~SPDesktop() {}
410 //--------------------------------------------------------------------
411 /* Public methods */
414 /* These methods help for temporarily showing things on-canvas.
415 * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
416 * is when you want to prematurely remove the item from the canvas, by calling
417 * desktop->remove_temporary_canvasitem(tempitem).
418 */
419 /** Note that lifetime is measured in milliseconds
420 * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
421 * delete the object for you and the reference will become invalid without you knowing it.
422 * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
423 * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
424 * because the object might be deleted already without you knowing it.
425 * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
426 */
427 Inkscape::Display::TemporaryItem *
428 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
429 {
430 if (move_to_bottom) {
431 sp_canvas_item_move_to_z(item, 0);
432 }
434 return temporary_item_list->add_item(item, lifetime);
435 }
437 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
438 */
439 void
440 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
441 {
442 // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
443 if (tempitem && temporary_item_list) {
444 temporary_item_list->delete_item(tempitem);
445 }
446 }
448 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
449 SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
450 canvas->rendermode = mode;
451 _display_mode = mode;
452 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
453 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
454 }
456 void SPDesktop::displayModeToggle() {
457 switch (_display_mode) {
458 case Inkscape::RENDERMODE_NORMAL:
459 _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
460 break;
461 case Inkscape::RENDERMODE_NO_FILTERS:
462 _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
463 break;
464 case Inkscape::RENDERMODE_OUTLINE:
465 default:
466 _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
467 }
468 }
470 /**
471 * Returns current root (=bottom) layer.
472 */
473 SPObject *SPDesktop::currentRoot() const
474 {
475 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
476 }
478 /**
479 * Returns current top layer.
480 */
481 SPObject *SPDesktop::currentLayer() const
482 {
483 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
484 }
486 /**
487 * Sets the current layer of the desktop.
488 *
489 * Make \a object the top layer.
490 */
491 void SPDesktop::setCurrentLayer(SPObject *object) {
492 g_return_if_fail(SP_IS_GROUP(object));
493 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
494 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
495 _layer_hierarchy->setBottom(object);
496 }
498 void SPDesktop::toggleLayerSolo(SPObject *object) {
499 g_return_if_fail(SP_IS_GROUP(object));
500 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
502 bool othersShowing = false;
503 std::vector<SPObject*> layers;
504 for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
505 layers.push_back(obj);
506 othersShowing |= !SP_ITEM(obj)->isHidden();
507 }
508 for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
509 layers.push_back(obj);
510 othersShowing |= !SP_ITEM(obj)->isHidden();
511 }
514 if ( SP_ITEM(object)->isHidden() ) {
515 SP_ITEM(object)->setHidden(false);
516 }
518 for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
519 SP_ITEM(*it)->setHidden(othersShowing);
520 }
521 }
523 /**
524 * Return layer that contains \a object.
525 */
526 SPObject *SPDesktop::layerForObject(SPObject *object) {
527 g_return_val_if_fail(object != NULL, NULL);
529 SPObject *root=currentRoot();
530 object = SP_OBJECT_PARENT(object);
531 while ( object && object != root && !isLayer(object) ) {
532 object = SP_OBJECT_PARENT(object);
533 }
534 return object;
535 }
537 /**
538 * True if object is a layer.
539 */
540 bool SPDesktop::isLayer(SPObject *object) const {
541 return ( SP_IS_GROUP(object)
542 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
543 == SPGroup::LAYER ) );
544 }
546 /**
547 * True if desktop viewport fully contains \a item's bbox.
548 */
549 bool SPDesktop::isWithinViewport (SPItem *item) const
550 {
551 Geom::Rect const viewport = get_display_area();
552 Geom::OptRect const bbox = sp_item_bbox_desktop(item);
553 if (bbox) {
554 return viewport.contains(*bbox);
555 } else {
556 return true;
557 }
558 }
560 ///
561 bool SPDesktop::itemIsHidden(SPItem const *item) const {
562 return item->isHidden(this->dkey);
563 }
565 /**
566 * Set activate property of desktop; emit signal if changed.
567 */
568 void
569 SPDesktop::set_active (bool new_active)
570 {
571 if (new_active != _active) {
572 _active = new_active;
573 if (new_active) {
574 _activate_signal.emit();
575 } else {
576 _deactivate_signal.emit();
577 }
578 }
579 }
581 /**
582 * Set activate status of current desktop's named view.
583 */
584 void
585 SPDesktop::activate_guides(bool activate)
586 {
587 guides_active = activate;
588 namedview->activateGuides(this, activate);
589 }
591 /**
592 * Make desktop switch documents.
593 */
594 void
595 SPDesktop::change_document (SPDocument *theDocument)
596 {
597 g_return_if_fail (theDocument != NULL);
599 /* unselect everything before switching documents */
600 selection->clear();
602 setDocument (theDocument);
604 /* update the rulers, connect the desktop widget's signal to the new namedview etc.
605 (this can probably be done in a better way) */
606 Gtk::Window *parent = this->getToplevel();
607 g_assert(parent != NULL);
608 SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
609 if (dtw) dtw->desktop = this;
610 sp_desktop_widget_update_namedview(dtw);
612 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
613 _document_replaced_signal.emit (this, theDocument);
614 }
616 /**
617 * Make desktop switch event contexts.
618 */
619 void
620 SPDesktop::set_event_context (GtkType type, const gchar *config)
621 {
622 SPEventContext *ec;
623 while (event_context) {
624 ec = event_context;
625 sp_event_context_deactivate (ec);
626 // we have to keep event_context valid during destruction - otherwise writing
627 // destructors is next to impossible
628 SPEventContext *next = ec->next;
629 sp_event_context_finish (ec);
630 g_object_unref (G_OBJECT (ec));
631 event_context = next;
632 }
634 ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
635 ec->next = event_context;
636 event_context = ec;
637 sp_event_context_activate (ec);
638 _event_context_changed_signal.emit (this, ec);
639 }
641 /**
642 * Push event context onto desktop's context stack.
643 */
644 void
645 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
646 {
647 SPEventContext *ref, *ec;
649 if (event_context && event_context->key == key) return;
650 ref = event_context;
651 while (ref && ref->next && ref->next->key != key) ref = ref->next;
652 if (ref && ref->next) {
653 ec = ref->next;
654 ref->next = ec->next;
655 sp_event_context_finish (ec);
656 g_object_unref (G_OBJECT (ec));
657 }
659 if (event_context) sp_event_context_deactivate (event_context);
660 ec = sp_event_context_new (type, this, config, key);
661 ec->next = event_context;
662 event_context = ec;
663 sp_event_context_activate (ec);
664 _event_context_changed_signal.emit (this, ec);
665 }
667 /**
668 * Sets the coordinate status to a given point
669 */
670 void
671 SPDesktop::set_coordinate_status (Geom::Point p) {
672 _widget->setCoordinateStatus(p);
673 }
675 /**
676 * \see sp_document_item_from_list_at_point_bottom()
677 */
678 SPItem *
679 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
680 {
681 g_return_val_if_fail (doc() != NULL, NULL);
682 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
683 }
685 /**
686 * \see sp_document_item_at_point()
687 */
688 SPItem *
689 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
690 {
691 g_return_val_if_fail (doc() != NULL, NULL);
692 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
693 }
695 /**
696 * \see sp_document_group_at_point()
697 */
698 SPItem *
699 SPDesktop::group_at_point (Geom::Point const p) const
700 {
701 g_return_val_if_fail (doc() != NULL, NULL);
702 return sp_document_group_at_point (doc(), dkey, p);
703 }
705 /**
706 * \brief Returns the mouse point in document coordinates; if mouse is
707 * outside the canvas, returns the center of canvas viewpoint
708 */
709 Geom::Point
710 SPDesktop::point() const
711 {
712 Geom::Point p = _widget->getPointer();
713 Geom::Point pw = sp_canvas_window_to_world (canvas, p);
714 p = w2d(pw);
716 Geom::Rect const r = canvas->getViewbox();
718 Geom::Point r0 = w2d(r.min());
719 Geom::Point r1 = w2d(r.max());
721 if (p[Geom::X] >= r0[Geom::X] &&
722 p[Geom::X] <= r1[Geom::X] &&
723 p[Geom::Y] >= r1[Geom::Y] &&
724 p[Geom::Y] <= r0[Geom::Y])
725 {
726 return p;
727 } else {
728 return (r0 + r1) / 2;
729 }
730 }
732 /**
733 * Put current zoom data in history list.
734 */
735 void
736 SPDesktop::push_current_zoom (GList **history)
737 {
738 Geom::Rect const area = get_display_area();
740 NRRect *old_zoom = g_new(NRRect, 1);
741 old_zoom->x0 = area.min()[Geom::X];
742 old_zoom->x1 = area.max()[Geom::X];
743 old_zoom->y0 = area.min()[Geom::Y];
744 old_zoom->y1 = area.max()[Geom::Y];
745 if ( *history == NULL
746 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
747 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
748 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
749 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
750 {
751 *history = g_list_prepend (*history, old_zoom);
752 }
753 }
755 /**
756 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
757 */
758 void
759 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
760 {
761 g_assert(_widget);
763 // save the zoom
764 if (log) {
765 push_current_zoom(&zooms_past);
766 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
767 g_list_free (zooms_future);
768 zooms_future = NULL;
769 }
771 double const cx = 0.5 * (x0 + x1);
772 double const cy = 0.5 * (y0 + y1);
774 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
775 Geom::Rect viewbox = canvas->getViewbox();
776 viewbox.expandBy(-border);
778 double scale = _d2w.descrim();
779 double newscale;
780 if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
781 newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
782 } else {
783 newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
784 }
786 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
788 int clear = FALSE;
789 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
790 // zoom changed - set new zoom factors
791 _d2w = Geom::Scale(newscale, -newscale);
792 _w2d = Geom::Scale(1/newscale, 1/-newscale);
793 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
794 clear = TRUE;
795 signal_zoom_changed.emit(_d2w.descrim());
796 }
798 /* Calculate top left corner (in document pixels) */
799 x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
800 y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
802 /* Scroll */
803 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
805 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
806 sp_box3d_context_update_lines(event_context);
808 _widget->updateRulers();
809 _widget->updateScrollbars(_d2w.descrim());
810 _widget->updateZoom();
811 }
813 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
814 {
815 set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
816 }
818 /**
819 * Return viewbox dimensions.
820 */
821 Geom::Rect SPDesktop::get_display_area() const
822 {
823 Geom::Rect const viewbox = canvas->getViewbox();
825 double const scale = _d2w[0];
827 return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
828 Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
829 }
831 /**
832 * Revert back to previous zoom if possible.
833 */
834 void
835 SPDesktop::prev_zoom()
836 {
837 if (zooms_past == NULL) {
838 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
839 return;
840 }
842 // push current zoom into forward zooms list
843 push_current_zoom (&zooms_future);
845 // restore previous zoom
846 set_display_area (((NRRect *) zooms_past->data)->x0,
847 ((NRRect *) zooms_past->data)->y0,
848 ((NRRect *) zooms_past->data)->x1,
849 ((NRRect *) zooms_past->data)->y1,
850 0, false);
852 // remove the just-added zoom from the past zooms list
853 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
854 }
856 /**
857 * Set zoom to next in list.
858 */
859 void
860 SPDesktop::next_zoom()
861 {
862 if (zooms_future == NULL) {
863 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
864 return;
865 }
867 // push current zoom into past zooms list
868 push_current_zoom (&zooms_past);
870 // restore next zoom
871 set_display_area (((NRRect *) zooms_future->data)->x0,
872 ((NRRect *) zooms_future->data)->y0,
873 ((NRRect *) zooms_future->data)->x1,
874 ((NRRect *) zooms_future->data)->y1,
875 0, false);
877 // remove the just-used zoom from the zooms_future list
878 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
879 }
881 /** \brief Performs a quick zoom into what the user is working on
882 \param enable Whether we're going in or out of quick zoom
884 */
885 void
886 SPDesktop::zoom_quick (bool enable)
887 {
888 if (enable == _quick_zoom_enabled) {
889 return;
890 }
892 if (enable == true) {
893 _quick_zoom_stored_area = get_display_area();
894 bool zoomed = false;
896 // TODO This needs to migrate into the node tool, but currently the design
897 // of this method is sufficiently wrong to prevent this.
898 if (!zoomed && INK_IS_NODE_TOOL(event_context)) {
899 InkNodeTool *nt = static_cast<InkNodeTool*>(event_context);
900 if (!nt->_selected_nodes->empty()) {
901 Geom::Rect nodes = *nt->_selected_nodes->bounds();
902 double area = nodes.area();
903 // do not zoom if a single cusp node is selected aand the bounds
904 // have zero area.
905 if (!Geom::are_near(area, 0) && area * 2.0 < _quick_zoom_stored_area.area()) {
906 set_display_area(nodes, true);
907 zoomed = true;
908 }
909 }
910 }
912 if (!zoomed) {
913 Geom::OptRect const d = selection->bounds();
914 if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
915 set_display_area(*d, true);
916 zoomed = true;
917 }
918 }
920 if (!zoomed) {
921 zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
922 zoomed = true;
923 }
924 } else {
925 set_display_area(_quick_zoom_stored_area, false);
926 }
928 _quick_zoom_enabled = enable;
929 return;
930 }
932 /**
933 * Zoom to point with absolute zoom factor.
934 */
935 void
936 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
937 {
938 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
940 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
941 // this check prevents "sliding" when trying to zoom in at maximum zoom;
942 /// \todo someone please fix calculations properly and remove this hack
943 if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
944 return;
946 Geom::Rect const viewbox = canvas->getViewbox();
948 double const width2 = viewbox.dimensions()[Geom::X] / zoom;
949 double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
951 set_display_area(cx - px * width2,
952 cy - py * height2,
953 cx + (1 - px) * width2,
954 cy + (1 - py) * height2,
955 0.0);
956 }
958 /**
959 * Zoom to center with absolute zoom factor.
960 */
961 void
962 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
963 {
964 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
965 }
967 /**
968 * Zoom to point with relative zoom factor.
969 */
970 void
971 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
972 {
973 Geom::Rect const area = get_display_area();
975 if (cx < area.min()[Geom::X]) {
976 cx = area.min()[Geom::X];
977 }
978 if (cx > area.max()[Geom::X]) {
979 cx = area.max()[Geom::X];
980 }
981 if (cy < area.min()[Geom::Y]) {
982 cy = area.min()[Geom::Y];
983 }
984 if (cy > area.max()[Geom::Y]) {
985 cy = area.max()[Geom::Y];
986 }
988 gdouble const scale = _d2w.descrim() * zoom;
989 double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
990 double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
992 zoom_absolute_keep_point(cx, cy, px, py, scale);
993 }
995 /**
996 * Zoom to center with relative zoom factor.
997 */
998 void
999 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1000 {
1001 gdouble scale = _d2w.descrim() * zoom;
1002 zoom_absolute (cx, cy, scale);
1003 }
1005 /**
1006 * Set display area to origin and current document dimensions.
1007 */
1008 void
1009 SPDesktop::zoom_page()
1010 {
1011 Geom::Rect d(Geom::Point(0, 0),
1012 Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1014 if (d.minExtent() < 1.0) {
1015 return;
1016 }
1018 set_display_area(d, 10);
1019 }
1021 /**
1022 * Set display area to current document width.
1023 */
1024 void
1025 SPDesktop::zoom_page_width()
1026 {
1027 Geom::Rect const a = get_display_area();
1029 if (sp_document_width(doc()) < 1.0) {
1030 return;
1031 }
1033 Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1034 Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1036 set_display_area(d, 10);
1037 }
1039 /**
1040 * Zoom to selection.
1041 */
1042 void
1043 SPDesktop::zoom_selection()
1044 {
1045 Geom::OptRect const d = selection->bounds();
1047 if ( !d || d->minExtent() < 0.1 ) {
1048 return;
1049 }
1051 set_display_area(*d, 10);
1052 }
1054 /**
1055 * Tell widget to let zoom widget grab keyboard focus.
1056 */
1057 void
1058 SPDesktop::zoom_grab_focus()
1059 {
1060 _widget->letZoomGrabFocus();
1061 }
1063 /**
1064 * Zoom to whole drawing.
1065 */
1066 void
1067 SPDesktop::zoom_drawing()
1068 {
1069 g_return_if_fail (doc() != NULL);
1070 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1071 g_return_if_fail (docitem != NULL);
1073 Geom::OptRect d = sp_item_bbox_desktop(docitem);
1075 /* Note that the second condition here indicates that
1076 ** there are no items in the drawing.
1077 */
1078 if ( !d || d->minExtent() < 0.1 ) {
1079 return;
1080 }
1082 set_display_area(*d, 10);
1083 }
1085 /**
1086 * Scroll canvas by specific coordinate amount in svg coordinates.
1087 */
1088 void
1089 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1090 {
1091 double scale = _d2w.descrim();
1092 scroll_world(dx*scale, dy*scale, is_scrolling);
1093 }
1095 /**
1096 * Scroll canvas by specific coordinate amount.
1097 */
1098 void
1099 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1100 {
1101 g_assert(_widget);
1103 Geom::Rect const viewbox = canvas->getViewbox();
1105 sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1107 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1108 sp_box3d_context_update_lines(event_context);
1110 _widget->updateRulers();
1111 _widget->updateScrollbars(_d2w.descrim());
1112 }
1114 bool
1115 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1116 {
1117 using Geom::X;
1118 using Geom::Y;
1120 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1121 gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1123 // autoscrolldistance is in screen pixels, but the display area is in document units
1124 autoscrolldistance /= _d2w.descrim();
1125 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1126 Geom::Rect dbox = get_display_area();
1127 dbox.expandBy(-autoscrolldistance);
1129 if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1130 !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y]) ) {
1132 Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1134 gdouble x_to;
1135 if (p[X] < dbox.min()[X])
1136 x_to = dbox.min()[X];
1137 else if (p[X] > dbox.max()[X])
1138 x_to = dbox.max()[X];
1139 else
1140 x_to = p[X];
1142 gdouble y_to;
1143 if (p[Y] < dbox.min()[Y])
1144 y_to = dbox.min()[Y];
1145 else if (p[Y] > dbox.max()[Y])
1146 y_to = dbox.max()[Y];
1147 else
1148 y_to = p[Y];
1150 Geom::Point const d_dt(x_to, y_to);
1151 Geom::Point const d_w( d_dt * _d2w );
1152 Geom::Point const moved_w( d_w - s_w );
1154 if (autoscrollspeed == 0)
1155 autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1157 if (autoscrollspeed != 0)
1158 scroll_world (autoscrollspeed * moved_w);
1160 return true;
1161 }
1162 return false;
1163 }
1165 bool
1166 SPDesktop::is_iconified()
1167 {
1168 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1169 }
1171 void
1172 SPDesktop::iconify()
1173 {
1174 _widget->setIconified();
1175 }
1177 bool
1178 SPDesktop::is_maximized()
1179 {
1180 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1181 }
1183 void
1184 SPDesktop::maximize()
1185 {
1186 _widget->setMaximized();
1187 }
1189 bool
1190 SPDesktop::is_fullscreen()
1191 {
1192 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1193 }
1195 void
1196 SPDesktop::fullscreen()
1197 {
1198 _widget->setFullscreen();
1199 }
1201 /** \brief Checks to see if the user is working in focused mode
1203 Returns the value of \c _focusMode
1204 */
1205 bool
1206 SPDesktop::is_focusMode()
1207 {
1208 return _focusMode;
1209 }
1211 /** \brief Changes whether the user is in focus mode or not
1212 \param mode Which mode the view should be in
1214 */
1215 void
1216 SPDesktop::focusMode (bool mode)
1217 {
1218 if (mode == _focusMode) { return; }
1220 _focusMode = mode;
1222 layoutWidget();
1223 //sp_desktop_widget_layout(SPDesktopWidget);
1225 return;
1226 }
1228 void
1229 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1230 {
1231 _widget->getGeometry (x, y, w, h);
1232 }
1234 void
1235 SPDesktop::setWindowPosition (Geom::Point p)
1236 {
1237 _widget->setPosition (p);
1238 }
1240 void
1241 SPDesktop::setWindowSize (gint w, gint h)
1242 {
1243 _widget->setSize (w, h);
1244 }
1246 void
1247 SPDesktop::setWindowTransient (void *p, int transient_policy)
1248 {
1249 _widget->setTransient (p, transient_policy);
1250 }
1252 Gtk::Window*
1253 SPDesktop::getToplevel( )
1254 {
1255 return _widget->getWindow();
1256 }
1258 void
1259 SPDesktop::presentWindow()
1260 {
1261 _widget->present();
1262 }
1264 bool
1265 SPDesktop::warnDialog (gchar *text)
1266 {
1267 return _widget->warnDialog (text);
1268 }
1270 void
1271 SPDesktop::toggleRulers()
1272 {
1273 _widget->toggleRulers();
1274 }
1276 void
1277 SPDesktop::toggleScrollbars()
1278 {
1279 _widget->toggleScrollbars();
1280 }
1282 void
1283 SPDesktop::layoutWidget()
1284 {
1285 _widget->layout();
1286 }
1288 void
1289 SPDesktop::destroyWidget()
1290 {
1291 _widget->destroy();
1292 }
1294 bool
1295 SPDesktop::shutdown()
1296 {
1297 return _widget->shutdown();
1298 }
1300 bool SPDesktop::onDeleteUI (GdkEventAny*)
1301 {
1302 if(shutdown())
1303 return true;
1305 destroyWidget();
1306 return false;
1307 }
1309 /**
1310 * onWindowStateEvent
1311 *
1312 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1313 * Since GTK doesn't have a way to query this state information directly, we
1314 * record it for the desktop here, and also possibly trigger a layout.
1315 */
1316 bool
1317 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1318 {
1319 // Record the desktop window's state
1320 window_state = event->new_window_state;
1322 // Layout may differ depending on full-screen mode or not
1323 GdkWindowState changed = event->changed_mask;
1324 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1325 layoutWidget();
1326 }
1328 return false;
1329 }
1331 void
1332 SPDesktop::setToolboxFocusTo (gchar const *label)
1333 {
1334 _widget->setToolboxFocusTo (label);
1335 }
1337 void
1338 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1339 {
1340 _widget->setToolboxAdjustmentValue (id, val);
1341 }
1343 void
1344 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1345 {
1346 _widget->setToolboxSelectOneValue (id, val);
1347 }
1349 bool
1350 SPDesktop::isToolboxButtonActive (gchar const *id)
1351 {
1352 return _widget->isToolboxButtonActive (id);
1353 }
1355 void
1356 SPDesktop::emitToolSubselectionChanged(gpointer data)
1357 {
1358 _tool_subselection_changed.emit(data);
1359 inkscape_subselection_changed (this);
1360 }
1362 void
1363 SPDesktop::updateNow()
1364 {
1365 sp_canvas_update_now(canvas);
1366 }
1368 void
1369 SPDesktop::enableInteraction()
1370 {
1371 _widget->enableInteraction();
1372 }
1374 void SPDesktop::disableInteraction()
1375 {
1376 _widget->disableInteraction();
1377 }
1379 void SPDesktop::setWaitingCursor()
1380 {
1381 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1382 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1383 gdk_cursor_unref(waiting);
1384 // GDK needs the flush for the cursor change to take effect
1385 gdk_flush();
1386 waiting_cursor = true;
1387 }
1389 void SPDesktop::clearWaitingCursor()
1390 {
1391 if (waiting_cursor)
1392 sp_event_context_update_cursor(sp_desktop_event_context(this));
1393 }
1395 void SPDesktop::toggleColorProfAdjust()
1396 {
1397 _widget->toggleColorProfAdjust();
1398 }
1400 void SPDesktop::toggleGrids()
1401 {
1402 if (namedview->grids) {
1403 if(gridgroup) {
1404 showGrids(!grids_visible);
1405 }
1406 } else {
1407 //there is no grid present at the moment. add a rectangular grid and make it visible
1408 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1409 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1410 showGrids(true);
1411 }
1412 }
1414 void SPDesktop::showGrids(bool show, bool dirty_document)
1415 {
1416 grids_visible = show;
1417 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1418 if (show) {
1419 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1420 } else {
1421 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1422 }
1423 }
1425 void SPDesktop::toggleSnapGlobal()
1426 {
1427 bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1428 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1429 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1430 }
1432 //----------------------------------------------------------------------
1433 // Callback implementations. The virtual ones are connected by the view.
1435 void
1436 SPDesktop::onPositionSet (double x, double y)
1437 {
1438 _widget->viewSetPosition (Geom::Point(x,y));
1439 }
1441 void
1442 SPDesktop::onResized (double /*x*/, double /*y*/)
1443 {
1444 // Nothing called here
1445 }
1447 /**
1448 * Redraw callback; queues Gtk redraw; connected by View.
1449 */
1450 void
1451 SPDesktop::onRedrawRequested ()
1452 {
1453 if (main) {
1454 _widget->requestCanvasUpdate();
1455 }
1456 }
1458 void
1459 SPDesktop::updateCanvasNow()
1460 {
1461 _widget->requestCanvasUpdateAndWait();
1462 }
1464 /**
1465 * Associate document with desktop.
1466 */
1467 void
1468 SPDesktop::setDocument (SPDocument *doc)
1469 {
1470 if (this->doc() && doc) {
1471 namedview->hide(this);
1472 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1473 }
1475 if (_layer_hierarchy) {
1476 _layer_hierarchy->clear();
1477 delete _layer_hierarchy;
1478 }
1479 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1480 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1481 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1482 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1483 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1485 /* setup EventLog */
1486 event_log = new Inkscape::EventLog(doc);
1487 doc->addUndoObserver(*event_log);
1489 _commit_connection.disconnect();
1490 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1492 /// \todo fixme: This condition exists to make sure the code
1493 /// inside is NOT called on initialization, only on replacement. But there
1494 /// are surely more safe methods to accomplish this.
1495 // TODO since the comment had reversed logic, check the intent of this block of code:
1496 if (drawing) {
1497 NRArenaItem *ai = 0;
1499 namedview = sp_document_namedview (doc, NULL);
1500 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1501 number = namedview->getViewCount();
1503 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1504 SP_CANVAS_ARENA (drawing)->arena,
1505 dkey,
1506 SP_ITEM_SHOW_DISPLAY);
1507 if (ai) {
1508 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1509 }
1510 namedview->show(this);
1511 /* Ugly hack */
1512 activate_guides (true);
1513 /* Ugly hack */
1514 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1515 }
1517 _document_replaced_signal.emit (this, doc);
1519 View::setDocument (doc);
1520 }
1522 void
1523 SPDesktop::onStatusMessage
1524 (Inkscape::MessageType type, gchar const *message)
1525 {
1526 if (_widget) {
1527 _widget->setMessage(type, message);
1528 }
1529 }
1531 void
1532 SPDesktop::onDocumentURISet (gchar const* uri)
1533 {
1534 _widget->setTitle(uri);
1535 }
1537 /**
1538 * Resized callback.
1539 */
1540 void
1541 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1542 {
1543 _doc2dt[5] = height;
1544 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1545 Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1546 SP_CTRLRECT(page)->setRectangle(a);
1547 SP_CTRLRECT(page_border)->setRectangle(a);
1548 }
1551 void
1552 SPDesktop::_onActivate (SPDesktop* dt)
1553 {
1554 if (!dt->_widget) return;
1555 dt->_widget->activateDesktop();
1556 }
1558 void
1559 SPDesktop::_onDeactivate (SPDesktop* dt)
1560 {
1561 if (!dt->_widget) return;
1562 dt->_widget->deactivateDesktop();
1563 }
1565 void
1566 SPDesktop::_onSelectionModified
1567 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1568 {
1569 if (!dt->_widget) return;
1570 dt->_widget->updateScrollbars (dt->_d2w.descrim());
1571 }
1573 static void
1574 _onSelectionChanged
1575 (Inkscape::Selection *selection, SPDesktop *desktop)
1576 {
1577 /** \todo
1578 * only change the layer for single selections, or what?
1579 * This seems reasonable -- for multiple selections there can be many
1580 * different layers involved.
1581 */
1582 SPItem *item=selection->singleItem();
1583 if (item) {
1584 SPObject *layer=desktop->layerForObject(item);
1585 if ( layer && layer != desktop->currentLayer() ) {
1586 desktop->setCurrentLayer(layer);
1587 }
1588 }
1589 }
1591 /**
1592 * Calls event handler of current event context.
1593 * \param arena Unused
1594 * \todo fixme
1595 */
1596 static gint
1597 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1598 {
1599 if (ai) {
1600 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1601 return sp_event_context_item_handler (desktop->event_context, spi, event);
1602 } else {
1603 return sp_event_context_root_handler (desktop->event_context, event);
1604 }
1605 }
1607 static void
1608 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1609 g_return_if_fail(SP_IS_GROUP(layer));
1610 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1611 }
1613 /// Callback
1614 static void
1615 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1616 g_return_if_fail(SP_IS_GROUP(layer));
1617 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1618 }
1620 /// Callback
1621 static void
1622 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1623 SPDesktop *desktop)
1624 {
1625 desktop->_layer_changed_signal.emit (bottom);
1626 }
1628 /// Called when document is starting to be rebuilt.
1629 static void
1630 _reconstruction_start (SPDesktop * desktop)
1631 {
1632 // printf("Desktop, starting reconstruction\n");
1633 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1634 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1636 /*
1637 GSList const * selection_objs = desktop->selection->list();
1638 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1640 }
1641 */
1642 desktop->selection->clear();
1644 // printf("Desktop, starting reconstruction end\n");
1645 }
1647 /// Called when document rebuild is finished.
1648 static void
1649 _reconstruction_finish (SPDesktop * desktop)
1650 {
1651 // printf("Desktop, finishing reconstruction\n");
1652 if (desktop->_reconstruction_old_layer_id == NULL)
1653 return;
1655 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1656 if (newLayer != NULL)
1657 desktop->setCurrentLayer(newLayer);
1659 g_free(desktop->_reconstruction_old_layer_id);
1660 desktop->_reconstruction_old_layer_id = NULL;
1661 // printf("Desktop, finishing reconstruction end\n");
1662 return;
1663 }
1665 /**
1666 * Namedview_modified callback.
1667 */
1668 static void
1669 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1670 {
1671 SPNamedView *nv=SP_NAMEDVIEW(obj);
1673 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1675 /* Show/hide page background */
1676 if (nv->pagecolor & 0xff) {
1677 sp_canvas_item_show (desktop->table);
1678 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1679 sp_canvas_item_move_to_z (desktop->table, 0);
1680 } else {
1681 sp_canvas_item_hide (desktop->table);
1682 }
1684 /* Show/hide page border */
1685 if (nv->showborder) {
1686 // show
1687 sp_canvas_item_show (desktop->page_border);
1688 // set color and shadow
1689 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1690 if (nv->pageshadow) {
1691 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1692 }
1693 // place in the z-order stack
1694 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1695 sp_canvas_item_move_to_z (desktop->page_border, 2);
1696 } else {
1697 int order = sp_canvas_item_order (desktop->page_border);
1698 int morder = sp_canvas_item_order (desktop->drawing);
1699 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1700 morder - order);
1701 }
1702 } else {
1703 sp_canvas_item_hide (desktop->page_border);
1704 if (nv->pageshadow) {
1705 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1706 }
1707 }
1709 /* Show/hide page shadow */
1710 if (nv->showpageshadow && nv->pageshadow) {
1711 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1712 } else {
1713 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1714 }
1716 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1717 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1718 (SP_RGBA32_R_U(nv->pagecolor) +
1719 SP_RGBA32_G_U(nv->pagecolor) +
1720 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1721 // the background color is light or transparent, use black outline
1722 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1723 } else { // use white outline
1724 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1725 }
1726 }
1727 }
1729 Geom::Matrix SPDesktop::w2d() const
1730 {
1731 return _w2d;
1732 }
1734 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1735 {
1736 return p * _w2d;
1737 }
1739 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1740 {
1741 return p * _d2w;
1742 }
1744 Geom::Matrix SPDesktop::doc2dt() const
1745 {
1746 return _doc2dt;
1747 }
1749 Geom::Matrix SPDesktop::dt2doc() const
1750 {
1751 // doc2dt is its own inverse
1752 return _doc2dt;
1753 }
1755 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1756 {
1757 return p * _doc2dt;
1758 }
1760 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1761 {
1762 return p * dt2doc();
1763 }
1766 /*
1767 * Pop event context from desktop's context stack. Never used.
1768 */
1769 // void
1770 // SPDesktop::pop_event_context (unsigned int key)
1771 // {
1772 // SPEventContext *ec = NULL;
1773 //
1774 // if (event_context && event_context->key == key) {
1775 // g_return_if_fail (event_context);
1776 // g_return_if_fail (event_context->next);
1777 // ec = event_context;
1778 // sp_event_context_deactivate (ec);
1779 // event_context = ec->next;
1780 // sp_event_context_activate (event_context);
1781 // _event_context_changed_signal.emit (this, ec);
1782 // }
1783 //
1784 // SPEventContext *ref = event_context;
1785 // while (ref && ref->next && ref->next->key != key)
1786 // ref = ref->next;
1787 //
1788 // if (ref && ref->next) {
1789 // ec = ref->next;
1790 // ref->next = ec->next;
1791 // }
1792 //
1793 // if (ec) {
1794 // sp_event_context_finish (ec);
1795 // g_object_unref (G_OBJECT (ec));
1796 // }
1797 // }
1799 /*
1800 Local Variables:
1801 mode:c++
1802 c-file-style:"stroustrup"
1803 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1804 indent-tabs-mode:nil
1805 fill-column:99
1806 End:
1807 */
1808 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :