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_NORMAL);
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 // The event_context will be null. This means that it will be impossible
640 // to process any event invoked by the lines below. See for example bug
641 // LP #622350. Cutting and undoing again in the node tool resets the event
642 // context to the node tool. In this bug the line bellow invokes GDK_LEAVE_NOTIFY
643 // events which cannot be handled and must be discarded.
644 ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
645 ec->next = event_context;
646 event_context = ec;
647 // Now the event_context has been set again and we can process all events again
648 sp_event_context_activate (ec);
649 _event_context_changed_signal.emit (this, ec);
650 }
652 /**
653 * Push event context onto desktop's context stack.
654 */
655 void
656 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
657 {
658 SPEventContext *ref, *ec;
660 if (event_context && event_context->key == key) return;
661 ref = event_context;
662 while (ref && ref->next && ref->next->key != key) ref = ref->next;
663 if (ref && ref->next) {
664 ec = ref->next;
665 ref->next = ec->next;
666 sp_event_context_finish (ec);
667 g_object_unref (G_OBJECT (ec));
668 }
670 if (event_context) sp_event_context_deactivate (event_context);
671 ec = sp_event_context_new (type, this, config, key);
672 ec->next = event_context;
673 event_context = ec;
674 sp_event_context_activate (ec);
675 _event_context_changed_signal.emit (this, ec);
676 }
678 /**
679 * Sets the coordinate status to a given point
680 */
681 void
682 SPDesktop::set_coordinate_status (Geom::Point p) {
683 _widget->setCoordinateStatus(p);
684 }
686 /**
687 * \see sp_document_item_from_list_at_point_bottom()
688 */
689 SPItem *
690 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
691 {
692 g_return_val_if_fail (doc() != NULL, NULL);
693 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
694 }
696 /**
697 * \see sp_document_item_at_point()
698 */
699 SPItem *
700 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
701 {
702 g_return_val_if_fail (doc() != NULL, NULL);
703 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
704 }
706 /**
707 * \see sp_document_group_at_point()
708 */
709 SPItem *
710 SPDesktop::group_at_point (Geom::Point const p) const
711 {
712 g_return_val_if_fail (doc() != NULL, NULL);
713 return sp_document_group_at_point (doc(), dkey, p);
714 }
716 /**
717 * \brief Returns the mouse point in document coordinates; if mouse is
718 * outside the canvas, returns the center of canvas viewpoint
719 */
720 Geom::Point
721 SPDesktop::point() const
722 {
723 Geom::Point p = _widget->getPointer();
724 Geom::Point pw = sp_canvas_window_to_world (canvas, p);
725 p = w2d(pw);
727 Geom::Rect const r = canvas->getViewbox();
729 Geom::Point r0 = w2d(r.min());
730 Geom::Point r1 = w2d(r.max());
732 if (p[Geom::X] >= r0[Geom::X] &&
733 p[Geom::X] <= r1[Geom::X] &&
734 p[Geom::Y] >= r1[Geom::Y] &&
735 p[Geom::Y] <= r0[Geom::Y])
736 {
737 return p;
738 } else {
739 return (r0 + r1) / 2;
740 }
741 }
743 /**
744 * Put current zoom data in history list.
745 */
746 void
747 SPDesktop::push_current_zoom (GList **history)
748 {
749 Geom::Rect const area = get_display_area();
751 NRRect *old_zoom = g_new(NRRect, 1);
752 old_zoom->x0 = area.min()[Geom::X];
753 old_zoom->x1 = area.max()[Geom::X];
754 old_zoom->y0 = area.min()[Geom::Y];
755 old_zoom->y1 = area.max()[Geom::Y];
756 if ( *history == NULL
757 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
758 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
759 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
760 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
761 {
762 *history = g_list_prepend (*history, old_zoom);
763 }
764 }
766 /**
767 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
768 */
769 void
770 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
771 {
772 g_assert(_widget);
774 // save the zoom
775 if (log) {
776 push_current_zoom(&zooms_past);
777 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
778 g_list_free (zooms_future);
779 zooms_future = NULL;
780 }
782 double const cx = 0.5 * (x0 + x1);
783 double const cy = 0.5 * (y0 + y1);
785 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
786 Geom::Rect viewbox = canvas->getViewbox();
787 viewbox.expandBy(-border);
789 double scale = _d2w.descrim();
790 double newscale;
791 if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
792 newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
793 } else {
794 newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
795 }
797 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
799 int clear = FALSE;
800 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
801 // zoom changed - set new zoom factors
802 _d2w = Geom::Scale(newscale, -newscale);
803 _w2d = Geom::Scale(1/newscale, 1/-newscale);
804 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
805 clear = TRUE;
806 signal_zoom_changed.emit(_d2w.descrim());
807 }
809 /* Calculate top left corner (in document pixels) */
810 x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
811 y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
813 /* Scroll */
814 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
816 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
817 sp_box3d_context_update_lines(event_context);
819 _widget->updateRulers();
820 _widget->updateScrollbars(_d2w.descrim());
821 _widget->updateZoom();
822 }
824 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
825 {
826 set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
827 }
829 /**
830 * Return viewbox dimensions.
831 */
832 Geom::Rect SPDesktop::get_display_area() const
833 {
834 Geom::Rect const viewbox = canvas->getViewbox();
836 double const scale = _d2w[0];
838 return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
839 Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
840 }
842 /**
843 * Revert back to previous zoom if possible.
844 */
845 void
846 SPDesktop::prev_zoom()
847 {
848 if (zooms_past == NULL) {
849 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
850 return;
851 }
853 // push current zoom into forward zooms list
854 push_current_zoom (&zooms_future);
856 // restore previous zoom
857 set_display_area (((NRRect *) zooms_past->data)->x0,
858 ((NRRect *) zooms_past->data)->y0,
859 ((NRRect *) zooms_past->data)->x1,
860 ((NRRect *) zooms_past->data)->y1,
861 0, false);
863 // remove the just-added zoom from the past zooms list
864 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
865 }
867 /**
868 * Set zoom to next in list.
869 */
870 void
871 SPDesktop::next_zoom()
872 {
873 if (zooms_future == NULL) {
874 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
875 return;
876 }
878 // push current zoom into past zooms list
879 push_current_zoom (&zooms_past);
881 // restore next zoom
882 set_display_area (((NRRect *) zooms_future->data)->x0,
883 ((NRRect *) zooms_future->data)->y0,
884 ((NRRect *) zooms_future->data)->x1,
885 ((NRRect *) zooms_future->data)->y1,
886 0, false);
888 // remove the just-used zoom from the zooms_future list
889 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
890 }
892 /** \brief Performs a quick zoom into what the user is working on
893 \param enable Whether we're going in or out of quick zoom
895 */
896 void
897 SPDesktop::zoom_quick (bool enable)
898 {
899 if (enable == _quick_zoom_enabled) {
900 return;
901 }
903 if (enable == true) {
904 _quick_zoom_stored_area = get_display_area();
905 bool zoomed = false;
907 // TODO This needs to migrate into the node tool, but currently the design
908 // of this method is sufficiently wrong to prevent this.
909 if (!zoomed && INK_IS_NODE_TOOL(event_context)) {
910 InkNodeTool *nt = static_cast<InkNodeTool*>(event_context);
911 if (!nt->_selected_nodes->empty()) {
912 Geom::Rect nodes = *nt->_selected_nodes->bounds();
913 double area = nodes.area();
914 // do not zoom if a single cusp node is selected aand the bounds
915 // have zero area.
916 if (!Geom::are_near(area, 0) && area * 2.0 < _quick_zoom_stored_area.area()) {
917 set_display_area(nodes, true);
918 zoomed = true;
919 }
920 }
921 }
923 if (!zoomed) {
924 Geom::OptRect const d = selection->bounds();
925 if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
926 set_display_area(*d, true);
927 zoomed = true;
928 }
929 }
931 if (!zoomed) {
932 zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
933 zoomed = true;
934 }
935 } else {
936 set_display_area(_quick_zoom_stored_area, false);
937 }
939 _quick_zoom_enabled = enable;
940 return;
941 }
943 /**
944 * Zoom to point with absolute zoom factor.
945 */
946 void
947 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
948 {
949 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
951 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
952 // this check prevents "sliding" when trying to zoom in at maximum zoom;
953 /// \todo someone please fix calculations properly and remove this hack
954 if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
955 return;
957 Geom::Rect const viewbox = canvas->getViewbox();
959 double const width2 = viewbox.dimensions()[Geom::X] / zoom;
960 double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
962 set_display_area(cx - px * width2,
963 cy - py * height2,
964 cx + (1 - px) * width2,
965 cy + (1 - py) * height2,
966 0.0);
967 }
969 /**
970 * Zoom to center with absolute zoom factor.
971 */
972 void
973 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
974 {
975 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
976 }
978 /**
979 * Zoom to point with relative zoom factor.
980 */
981 void
982 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
983 {
984 Geom::Rect const area = get_display_area();
986 if (cx < area.min()[Geom::X]) {
987 cx = area.min()[Geom::X];
988 }
989 if (cx > area.max()[Geom::X]) {
990 cx = area.max()[Geom::X];
991 }
992 if (cy < area.min()[Geom::Y]) {
993 cy = area.min()[Geom::Y];
994 }
995 if (cy > area.max()[Geom::Y]) {
996 cy = area.max()[Geom::Y];
997 }
999 gdouble const scale = _d2w.descrim() * zoom;
1000 double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1001 double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
1003 zoom_absolute_keep_point(cx, cy, px, py, scale);
1004 }
1006 /**
1007 * Zoom to center with relative zoom factor.
1008 */
1009 void
1010 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1011 {
1012 gdouble scale = _d2w.descrim() * zoom;
1013 zoom_absolute (cx, cy, scale);
1014 }
1016 /**
1017 * Set display area to origin and current document dimensions.
1018 */
1019 void
1020 SPDesktop::zoom_page()
1021 {
1022 Geom::Rect d(Geom::Point(0, 0),
1023 Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1025 if (d.minExtent() < 1.0) {
1026 return;
1027 }
1029 set_display_area(d, 10);
1030 }
1032 /**
1033 * Set display area to current document width.
1034 */
1035 void
1036 SPDesktop::zoom_page_width()
1037 {
1038 Geom::Rect const a = get_display_area();
1040 if (sp_document_width(doc()) < 1.0) {
1041 return;
1042 }
1044 Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1045 Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1047 set_display_area(d, 10);
1048 }
1050 /**
1051 * Zoom to selection.
1052 */
1053 void
1054 SPDesktop::zoom_selection()
1055 {
1056 Geom::OptRect const d = selection->bounds();
1058 if ( !d || d->minExtent() < 0.1 ) {
1059 return;
1060 }
1062 set_display_area(*d, 10);
1063 }
1065 /**
1066 * Tell widget to let zoom widget grab keyboard focus.
1067 */
1068 void
1069 SPDesktop::zoom_grab_focus()
1070 {
1071 _widget->letZoomGrabFocus();
1072 }
1074 /**
1075 * Zoom to whole drawing.
1076 */
1077 void
1078 SPDesktop::zoom_drawing()
1079 {
1080 g_return_if_fail (doc() != NULL);
1081 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1082 g_return_if_fail (docitem != NULL);
1084 Geom::OptRect d = sp_item_bbox_desktop(docitem);
1086 /* Note that the second condition here indicates that
1087 ** there are no items in the drawing.
1088 */
1089 if ( !d || d->minExtent() < 0.1 ) {
1090 return;
1091 }
1093 set_display_area(*d, 10);
1094 }
1096 /**
1097 * Scroll canvas by specific coordinate amount in svg coordinates.
1098 */
1099 void
1100 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1101 {
1102 double scale = _d2w.descrim();
1103 scroll_world(dx*scale, dy*scale, is_scrolling);
1104 }
1106 /**
1107 * Scroll canvas by specific coordinate amount.
1108 */
1109 void
1110 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1111 {
1112 g_assert(_widget);
1114 Geom::Rect const viewbox = canvas->getViewbox();
1116 sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1118 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1119 sp_box3d_context_update_lines(event_context);
1121 _widget->updateRulers();
1122 _widget->updateScrollbars(_d2w.descrim());
1123 }
1125 bool
1126 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1127 {
1128 using Geom::X;
1129 using Geom::Y;
1131 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1132 gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1134 // autoscrolldistance is in screen pixels, but the display area is in document units
1135 autoscrolldistance /= _d2w.descrim();
1136 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1137 Geom::Rect dbox = get_display_area();
1138 dbox.expandBy(-autoscrolldistance);
1140 if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1141 !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y]) ) {
1143 Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1145 gdouble x_to;
1146 if (p[X] < dbox.min()[X])
1147 x_to = dbox.min()[X];
1148 else if (p[X] > dbox.max()[X])
1149 x_to = dbox.max()[X];
1150 else
1151 x_to = p[X];
1153 gdouble y_to;
1154 if (p[Y] < dbox.min()[Y])
1155 y_to = dbox.min()[Y];
1156 else if (p[Y] > dbox.max()[Y])
1157 y_to = dbox.max()[Y];
1158 else
1159 y_to = p[Y];
1161 Geom::Point const d_dt(x_to, y_to);
1162 Geom::Point const d_w( d_dt * _d2w );
1163 Geom::Point const moved_w( d_w - s_w );
1165 if (autoscrollspeed == 0)
1166 autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1168 if (autoscrollspeed != 0)
1169 scroll_world (autoscrollspeed * moved_w);
1171 return true;
1172 }
1173 return false;
1174 }
1176 bool
1177 SPDesktop::is_iconified()
1178 {
1179 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1180 }
1182 void
1183 SPDesktop::iconify()
1184 {
1185 _widget->setIconified();
1186 }
1188 bool
1189 SPDesktop::is_maximized()
1190 {
1191 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1192 }
1194 void
1195 SPDesktop::maximize()
1196 {
1197 _widget->setMaximized();
1198 }
1200 bool
1201 SPDesktop::is_fullscreen()
1202 {
1203 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1204 }
1206 void
1207 SPDesktop::fullscreen()
1208 {
1209 _widget->setFullscreen();
1210 }
1212 /** \brief Checks to see if the user is working in focused mode
1214 Returns the value of \c _focusMode
1215 */
1216 bool
1217 SPDesktop::is_focusMode()
1218 {
1219 return _focusMode;
1220 }
1222 /** \brief Changes whether the user is in focus mode or not
1223 \param mode Which mode the view should be in
1225 */
1226 void
1227 SPDesktop::focusMode (bool mode)
1228 {
1229 if (mode == _focusMode) { return; }
1231 _focusMode = mode;
1233 layoutWidget();
1234 //sp_desktop_widget_layout(SPDesktopWidget);
1236 return;
1237 }
1239 void
1240 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1241 {
1242 _widget->getGeometry (x, y, w, h);
1243 }
1245 void
1246 SPDesktop::setWindowPosition (Geom::Point p)
1247 {
1248 _widget->setPosition (p);
1249 }
1251 void
1252 SPDesktop::setWindowSize (gint w, gint h)
1253 {
1254 _widget->setSize (w, h);
1255 }
1257 void
1258 SPDesktop::setWindowTransient (void *p, int transient_policy)
1259 {
1260 _widget->setTransient (p, transient_policy);
1261 }
1263 Gtk::Window*
1264 SPDesktop::getToplevel( )
1265 {
1266 return _widget->getWindow();
1267 }
1269 void
1270 SPDesktop::presentWindow()
1271 {
1272 _widget->present();
1273 }
1275 bool
1276 SPDesktop::warnDialog (gchar *text)
1277 {
1278 return _widget->warnDialog (text);
1279 }
1281 void
1282 SPDesktop::toggleRulers()
1283 {
1284 _widget->toggleRulers();
1285 }
1287 void
1288 SPDesktop::toggleScrollbars()
1289 {
1290 _widget->toggleScrollbars();
1291 }
1293 void
1294 SPDesktop::layoutWidget()
1295 {
1296 _widget->layout();
1297 }
1299 void
1300 SPDesktop::destroyWidget()
1301 {
1302 _widget->destroy();
1303 }
1305 bool
1306 SPDesktop::shutdown()
1307 {
1308 return _widget->shutdown();
1309 }
1311 bool SPDesktop::onDeleteUI (GdkEventAny*)
1312 {
1313 if(shutdown())
1314 return true;
1316 destroyWidget();
1317 return false;
1318 }
1320 /**
1321 * onWindowStateEvent
1322 *
1323 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1324 * Since GTK doesn't have a way to query this state information directly, we
1325 * record it for the desktop here, and also possibly trigger a layout.
1326 */
1327 bool
1328 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1329 {
1330 // Record the desktop window's state
1331 window_state = event->new_window_state;
1333 // Layout may differ depending on full-screen mode or not
1334 GdkWindowState changed = event->changed_mask;
1335 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1336 layoutWidget();
1337 }
1339 return false;
1340 }
1342 void
1343 SPDesktop::setToolboxFocusTo (gchar const *label)
1344 {
1345 _widget->setToolboxFocusTo (label);
1346 }
1348 void
1349 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1350 {
1351 _widget->setToolboxAdjustmentValue (id, val);
1352 }
1354 void
1355 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1356 {
1357 _widget->setToolboxSelectOneValue (id, val);
1358 }
1360 bool
1361 SPDesktop::isToolboxButtonActive (gchar const *id)
1362 {
1363 return _widget->isToolboxButtonActive (id);
1364 }
1366 void
1367 SPDesktop::emitToolSubselectionChanged(gpointer data)
1368 {
1369 _tool_subselection_changed.emit(data);
1370 inkscape_subselection_changed (this);
1371 }
1373 void
1374 SPDesktop::updateNow()
1375 {
1376 sp_canvas_update_now(canvas);
1377 }
1379 void
1380 SPDesktop::enableInteraction()
1381 {
1382 _widget->enableInteraction();
1383 }
1385 void SPDesktop::disableInteraction()
1386 {
1387 _widget->disableInteraction();
1388 }
1390 void SPDesktop::setWaitingCursor()
1391 {
1392 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1393 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1394 gdk_cursor_unref(waiting);
1395 // GDK needs the flush for the cursor change to take effect
1396 gdk_flush();
1397 waiting_cursor = true;
1398 }
1400 void SPDesktop::clearWaitingCursor()
1401 {
1402 if (waiting_cursor)
1403 sp_event_context_update_cursor(sp_desktop_event_context(this));
1404 }
1406 void SPDesktop::toggleColorProfAdjust()
1407 {
1408 _widget->toggleColorProfAdjust();
1409 }
1411 void SPDesktop::toggleGrids()
1412 {
1413 if (namedview->grids) {
1414 if(gridgroup) {
1415 showGrids(!grids_visible);
1416 }
1417 } else {
1418 //there is no grid present at the moment. add a rectangular grid and make it visible
1419 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1420 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1421 showGrids(true);
1422 }
1423 }
1425 void SPDesktop::showGrids(bool show, bool dirty_document)
1426 {
1427 grids_visible = show;
1428 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1429 if (show) {
1430 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1431 } else {
1432 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1433 }
1434 }
1436 void SPDesktop::toggleSnapGlobal()
1437 {
1438 bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1439 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1440 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1441 }
1443 //----------------------------------------------------------------------
1444 // Callback implementations. The virtual ones are connected by the view.
1446 void
1447 SPDesktop::onPositionSet (double x, double y)
1448 {
1449 _widget->viewSetPosition (Geom::Point(x,y));
1450 }
1452 void
1453 SPDesktop::onResized (double /*x*/, double /*y*/)
1454 {
1455 // Nothing called here
1456 }
1458 /**
1459 * Redraw callback; queues Gtk redraw; connected by View.
1460 */
1461 void
1462 SPDesktop::onRedrawRequested ()
1463 {
1464 if (main) {
1465 _widget->requestCanvasUpdate();
1466 }
1467 }
1469 void
1470 SPDesktop::updateCanvasNow()
1471 {
1472 _widget->requestCanvasUpdateAndWait();
1473 }
1475 /**
1476 * Associate document with desktop.
1477 */
1478 void
1479 SPDesktop::setDocument (SPDocument *doc)
1480 {
1481 if (this->doc() && doc) {
1482 namedview->hide(this);
1483 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1484 }
1486 if (_layer_hierarchy) {
1487 _layer_hierarchy->clear();
1488 delete _layer_hierarchy;
1489 }
1490 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1491 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1492 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1493 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1494 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1496 /* setup EventLog */
1497 event_log = new Inkscape::EventLog(doc);
1498 doc->addUndoObserver(*event_log);
1500 _commit_connection.disconnect();
1501 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1503 /// \todo fixme: This condition exists to make sure the code
1504 /// inside is NOT called on initialization, only on replacement. But there
1505 /// are surely more safe methods to accomplish this.
1506 // TODO since the comment had reversed logic, check the intent of this block of code:
1507 if (drawing) {
1508 NRArenaItem *ai = 0;
1510 namedview = sp_document_namedview (doc, NULL);
1511 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1512 number = namedview->getViewCount();
1514 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1515 SP_CANVAS_ARENA (drawing)->arena,
1516 dkey,
1517 SP_ITEM_SHOW_DISPLAY);
1518 if (ai) {
1519 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1520 }
1521 namedview->show(this);
1522 /* Ugly hack */
1523 activate_guides (true);
1524 /* Ugly hack */
1525 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1526 }
1528 _document_replaced_signal.emit (this, doc);
1530 View::setDocument (doc);
1531 }
1533 void
1534 SPDesktop::onStatusMessage
1535 (Inkscape::MessageType type, gchar const *message)
1536 {
1537 if (_widget) {
1538 _widget->setMessage(type, message);
1539 }
1540 }
1542 void
1543 SPDesktop::onDocumentURISet (gchar const* uri)
1544 {
1545 _widget->setTitle(uri);
1546 }
1548 /**
1549 * Resized callback.
1550 */
1551 void
1552 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1553 {
1554 _doc2dt[5] = height;
1555 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1556 Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1557 SP_CTRLRECT(page)->setRectangle(a);
1558 SP_CTRLRECT(page_border)->setRectangle(a);
1559 }
1562 void
1563 SPDesktop::_onActivate (SPDesktop* dt)
1564 {
1565 if (!dt->_widget) return;
1566 dt->_widget->activateDesktop();
1567 }
1569 void
1570 SPDesktop::_onDeactivate (SPDesktop* dt)
1571 {
1572 if (!dt->_widget) return;
1573 dt->_widget->deactivateDesktop();
1574 }
1576 void
1577 SPDesktop::_onSelectionModified
1578 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1579 {
1580 if (!dt->_widget) return;
1581 dt->_widget->updateScrollbars (dt->_d2w.descrim());
1582 }
1584 static void
1585 _onSelectionChanged
1586 (Inkscape::Selection *selection, SPDesktop *desktop)
1587 {
1588 /** \todo
1589 * only change the layer for single selections, or what?
1590 * This seems reasonable -- for multiple selections there can be many
1591 * different layers involved.
1592 */
1593 SPItem *item=selection->singleItem();
1594 if (item) {
1595 SPObject *layer=desktop->layerForObject(item);
1596 if ( layer && layer != desktop->currentLayer() ) {
1597 desktop->setCurrentLayer(layer);
1598 }
1599 }
1600 }
1602 /**
1603 * Calls event handler of current event context.
1604 * \param arena Unused
1605 * \todo fixme
1606 */
1607 static gint
1608 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1609 {
1610 if (ai) {
1611 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1612 return sp_event_context_item_handler (desktop->event_context, spi, event);
1613 } else {
1614 return sp_event_context_root_handler (desktop->event_context, event);
1615 }
1616 }
1618 static void
1619 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1620 g_return_if_fail(SP_IS_GROUP(layer));
1621 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1622 }
1624 /// Callback
1625 static void
1626 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1627 g_return_if_fail(SP_IS_GROUP(layer));
1628 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1629 }
1631 /// Callback
1632 static void
1633 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1634 SPDesktop *desktop)
1635 {
1636 desktop->_layer_changed_signal.emit (bottom);
1637 }
1639 /// Called when document is starting to be rebuilt.
1640 static void
1641 _reconstruction_start (SPDesktop * desktop)
1642 {
1643 // printf("Desktop, starting reconstruction\n");
1644 desktop->_reconstruction_old_layer_id = g_strdup(desktop->currentLayer()->getId());
1645 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1647 /*
1648 GSList const * selection_objs = desktop->selection->list();
1649 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1651 }
1652 */
1653 desktop->selection->clear();
1655 // printf("Desktop, starting reconstruction end\n");
1656 }
1658 /// Called when document rebuild is finished.
1659 static void
1660 _reconstruction_finish (SPDesktop * desktop)
1661 {
1662 // printf("Desktop, finishing reconstruction\n");
1663 if (desktop->_reconstruction_old_layer_id == NULL)
1664 return;
1666 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1667 if (newLayer != NULL)
1668 desktop->setCurrentLayer(newLayer);
1670 g_free(desktop->_reconstruction_old_layer_id);
1671 desktop->_reconstruction_old_layer_id = NULL;
1672 // printf("Desktop, finishing reconstruction end\n");
1673 return;
1674 }
1676 /**
1677 * Namedview_modified callback.
1678 */
1679 static void
1680 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1681 {
1682 SPNamedView *nv=SP_NAMEDVIEW(obj);
1684 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1686 /* Show/hide page background */
1687 if (nv->pagecolor & 0xff) {
1688 sp_canvas_item_show (desktop->table);
1689 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1690 sp_canvas_item_move_to_z (desktop->table, 0);
1691 } else {
1692 sp_canvas_item_hide (desktop->table);
1693 }
1695 /* Show/hide page border */
1696 if (nv->showborder) {
1697 // show
1698 sp_canvas_item_show (desktop->page_border);
1699 // set color and shadow
1700 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1701 if (nv->pageshadow) {
1702 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1703 }
1704 // place in the z-order stack
1705 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1706 sp_canvas_item_move_to_z (desktop->page_border, 2);
1707 } else {
1708 int order = sp_canvas_item_order (desktop->page_border);
1709 int morder = sp_canvas_item_order (desktop->drawing);
1710 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1711 morder - order);
1712 }
1713 } else {
1714 sp_canvas_item_hide (desktop->page_border);
1715 if (nv->pageshadow) {
1716 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1717 }
1718 }
1720 /* Show/hide page shadow */
1721 if (nv->showpageshadow && nv->pageshadow) {
1722 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1723 } else {
1724 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1725 }
1727 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1728 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1729 (SP_RGBA32_R_U(nv->pagecolor) +
1730 SP_RGBA32_G_U(nv->pagecolor) +
1731 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1732 // the background color is light or transparent, use black outline
1733 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1734 } else { // use white outline
1735 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1736 }
1737 }
1738 }
1740 Geom::Matrix SPDesktop::w2d() const
1741 {
1742 return _w2d;
1743 }
1745 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1746 {
1747 return p * _w2d;
1748 }
1750 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1751 {
1752 return p * _d2w;
1753 }
1755 Geom::Matrix SPDesktop::doc2dt() const
1756 {
1757 return _doc2dt;
1758 }
1760 Geom::Matrix SPDesktop::dt2doc() const
1761 {
1762 // doc2dt is its own inverse
1763 return _doc2dt;
1764 }
1766 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1767 {
1768 return p * _doc2dt;
1769 }
1771 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1772 {
1773 return p * dt2doc();
1774 }
1777 /*
1778 * Pop event context from desktop's context stack. Never used.
1779 */
1780 // void
1781 // SPDesktop::pop_event_context (unsigned int key)
1782 // {
1783 // SPEventContext *ec = NULL;
1784 //
1785 // if (event_context && event_context->key == key) {
1786 // g_return_if_fail (event_context);
1787 // g_return_if_fail (event_context->next);
1788 // ec = event_context;
1789 // sp_event_context_deactivate (ec);
1790 // event_context = ec->next;
1791 // sp_event_context_activate (event_context);
1792 // _event_context_changed_signal.emit (this, ec);
1793 // }
1794 //
1795 // SPEventContext *ref = event_context;
1796 // while (ref && ref->next && ref->next->key != key)
1797 // ref = ref->next;
1798 //
1799 // if (ref && ref->next) {
1800 // ec = ref->next;
1801 // ref->next = ec->next;
1802 // }
1803 //
1804 // if (ec) {
1805 // sp_event_context_finish (ec);
1806 // g_object_unref (G_OBJECT (ec));
1807 // }
1808 // }
1810 /*
1811 Local Variables:
1812 mode:c++
1813 c-file-style:"stroustrup"
1814 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1815 indent-tabs-mode:nil
1816 fill-column:99
1817 End:
1818 */
1819 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :