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