69aec65e09dae14b8b5aa181d6d3a7de3d853c54
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 _setDisplayMode(Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW);
466 break;
467 case Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW:
468 default:
469 _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
470 }
471 }
473 /**
474 * Returns current root (=bottom) layer.
475 */
476 SPObject *SPDesktop::currentRoot() const
477 {
478 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
479 }
481 /**
482 * Returns current top layer.
483 */
484 SPObject *SPDesktop::currentLayer() const
485 {
486 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
487 }
489 /**
490 * Sets the current layer of the desktop.
491 *
492 * Make \a object the top layer.
493 */
494 void SPDesktop::setCurrentLayer(SPObject *object) {
495 g_return_if_fail(SP_IS_GROUP(object));
496 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
497 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
498 _layer_hierarchy->setBottom(object);
499 }
501 void SPDesktop::toggleLayerSolo(SPObject *object) {
502 g_return_if_fail(SP_IS_GROUP(object));
503 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
505 bool othersShowing = false;
506 std::vector<SPObject*> layers;
507 for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
508 layers.push_back(obj);
509 othersShowing |= !SP_ITEM(obj)->isHidden();
510 }
511 for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
512 layers.push_back(obj);
513 othersShowing |= !SP_ITEM(obj)->isHidden();
514 }
517 if ( SP_ITEM(object)->isHidden() ) {
518 SP_ITEM(object)->setHidden(false);
519 }
521 for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
522 SP_ITEM(*it)->setHidden(othersShowing);
523 }
524 }
526 /**
527 * Return layer that contains \a object.
528 */
529 SPObject *SPDesktop::layerForObject(SPObject *object) {
530 g_return_val_if_fail(object != NULL, NULL);
532 SPObject *root=currentRoot();
533 object = SP_OBJECT_PARENT(object);
534 while ( object && object != root && !isLayer(object) ) {
535 object = SP_OBJECT_PARENT(object);
536 }
537 return object;
538 }
540 /**
541 * True if object is a layer.
542 */
543 bool SPDesktop::isLayer(SPObject *object) const {
544 return ( SP_IS_GROUP(object)
545 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
546 == SPGroup::LAYER ) );
547 }
549 /**
550 * True if desktop viewport fully contains \a item's bbox.
551 */
552 bool SPDesktop::isWithinViewport (SPItem *item) const
553 {
554 Geom::Rect const viewport = get_display_area();
555 Geom::OptRect const bbox = sp_item_bbox_desktop(item);
556 if (bbox) {
557 return viewport.contains(*bbox);
558 } else {
559 return true;
560 }
561 }
563 ///
564 bool SPDesktop::itemIsHidden(SPItem const *item) const {
565 return item->isHidden(this->dkey);
566 }
568 /**
569 * Set activate property of desktop; emit signal if changed.
570 */
571 void
572 SPDesktop::set_active (bool new_active)
573 {
574 if (new_active != _active) {
575 _active = new_active;
576 if (new_active) {
577 _activate_signal.emit();
578 } else {
579 _deactivate_signal.emit();
580 }
581 }
582 }
584 /**
585 * Set activate status of current desktop's named view.
586 */
587 void
588 SPDesktop::activate_guides(bool activate)
589 {
590 guides_active = activate;
591 namedview->activateGuides(this, activate);
592 }
594 /**
595 * Make desktop switch documents.
596 */
597 void
598 SPDesktop::change_document (SPDocument *theDocument)
599 {
600 g_return_if_fail (theDocument != NULL);
602 /* unselect everything before switching documents */
603 selection->clear();
605 setDocument (theDocument);
607 /* update the rulers, connect the desktop widget's signal to the new namedview etc.
608 (this can probably be done in a better way) */
609 Gtk::Window *parent = this->getToplevel();
610 g_assert(parent != NULL);
611 SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
612 if (dtw) {
613 dtw->desktop = this;
614 }
615 dtw->updateNamedview();
617 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
618 _document_replaced_signal.emit (this, theDocument);
619 }
621 /**
622 * Make desktop switch event contexts.
623 */
624 void
625 SPDesktop::set_event_context (GtkType type, const gchar *config)
626 {
627 SPEventContext *ec;
628 while (event_context) {
629 ec = event_context;
630 sp_event_context_deactivate (ec);
631 // we have to keep event_context valid during destruction - otherwise writing
632 // destructors is next to impossible
633 SPEventContext *next = ec->next;
634 sp_event_context_finish (ec);
635 g_object_unref (G_OBJECT (ec));
636 event_context = next;
637 }
639 ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
640 ec->next = event_context;
641 event_context = ec;
642 sp_event_context_activate (ec);
643 _event_context_changed_signal.emit (this, ec);
644 }
646 /**
647 * Push event context onto desktop's context stack.
648 */
649 void
650 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
651 {
652 SPEventContext *ref, *ec;
654 if (event_context && event_context->key == key) return;
655 ref = event_context;
656 while (ref && ref->next && ref->next->key != key) ref = ref->next;
657 if (ref && ref->next) {
658 ec = ref->next;
659 ref->next = ec->next;
660 sp_event_context_finish (ec);
661 g_object_unref (G_OBJECT (ec));
662 }
664 if (event_context) sp_event_context_deactivate (event_context);
665 ec = sp_event_context_new (type, this, config, key);
666 ec->next = event_context;
667 event_context = ec;
668 sp_event_context_activate (ec);
669 _event_context_changed_signal.emit (this, ec);
670 }
672 /**
673 * Sets the coordinate status to a given point
674 */
675 void
676 SPDesktop::set_coordinate_status (Geom::Point p) {
677 _widget->setCoordinateStatus(p);
678 }
680 /**
681 * \see sp_document_item_from_list_at_point_bottom()
682 */
683 SPItem *
684 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
685 {
686 g_return_val_if_fail (doc() != NULL, NULL);
687 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
688 }
690 /**
691 * \see sp_document_item_at_point()
692 */
693 SPItem *
694 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
695 {
696 g_return_val_if_fail (doc() != NULL, NULL);
697 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
698 }
700 /**
701 * \see sp_document_group_at_point()
702 */
703 SPItem *
704 SPDesktop::group_at_point (Geom::Point const p) const
705 {
706 g_return_val_if_fail (doc() != NULL, NULL);
707 return sp_document_group_at_point (doc(), dkey, p);
708 }
710 /**
711 * \brief Returns the mouse point in document coordinates; if mouse is
712 * outside the canvas, returns the center of canvas viewpoint
713 */
714 Geom::Point
715 SPDesktop::point() const
716 {
717 Geom::Point p = _widget->getPointer();
718 Geom::Point pw = sp_canvas_window_to_world (canvas, p);
719 p = w2d(pw);
721 Geom::Rect const r = canvas->getViewbox();
723 Geom::Point r0 = w2d(r.min());
724 Geom::Point r1 = w2d(r.max());
726 if (p[Geom::X] >= r0[Geom::X] &&
727 p[Geom::X] <= r1[Geom::X] &&
728 p[Geom::Y] >= r1[Geom::Y] &&
729 p[Geom::Y] <= r0[Geom::Y])
730 {
731 return p;
732 } else {
733 return (r0 + r1) / 2;
734 }
735 }
737 /**
738 * Put current zoom data in history list.
739 */
740 void
741 SPDesktop::push_current_zoom (GList **history)
742 {
743 Geom::Rect const area = get_display_area();
745 NRRect *old_zoom = g_new(NRRect, 1);
746 old_zoom->x0 = area.min()[Geom::X];
747 old_zoom->x1 = area.max()[Geom::X];
748 old_zoom->y0 = area.min()[Geom::Y];
749 old_zoom->y1 = area.max()[Geom::Y];
750 if ( *history == NULL
751 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
752 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
753 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
754 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
755 {
756 *history = g_list_prepend (*history, old_zoom);
757 }
758 }
760 /**
761 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
762 */
763 void
764 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
765 {
766 g_assert(_widget);
768 // save the zoom
769 if (log) {
770 push_current_zoom(&zooms_past);
771 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
772 g_list_free (zooms_future);
773 zooms_future = NULL;
774 }
776 double const cx = 0.5 * (x0 + x1);
777 double const cy = 0.5 * (y0 + y1);
779 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
780 Geom::Rect viewbox = canvas->getViewbox();
781 viewbox.expandBy(-border);
783 double scale = _d2w.descrim();
784 double newscale;
785 if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
786 newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
787 } else {
788 newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
789 }
791 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
793 int clear = FALSE;
794 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
795 // zoom changed - set new zoom factors
796 _d2w = Geom::Scale(newscale, -newscale);
797 _w2d = Geom::Scale(1/newscale, 1/-newscale);
798 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
799 clear = TRUE;
800 signal_zoom_changed.emit(_d2w.descrim());
801 }
803 /* Calculate top left corner (in document pixels) */
804 x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
805 y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
807 /* Scroll */
808 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
810 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
811 sp_box3d_context_update_lines(event_context);
813 _widget->updateRulers();
814 _widget->updateScrollbars(_d2w.descrim());
815 _widget->updateZoom();
816 }
818 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
819 {
820 set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
821 }
823 /**
824 * Return viewbox dimensions.
825 */
826 Geom::Rect SPDesktop::get_display_area() const
827 {
828 Geom::Rect const viewbox = canvas->getViewbox();
830 double const scale = _d2w[0];
832 return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
833 Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
834 }
836 /**
837 * Revert back to previous zoom if possible.
838 */
839 void
840 SPDesktop::prev_zoom()
841 {
842 if (zooms_past == NULL) {
843 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
844 return;
845 }
847 // push current zoom into forward zooms list
848 push_current_zoom (&zooms_future);
850 // restore previous zoom
851 set_display_area (((NRRect *) zooms_past->data)->x0,
852 ((NRRect *) zooms_past->data)->y0,
853 ((NRRect *) zooms_past->data)->x1,
854 ((NRRect *) zooms_past->data)->y1,
855 0, false);
857 // remove the just-added zoom from the past zooms list
858 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
859 }
861 /**
862 * Set zoom to next in list.
863 */
864 void
865 SPDesktop::next_zoom()
866 {
867 if (zooms_future == NULL) {
868 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
869 return;
870 }
872 // push current zoom into past zooms list
873 push_current_zoom (&zooms_past);
875 // restore next zoom
876 set_display_area (((NRRect *) zooms_future->data)->x0,
877 ((NRRect *) zooms_future->data)->y0,
878 ((NRRect *) zooms_future->data)->x1,
879 ((NRRect *) zooms_future->data)->y1,
880 0, false);
882 // remove the just-used zoom from the zooms_future list
883 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
884 }
886 /** \brief Performs a quick zoom into what the user is working on
887 \param enable Whether we're going in or out of quick zoom
889 */
890 void
891 SPDesktop::zoom_quick (bool enable)
892 {
893 if (enable == _quick_zoom_enabled) {
894 return;
895 }
897 if (enable == true) {
898 _quick_zoom_stored_area = get_display_area();
899 bool zoomed = false;
901 // TODO This needs to migrate into the node tool, but currently the design
902 // of this method is sufficiently wrong to prevent this.
903 if (!zoomed && INK_IS_NODE_TOOL(event_context)) {
904 InkNodeTool *nt = static_cast<InkNodeTool*>(event_context);
905 if (!nt->_selected_nodes->empty()) {
906 Geom::Rect nodes = *nt->_selected_nodes->bounds();
907 double area = nodes.area();
908 // do not zoom if a single cusp node is selected aand the bounds
909 // have zero area.
910 if (!Geom::are_near(area, 0) && area * 2.0 < _quick_zoom_stored_area.area()) {
911 set_display_area(nodes, true);
912 zoomed = true;
913 }
914 }
915 }
917 if (!zoomed) {
918 Geom::OptRect const d = selection->bounds();
919 if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
920 set_display_area(*d, true);
921 zoomed = true;
922 }
923 }
925 if (!zoomed) {
926 zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
927 zoomed = true;
928 }
929 } else {
930 set_display_area(_quick_zoom_stored_area, false);
931 }
933 _quick_zoom_enabled = enable;
934 return;
935 }
937 /**
938 * Zoom to point with absolute zoom factor.
939 */
940 void
941 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
942 {
943 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
945 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
946 // this check prevents "sliding" when trying to zoom in at maximum zoom;
947 /// \todo someone please fix calculations properly and remove this hack
948 if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
949 return;
951 Geom::Rect const viewbox = canvas->getViewbox();
953 double const width2 = viewbox.dimensions()[Geom::X] / zoom;
954 double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
956 set_display_area(cx - px * width2,
957 cy - py * height2,
958 cx + (1 - px) * width2,
959 cy + (1 - py) * height2,
960 0.0);
961 }
963 /**
964 * Zoom to center with absolute zoom factor.
965 */
966 void
967 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
968 {
969 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
970 }
972 /**
973 * Zoom to point with relative zoom factor.
974 */
975 void
976 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
977 {
978 Geom::Rect const area = get_display_area();
980 if (cx < area.min()[Geom::X]) {
981 cx = area.min()[Geom::X];
982 }
983 if (cx > area.max()[Geom::X]) {
984 cx = area.max()[Geom::X];
985 }
986 if (cy < area.min()[Geom::Y]) {
987 cy = area.min()[Geom::Y];
988 }
989 if (cy > area.max()[Geom::Y]) {
990 cy = area.max()[Geom::Y];
991 }
993 gdouble const scale = _d2w.descrim() * zoom;
994 double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
995 double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
997 zoom_absolute_keep_point(cx, cy, px, py, scale);
998 }
1000 /**
1001 * Zoom to center with relative zoom factor.
1002 */
1003 void
1004 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1005 {
1006 gdouble scale = _d2w.descrim() * zoom;
1007 zoom_absolute (cx, cy, scale);
1008 }
1010 /**
1011 * Set display area to origin and current document dimensions.
1012 */
1013 void
1014 SPDesktop::zoom_page()
1015 {
1016 Geom::Rect d(Geom::Point(0, 0),
1017 Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1019 if (d.minExtent() < 1.0) {
1020 return;
1021 }
1023 set_display_area(d, 10);
1024 }
1026 /**
1027 * Set display area to current document width.
1028 */
1029 void
1030 SPDesktop::zoom_page_width()
1031 {
1032 Geom::Rect const a = get_display_area();
1034 if (sp_document_width(doc()) < 1.0) {
1035 return;
1036 }
1038 Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1039 Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1041 set_display_area(d, 10);
1042 }
1044 /**
1045 * Zoom to selection.
1046 */
1047 void
1048 SPDesktop::zoom_selection()
1049 {
1050 Geom::OptRect const d = selection->bounds();
1052 if ( !d || d->minExtent() < 0.1 ) {
1053 return;
1054 }
1056 set_display_area(*d, 10);
1057 }
1059 /**
1060 * Tell widget to let zoom widget grab keyboard focus.
1061 */
1062 void
1063 SPDesktop::zoom_grab_focus()
1064 {
1065 _widget->letZoomGrabFocus();
1066 }
1068 /**
1069 * Zoom to whole drawing.
1070 */
1071 void
1072 SPDesktop::zoom_drawing()
1073 {
1074 g_return_if_fail (doc() != NULL);
1075 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1076 g_return_if_fail (docitem != NULL);
1078 Geom::OptRect d = sp_item_bbox_desktop(docitem);
1080 /* Note that the second condition here indicates that
1081 ** there are no items in the drawing.
1082 */
1083 if ( !d || d->minExtent() < 0.1 ) {
1084 return;
1085 }
1087 set_display_area(*d, 10);
1088 }
1090 /**
1091 * Scroll canvas by specific coordinate amount in svg coordinates.
1092 */
1093 void
1094 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1095 {
1096 double scale = _d2w.descrim();
1097 scroll_world(dx*scale, dy*scale, is_scrolling);
1098 }
1100 /**
1101 * Scroll canvas by specific coordinate amount.
1102 */
1103 void
1104 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1105 {
1106 g_assert(_widget);
1108 Geom::Rect const viewbox = canvas->getViewbox();
1110 sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1112 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1113 sp_box3d_context_update_lines(event_context);
1115 _widget->updateRulers();
1116 _widget->updateScrollbars(_d2w.descrim());
1117 }
1119 bool
1120 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1121 {
1122 using Geom::X;
1123 using Geom::Y;
1125 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1126 gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1128 // autoscrolldistance is in screen pixels, but the display area is in document units
1129 autoscrolldistance /= _d2w.descrim();
1130 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1131 Geom::Rect dbox = get_display_area();
1132 dbox.expandBy(-autoscrolldistance);
1134 if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1135 !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y]) ) {
1137 Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1139 gdouble x_to;
1140 if (p[X] < dbox.min()[X])
1141 x_to = dbox.min()[X];
1142 else if (p[X] > dbox.max()[X])
1143 x_to = dbox.max()[X];
1144 else
1145 x_to = p[X];
1147 gdouble y_to;
1148 if (p[Y] < dbox.min()[Y])
1149 y_to = dbox.min()[Y];
1150 else if (p[Y] > dbox.max()[Y])
1151 y_to = dbox.max()[Y];
1152 else
1153 y_to = p[Y];
1155 Geom::Point const d_dt(x_to, y_to);
1156 Geom::Point const d_w( d_dt * _d2w );
1157 Geom::Point const moved_w( d_w - s_w );
1159 if (autoscrollspeed == 0)
1160 autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1162 if (autoscrollspeed != 0)
1163 scroll_world (autoscrollspeed * moved_w);
1165 return true;
1166 }
1167 return false;
1168 }
1170 bool
1171 SPDesktop::is_iconified()
1172 {
1173 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1174 }
1176 void
1177 SPDesktop::iconify()
1178 {
1179 _widget->setIconified();
1180 }
1182 bool
1183 SPDesktop::is_maximized()
1184 {
1185 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1186 }
1188 void
1189 SPDesktop::maximize()
1190 {
1191 _widget->setMaximized();
1192 }
1194 bool
1195 SPDesktop::is_fullscreen()
1196 {
1197 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1198 }
1200 void
1201 SPDesktop::fullscreen()
1202 {
1203 _widget->setFullscreen();
1204 }
1206 /** \brief Checks to see if the user is working in focused mode
1208 Returns the value of \c _focusMode
1209 */
1210 bool
1211 SPDesktop::is_focusMode()
1212 {
1213 return _focusMode;
1214 }
1216 /** \brief Changes whether the user is in focus mode or not
1217 \param mode Which mode the view should be in
1219 */
1220 void
1221 SPDesktop::focusMode (bool mode)
1222 {
1223 if (mode == _focusMode) { return; }
1225 _focusMode = mode;
1227 layoutWidget();
1228 //sp_desktop_widget_layout(SPDesktopWidget);
1230 return;
1231 }
1233 void
1234 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1235 {
1236 _widget->getGeometry (x, y, w, h);
1237 }
1239 void
1240 SPDesktop::setWindowPosition (Geom::Point p)
1241 {
1242 _widget->setPosition (p);
1243 }
1245 void
1246 SPDesktop::setWindowSize (gint w, gint h)
1247 {
1248 _widget->setSize (w, h);
1249 }
1251 void
1252 SPDesktop::setWindowTransient (void *p, int transient_policy)
1253 {
1254 _widget->setTransient (p, transient_policy);
1255 }
1257 Gtk::Window*
1258 SPDesktop::getToplevel( )
1259 {
1260 return _widget->getWindow();
1261 }
1263 void
1264 SPDesktop::presentWindow()
1265 {
1266 _widget->present();
1267 }
1269 bool
1270 SPDesktop::warnDialog (gchar *text)
1271 {
1272 return _widget->warnDialog (text);
1273 }
1275 void
1276 SPDesktop::toggleRulers()
1277 {
1278 _widget->toggleRulers();
1279 }
1281 void
1282 SPDesktop::toggleScrollbars()
1283 {
1284 _widget->toggleScrollbars();
1285 }
1287 void
1288 SPDesktop::layoutWidget()
1289 {
1290 _widget->layout();
1291 }
1293 void
1294 SPDesktop::destroyWidget()
1295 {
1296 _widget->destroy();
1297 }
1299 bool
1300 SPDesktop::shutdown()
1301 {
1302 return _widget->shutdown();
1303 }
1305 bool SPDesktop::onDeleteUI (GdkEventAny*)
1306 {
1307 if(shutdown())
1308 return true;
1310 destroyWidget();
1311 return false;
1312 }
1314 /**
1315 * onWindowStateEvent
1316 *
1317 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1318 * Since GTK doesn't have a way to query this state information directly, we
1319 * record it for the desktop here, and also possibly trigger a layout.
1320 */
1321 bool
1322 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1323 {
1324 // Record the desktop window's state
1325 window_state = event->new_window_state;
1327 // Layout may differ depending on full-screen mode or not
1328 GdkWindowState changed = event->changed_mask;
1329 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1330 layoutWidget();
1331 }
1333 return false;
1334 }
1336 void
1337 SPDesktop::setToolboxFocusTo (gchar const *label)
1338 {
1339 _widget->setToolboxFocusTo (label);
1340 }
1342 void
1343 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1344 {
1345 _widget->setToolboxAdjustmentValue (id, val);
1346 }
1348 void
1349 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1350 {
1351 _widget->setToolboxSelectOneValue (id, val);
1352 }
1354 bool
1355 SPDesktop::isToolboxButtonActive (gchar const *id)
1356 {
1357 return _widget->isToolboxButtonActive (id);
1358 }
1360 void
1361 SPDesktop::emitToolSubselectionChanged(gpointer data)
1362 {
1363 _tool_subselection_changed.emit(data);
1364 inkscape_subselection_changed (this);
1365 }
1367 void
1368 SPDesktop::updateNow()
1369 {
1370 sp_canvas_update_now(canvas);
1371 }
1373 void
1374 SPDesktop::enableInteraction()
1375 {
1376 _widget->enableInteraction();
1377 }
1379 void SPDesktop::disableInteraction()
1380 {
1381 _widget->disableInteraction();
1382 }
1384 void SPDesktop::setWaitingCursor()
1385 {
1386 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1387 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1388 gdk_cursor_unref(waiting);
1389 // GDK needs the flush for the cursor change to take effect
1390 gdk_flush();
1391 waiting_cursor = true;
1392 }
1394 void SPDesktop::clearWaitingCursor()
1395 {
1396 if (waiting_cursor)
1397 sp_event_context_update_cursor(sp_desktop_event_context(this));
1398 }
1400 void SPDesktop::toggleColorProfAdjust()
1401 {
1402 _widget->toggleColorProfAdjust();
1403 }
1405 void SPDesktop::toggleGrids()
1406 {
1407 if (namedview->grids) {
1408 if(gridgroup) {
1409 showGrids(!grids_visible);
1410 }
1411 } else {
1412 //there is no grid present at the moment. add a rectangular grid and make it visible
1413 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1414 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1415 showGrids(true);
1416 }
1417 }
1419 void SPDesktop::showGrids(bool show, bool dirty_document)
1420 {
1421 grids_visible = show;
1422 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1423 if (show) {
1424 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1425 } else {
1426 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1427 }
1428 }
1430 void SPDesktop::toggleSnapGlobal()
1431 {
1432 bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1433 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1434 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1435 }
1437 //----------------------------------------------------------------------
1438 // Callback implementations. The virtual ones are connected by the view.
1440 void
1441 SPDesktop::onPositionSet (double x, double y)
1442 {
1443 _widget->viewSetPosition (Geom::Point(x,y));
1444 }
1446 void
1447 SPDesktop::onResized (double /*x*/, double /*y*/)
1448 {
1449 // Nothing called here
1450 }
1452 /**
1453 * Redraw callback; queues Gtk redraw; connected by View.
1454 */
1455 void
1456 SPDesktop::onRedrawRequested ()
1457 {
1458 if (main) {
1459 _widget->requestCanvasUpdate();
1460 }
1461 }
1463 void
1464 SPDesktop::updateCanvasNow()
1465 {
1466 _widget->requestCanvasUpdateAndWait();
1467 }
1469 /**
1470 * Associate document with desktop.
1471 */
1472 void
1473 SPDesktop::setDocument (SPDocument *doc)
1474 {
1475 if (this->doc() && doc) {
1476 namedview->hide(this);
1477 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1478 }
1480 if (_layer_hierarchy) {
1481 _layer_hierarchy->clear();
1482 delete _layer_hierarchy;
1483 }
1484 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1485 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1486 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1487 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1488 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1490 /* setup EventLog */
1491 event_log = new Inkscape::EventLog(doc);
1492 doc->addUndoObserver(*event_log);
1494 _commit_connection.disconnect();
1495 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1497 /// \todo fixme: This condition exists to make sure the code
1498 /// inside is NOT called on initialization, only on replacement. But there
1499 /// are surely more safe methods to accomplish this.
1500 // TODO since the comment had reversed logic, check the intent of this block of code:
1501 if (drawing) {
1502 NRArenaItem *ai = 0;
1504 namedview = sp_document_namedview (doc, NULL);
1505 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1506 number = namedview->getViewCount();
1508 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1509 SP_CANVAS_ARENA (drawing)->arena,
1510 dkey,
1511 SP_ITEM_SHOW_DISPLAY);
1512 if (ai) {
1513 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1514 }
1515 namedview->show(this);
1516 /* Ugly hack */
1517 activate_guides (true);
1518 /* Ugly hack */
1519 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1520 }
1522 _document_replaced_signal.emit (this, doc);
1524 View::setDocument (doc);
1525 }
1527 void
1528 SPDesktop::onStatusMessage
1529 (Inkscape::MessageType type, gchar const *message)
1530 {
1531 if (_widget) {
1532 _widget->setMessage(type, message);
1533 }
1534 }
1536 void
1537 SPDesktop::onDocumentURISet (gchar const* uri)
1538 {
1539 _widget->setTitle(uri);
1540 }
1542 /**
1543 * Resized callback.
1544 */
1545 void
1546 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1547 {
1548 _doc2dt[5] = height;
1549 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1550 Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1551 SP_CTRLRECT(page)->setRectangle(a);
1552 SP_CTRLRECT(page_border)->setRectangle(a);
1553 }
1556 void
1557 SPDesktop::_onActivate (SPDesktop* dt)
1558 {
1559 if (!dt->_widget) return;
1560 dt->_widget->activateDesktop();
1561 }
1563 void
1564 SPDesktop::_onDeactivate (SPDesktop* dt)
1565 {
1566 if (!dt->_widget) return;
1567 dt->_widget->deactivateDesktop();
1568 }
1570 void
1571 SPDesktop::_onSelectionModified
1572 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1573 {
1574 if (!dt->_widget) return;
1575 dt->_widget->updateScrollbars (dt->_d2w.descrim());
1576 }
1578 static void
1579 _onSelectionChanged
1580 (Inkscape::Selection *selection, SPDesktop *desktop)
1581 {
1582 /** \todo
1583 * only change the layer for single selections, or what?
1584 * This seems reasonable -- for multiple selections there can be many
1585 * different layers involved.
1586 */
1587 SPItem *item=selection->singleItem();
1588 if (item) {
1589 SPObject *layer=desktop->layerForObject(item);
1590 if ( layer && layer != desktop->currentLayer() ) {
1591 desktop->setCurrentLayer(layer);
1592 }
1593 }
1594 }
1596 /**
1597 * Calls event handler of current event context.
1598 * \param arena Unused
1599 * \todo fixme
1600 */
1601 static gint
1602 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1603 {
1604 if (ai) {
1605 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1606 return sp_event_context_item_handler (desktop->event_context, spi, event);
1607 } else {
1608 return sp_event_context_root_handler (desktop->event_context, event);
1609 }
1610 }
1612 static void
1613 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1614 g_return_if_fail(SP_IS_GROUP(layer));
1615 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1616 }
1618 /// Callback
1619 static void
1620 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1621 g_return_if_fail(SP_IS_GROUP(layer));
1622 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1623 }
1625 /// Callback
1626 static void
1627 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1628 SPDesktop *desktop)
1629 {
1630 desktop->_layer_changed_signal.emit (bottom);
1631 }
1633 /// Called when document is starting to be rebuilt.
1634 static void
1635 _reconstruction_start (SPDesktop * desktop)
1636 {
1637 // printf("Desktop, starting reconstruction\n");
1638 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1639 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1641 /*
1642 GSList const * selection_objs = desktop->selection->list();
1643 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1645 }
1646 */
1647 desktop->selection->clear();
1649 // printf("Desktop, starting reconstruction end\n");
1650 }
1652 /// Called when document rebuild is finished.
1653 static void
1654 _reconstruction_finish (SPDesktop * desktop)
1655 {
1656 // printf("Desktop, finishing reconstruction\n");
1657 if (desktop->_reconstruction_old_layer_id == NULL)
1658 return;
1660 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1661 if (newLayer != NULL)
1662 desktop->setCurrentLayer(newLayer);
1664 g_free(desktop->_reconstruction_old_layer_id);
1665 desktop->_reconstruction_old_layer_id = NULL;
1666 // printf("Desktop, finishing reconstruction end\n");
1667 return;
1668 }
1670 /**
1671 * Namedview_modified callback.
1672 */
1673 static void
1674 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1675 {
1676 SPNamedView *nv=SP_NAMEDVIEW(obj);
1678 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1680 /* Show/hide page background */
1681 if (nv->pagecolor & 0xff) {
1682 sp_canvas_item_show (desktop->table);
1683 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1684 sp_canvas_item_move_to_z (desktop->table, 0);
1685 } else {
1686 sp_canvas_item_hide (desktop->table);
1687 }
1689 /* Show/hide page border */
1690 if (nv->showborder) {
1691 // show
1692 sp_canvas_item_show (desktop->page_border);
1693 // set color and shadow
1694 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1695 if (nv->pageshadow) {
1696 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1697 }
1698 // place in the z-order stack
1699 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1700 sp_canvas_item_move_to_z (desktop->page_border, 2);
1701 } else {
1702 int order = sp_canvas_item_order (desktop->page_border);
1703 int morder = sp_canvas_item_order (desktop->drawing);
1704 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1705 morder - order);
1706 }
1707 } else {
1708 sp_canvas_item_hide (desktop->page_border);
1709 if (nv->pageshadow) {
1710 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1711 }
1712 }
1714 /* Show/hide page shadow */
1715 if (nv->showpageshadow && nv->pageshadow) {
1716 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1717 } else {
1718 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1719 }
1721 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1722 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1723 (SP_RGBA32_R_U(nv->pagecolor) +
1724 SP_RGBA32_G_U(nv->pagecolor) +
1725 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1726 // the background color is light or transparent, use black outline
1727 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1728 } else { // use white outline
1729 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1730 }
1731 }
1732 }
1734 Geom::Matrix SPDesktop::w2d() const
1735 {
1736 return _w2d;
1737 }
1739 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1740 {
1741 return p * _w2d;
1742 }
1744 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1745 {
1746 return p * _d2w;
1747 }
1749 Geom::Matrix SPDesktop::doc2dt() const
1750 {
1751 return _doc2dt;
1752 }
1754 Geom::Matrix SPDesktop::dt2doc() const
1755 {
1756 // doc2dt is its own inverse
1757 return _doc2dt;
1758 }
1760 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1761 {
1762 return p * _doc2dt;
1763 }
1765 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1766 {
1767 return p * dt2doc();
1768 }
1771 /*
1772 * Pop event context from desktop's context stack. Never used.
1773 */
1774 // void
1775 // SPDesktop::pop_event_context (unsigned int key)
1776 // {
1777 // SPEventContext *ec = NULL;
1778 //
1779 // if (event_context && event_context->key == key) {
1780 // g_return_if_fail (event_context);
1781 // g_return_if_fail (event_context->next);
1782 // ec = event_context;
1783 // sp_event_context_deactivate (ec);
1784 // event_context = ec->next;
1785 // sp_event_context_activate (event_context);
1786 // _event_context_changed_signal.emit (this, ec);
1787 // }
1788 //
1789 // SPEventContext *ref = event_context;
1790 // while (ref && ref->next && ref->next->key != key)
1791 // ref = ref->next;
1792 //
1793 // if (ref && ref->next) {
1794 // ec = ref->next;
1795 // ref->next = ec->next;
1796 // }
1797 //
1798 // if (ec) {
1799 // sp_event_context_finish (ec);
1800 // g_object_unref (G_OBJECT (ec));
1801 // }
1802 // }
1804 /*
1805 Local Variables:
1806 mode:c++
1807 c-file-style:"stroustrup"
1808 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1809 indent-tabs-mode:nil
1810 fill-column:99
1811 End:
1812 */
1813 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :