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) {
609 dtw->desktop = this;
610 }
611 dtw->updateNamedview();
613 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
614 _document_replaced_signal.emit (this, theDocument);
615 }
617 /**
618 * Make desktop switch event contexts.
619 */
620 void
621 SPDesktop::set_event_context (GtkType type, const gchar *config)
622 {
623 SPEventContext *ec;
624 while (event_context) {
625 ec = event_context;
626 sp_event_context_deactivate (ec);
627 event_context = ec->next;
628 sp_event_context_finish (ec);
629 g_object_unref (G_OBJECT (ec));
630 }
632 ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
633 ec->next = event_context;
634 event_context = ec;
635 sp_event_context_activate (ec);
636 _event_context_changed_signal.emit (this, ec);
637 }
639 /**
640 * Push event context onto desktop's context stack.
641 */
642 void
643 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
644 {
645 SPEventContext *ref, *ec;
647 if (event_context && event_context->key == key) return;
648 ref = event_context;
649 while (ref && ref->next && ref->next->key != key) ref = ref->next;
650 if (ref && ref->next) {
651 ec = ref->next;
652 ref->next = ec->next;
653 sp_event_context_finish (ec);
654 g_object_unref (G_OBJECT (ec));
655 }
657 if (event_context) sp_event_context_deactivate (event_context);
658 ec = sp_event_context_new (type, this, config, key);
659 ec->next = event_context;
660 event_context = ec;
661 sp_event_context_activate (ec);
662 _event_context_changed_signal.emit (this, ec);
663 }
665 /**
666 * Sets the coordinate status to a given point
667 */
668 void
669 SPDesktop::set_coordinate_status (Geom::Point p) {
670 _widget->setCoordinateStatus(p);
671 }
673 /**
674 * \see sp_document_item_from_list_at_point_bottom()
675 */
676 SPItem *
677 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
678 {
679 g_return_val_if_fail (doc() != NULL, NULL);
680 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
681 }
683 /**
684 * \see sp_document_item_at_point()
685 */
686 SPItem *
687 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
688 {
689 g_return_val_if_fail (doc() != NULL, NULL);
690 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
691 }
693 /**
694 * \see sp_document_group_at_point()
695 */
696 SPItem *
697 SPDesktop::group_at_point (Geom::Point const p) const
698 {
699 g_return_val_if_fail (doc() != NULL, NULL);
700 return sp_document_group_at_point (doc(), dkey, p);
701 }
703 /**
704 * \brief Returns the mouse point in document coordinates; if mouse is
705 * outside the canvas, returns the center of canvas viewpoint
706 */
707 Geom::Point
708 SPDesktop::point() const
709 {
710 Geom::Point p = _widget->getPointer();
711 Geom::Point pw = sp_canvas_window_to_world (canvas, p);
712 p = w2d(pw);
714 Geom::Rect const r = canvas->getViewbox();
716 Geom::Point r0 = w2d(r.min());
717 Geom::Point r1 = w2d(r.max());
719 if (p[Geom::X] >= r0[Geom::X] &&
720 p[Geom::X] <= r1[Geom::X] &&
721 p[Geom::Y] >= r1[Geom::Y] &&
722 p[Geom::Y] <= r0[Geom::Y])
723 {
724 return p;
725 } else {
726 return (r0 + r1) / 2;
727 }
728 }
730 /**
731 * Put current zoom data in history list.
732 */
733 void
734 SPDesktop::push_current_zoom (GList **history)
735 {
736 Geom::Rect const area = get_display_area();
738 NRRect *old_zoom = g_new(NRRect, 1);
739 old_zoom->x0 = area.min()[Geom::X];
740 old_zoom->x1 = area.max()[Geom::X];
741 old_zoom->y0 = area.min()[Geom::Y];
742 old_zoom->y1 = area.max()[Geom::Y];
743 if ( *history == NULL
744 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
745 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
746 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
747 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
748 {
749 *history = g_list_prepend (*history, old_zoom);
750 }
751 }
753 /**
754 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
755 */
756 void
757 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
758 {
759 g_assert(_widget);
761 // save the zoom
762 if (log) {
763 push_current_zoom(&zooms_past);
764 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
765 g_list_free (zooms_future);
766 zooms_future = NULL;
767 }
769 double const cx = 0.5 * (x0 + x1);
770 double const cy = 0.5 * (y0 + y1);
772 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
773 Geom::Rect viewbox = canvas->getViewbox();
774 viewbox.expandBy(-border);
776 double scale = _d2w.descrim();
777 double newscale;
778 if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
779 newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
780 } else {
781 newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
782 }
784 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
786 int clear = FALSE;
787 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
788 /* Set zoom factors */
789 _d2w = Geom::Scale(newscale, -newscale);
790 _w2d = Geom::Scale(1/newscale, 1/-newscale);
791 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
792 clear = TRUE;
793 }
795 /* Calculate top left corner (in document pixels) */
796 x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
797 y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
799 /* Scroll */
800 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
802 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
803 sp_box3d_context_update_lines(event_context);
805 _widget->updateRulers();
806 _widget->updateScrollbars(_d2w.descrim());
807 _widget->updateZoom();
808 }
810 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
811 {
812 set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
813 }
815 /**
816 * Return viewbox dimensions.
817 */
818 Geom::Rect SPDesktop::get_display_area() const
819 {
820 Geom::Rect const viewbox = canvas->getViewbox();
822 double const scale = _d2w[0];
824 return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
825 Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
826 }
828 /**
829 * Revert back to previous zoom if possible.
830 */
831 void
832 SPDesktop::prev_zoom()
833 {
834 if (zooms_past == NULL) {
835 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
836 return;
837 }
839 // push current zoom into forward zooms list
840 push_current_zoom (&zooms_future);
842 // restore previous zoom
843 set_display_area (((NRRect *) zooms_past->data)->x0,
844 ((NRRect *) zooms_past->data)->y0,
845 ((NRRect *) zooms_past->data)->x1,
846 ((NRRect *) zooms_past->data)->y1,
847 0, false);
849 // remove the just-added zoom from the past zooms list
850 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
851 }
853 /**
854 * Set zoom to next in list.
855 */
856 void
857 SPDesktop::next_zoom()
858 {
859 if (zooms_future == NULL) {
860 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
861 return;
862 }
864 // push current zoom into past zooms list
865 push_current_zoom (&zooms_past);
867 // restore next zoom
868 set_display_area (((NRRect *) zooms_future->data)->x0,
869 ((NRRect *) zooms_future->data)->y0,
870 ((NRRect *) zooms_future->data)->x1,
871 ((NRRect *) zooms_future->data)->y1,
872 0, false);
874 // remove the just-used zoom from the zooms_future list
875 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
876 }
878 #include "tools-switch.h"
879 #include "node-context.h"
880 #include "shape-editor.h"
881 #include "nodepath.h"
883 /** \brief Performs a quick zoom into what the user is working on
884 \param enable Whether we're going in or out of quick zoom
886 */
887 void
888 SPDesktop::zoom_quick (bool enable)
889 {
890 if (enable == _quick_zoom_enabled) {
891 return;
892 }
894 if (enable == true) {
895 _quick_zoom_stored_area = get_display_area();
896 bool zoomed = false;
898 if (!zoomed) {
899 SPItem * singleItem = selection->singleItem();
900 if (singleItem != NULL && tools_isactive(this, TOOLS_NODES)) {
902 Inkscape::NodePath::Path * nodepath = event_context->shape_editor->get_nodepath();
903 // printf("I've got a nodepath, crazy\n");
905 if (nodepath) {
906 Geom::Rect nodes;
907 bool firstnode = true;
909 if (nodepath->selected) {
910 for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
911 Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
912 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
913 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
914 if (node->selected) {
915 // printf("\tSelected node\n");
916 if (firstnode) {
917 nodes = Geom::Rect(node->pos, node->pos);
918 firstnode = false;
919 } else {
920 nodes.expandTo(node->pos);
921 }
923 if (node->p.other != NULL) {
924 /* Include previous node pos */
925 nodes.expandTo(node->p.other->pos);
927 /* Include previous handle */
928 if (!sp_node_side_is_line(node, &node->p)) {
929 nodes.expandTo(node->p.pos);
930 }
931 }
933 if (node->n.other != NULL) {
934 /* Include previous node pos */
935 nodes.expandTo(node->n.other->pos);
937 /* Include previous handle */
938 if (!sp_node_side_is_line(node, &node->n)) {
939 nodes.expandTo(node->n.pos);
940 }
941 }
942 }
943 }
944 }
946 if (!firstnode && nodes.area() * 2.0 < _quick_zoom_stored_area.area()) {
947 set_display_area(nodes, 10);
948 zoomed = true;
949 }
950 }
951 }
952 }
953 }
955 if (!zoomed) {
956 Geom::OptRect const d = selection->bounds();
957 if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
958 set_display_area(*d, 10);
959 zoomed = true;
960 }
961 }
963 if (!zoomed) {
964 zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
965 zoomed = true;
966 }
967 } else {
968 set_display_area(_quick_zoom_stored_area, 0);
969 }
971 _quick_zoom_enabled = enable;
972 return;
973 }
975 /**
976 * Zoom to point with absolute zoom factor.
977 */
978 void
979 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
980 {
981 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
983 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
984 // this check prevents "sliding" when trying to zoom in at maximum zoom;
985 /// \todo someone please fix calculations properly and remove this hack
986 if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
987 return;
989 Geom::Rect const viewbox = canvas->getViewbox();
991 double const width2 = viewbox.dimensions()[Geom::X] / zoom;
992 double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
994 set_display_area(cx - px * width2,
995 cy - py * height2,
996 cx + (1 - px) * width2,
997 cy + (1 - py) * height2,
998 0.0);
999 }
1001 /**
1002 * Zoom to center with absolute zoom factor.
1003 */
1004 void
1005 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
1006 {
1007 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
1008 }
1010 /**
1011 * Zoom to point with relative zoom factor.
1012 */
1013 void
1014 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
1015 {
1016 Geom::Rect const area = get_display_area();
1018 if (cx < area.min()[Geom::X]) {
1019 cx = area.min()[Geom::X];
1020 }
1021 if (cx > area.max()[Geom::X]) {
1022 cx = area.max()[Geom::X];
1023 }
1024 if (cy < area.min()[Geom::Y]) {
1025 cy = area.min()[Geom::Y];
1026 }
1027 if (cy > area.max()[Geom::Y]) {
1028 cy = area.max()[Geom::Y];
1029 }
1031 gdouble const scale = _d2w.descrim() * zoom;
1032 double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1033 double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
1035 zoom_absolute_keep_point(cx, cy, px, py, scale);
1036 }
1038 /**
1039 * Zoom to center with relative zoom factor.
1040 */
1041 void
1042 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1043 {
1044 gdouble scale = _d2w.descrim() * zoom;
1045 zoom_absolute (cx, cy, scale);
1046 }
1048 /**
1049 * Set display area to origin and current document dimensions.
1050 */
1051 void
1052 SPDesktop::zoom_page()
1053 {
1054 Geom::Rect d(Geom::Point(0, 0),
1055 Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1057 if (d.minExtent() < 1.0) {
1058 return;
1059 }
1061 set_display_area(d, 10);
1062 }
1064 /**
1065 * Set display area to current document width.
1066 */
1067 void
1068 SPDesktop::zoom_page_width()
1069 {
1070 Geom::Rect const a = get_display_area();
1072 if (sp_document_width(doc()) < 1.0) {
1073 return;
1074 }
1076 Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1077 Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1079 set_display_area(d, 10);
1080 }
1082 /**
1083 * Zoom to selection.
1084 */
1085 void
1086 SPDesktop::zoom_selection()
1087 {
1088 Geom::OptRect const d = selection->bounds();
1090 if ( !d || d->minExtent() < 0.1 ) {
1091 return;
1092 }
1094 set_display_area(*d, 10);
1095 }
1097 /**
1098 * Tell widget to let zoom widget grab keyboard focus.
1099 */
1100 void
1101 SPDesktop::zoom_grab_focus()
1102 {
1103 _widget->letZoomGrabFocus();
1104 }
1106 /**
1107 * Zoom to whole drawing.
1108 */
1109 void
1110 SPDesktop::zoom_drawing()
1111 {
1112 g_return_if_fail (doc() != NULL);
1113 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1114 g_return_if_fail (docitem != NULL);
1116 Geom::OptRect d = sp_item_bbox_desktop(docitem);
1118 /* Note that the second condition here indicates that
1119 ** there are no items in the drawing.
1120 */
1121 if ( !d || d->minExtent() < 0.1 ) {
1122 return;
1123 }
1125 set_display_area(*d, 10);
1126 }
1128 /**
1129 * Scroll canvas by specific coordinate amount in svg coordinates.
1130 */
1131 void
1132 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1133 {
1134 double scale = _d2w.descrim();
1135 scroll_world(dx*scale, dy*scale, is_scrolling);
1136 }
1138 /**
1139 * Scroll canvas by specific coordinate amount.
1140 */
1141 void
1142 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1143 {
1144 g_assert(_widget);
1146 Geom::Rect const viewbox = canvas->getViewbox();
1148 sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1150 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1151 sp_box3d_context_update_lines(event_context);
1153 _widget->updateRulers();
1154 _widget->updateScrollbars(_d2w.descrim());
1155 }
1157 bool
1158 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1159 {
1160 using Geom::X;
1161 using Geom::Y;
1163 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1164 gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1166 // autoscrolldistance is in screen pixels, but the display area is in document units
1167 autoscrolldistance /= _d2w.descrim();
1168 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1169 Geom::Rect dbox = get_display_area();
1170 dbox.expandBy(-autoscrolldistance);
1172 if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1173 !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y]) ) {
1175 Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1177 gdouble x_to;
1178 if (p[X] < dbox.min()[X])
1179 x_to = dbox.min()[X];
1180 else if (p[X] > dbox.max()[X])
1181 x_to = dbox.max()[X];
1182 else
1183 x_to = p[X];
1185 gdouble y_to;
1186 if (p[Y] < dbox.min()[Y])
1187 y_to = dbox.min()[Y];
1188 else if (p[Y] > dbox.max()[Y])
1189 y_to = dbox.max()[Y];
1190 else
1191 y_to = p[Y];
1193 Geom::Point const d_dt(x_to, y_to);
1194 Geom::Point const d_w( d_dt * _d2w );
1195 Geom::Point const moved_w( d_w - s_w );
1197 if (autoscrollspeed == 0)
1198 autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1200 if (autoscrollspeed != 0)
1201 scroll_world (autoscrollspeed * moved_w);
1203 return true;
1204 }
1205 return false;
1206 }
1208 bool
1209 SPDesktop::is_iconified()
1210 {
1211 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1212 }
1214 void
1215 SPDesktop::iconify()
1216 {
1217 _widget->setIconified();
1218 }
1220 bool
1221 SPDesktop::is_maximized()
1222 {
1223 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1224 }
1226 void
1227 SPDesktop::maximize()
1228 {
1229 _widget->setMaximized();
1230 }
1232 bool
1233 SPDesktop::is_fullscreen()
1234 {
1235 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1236 }
1238 void
1239 SPDesktop::fullscreen()
1240 {
1241 _widget->setFullscreen();
1242 }
1244 /** \brief Checks to see if the user is working in focused mode
1246 Returns the value of \c _focusMode
1247 */
1248 bool
1249 SPDesktop::is_focusMode()
1250 {
1251 return _focusMode;
1252 }
1254 /** \brief Changes whether the user is in focus mode or not
1255 \param mode Which mode the view should be in
1257 */
1258 void
1259 SPDesktop::focusMode (bool mode)
1260 {
1261 if (mode == _focusMode) { return; }
1263 _focusMode = mode;
1265 layoutWidget();
1266 //sp_desktop_widget_layout(SPDesktopWidget);
1268 return;
1269 }
1271 void
1272 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1273 {
1274 _widget->getGeometry (x, y, w, h);
1275 }
1277 void
1278 SPDesktop::setWindowPosition (Geom::Point p)
1279 {
1280 _widget->setPosition (p);
1281 }
1283 void
1284 SPDesktop::setWindowSize (gint w, gint h)
1285 {
1286 _widget->setSize (w, h);
1287 }
1289 void
1290 SPDesktop::setWindowTransient (void *p, int transient_policy)
1291 {
1292 _widget->setTransient (p, transient_policy);
1293 }
1295 Gtk::Window*
1296 SPDesktop::getToplevel( )
1297 {
1298 return _widget->getWindow();
1299 }
1301 void
1302 SPDesktop::presentWindow()
1303 {
1304 _widget->present();
1305 }
1307 bool
1308 SPDesktop::warnDialog (gchar *text)
1309 {
1310 return _widget->warnDialog (text);
1311 }
1313 void
1314 SPDesktop::toggleRulers()
1315 {
1316 _widget->toggleRulers();
1317 }
1319 void
1320 SPDesktop::toggleScrollbars()
1321 {
1322 _widget->toggleScrollbars();
1323 }
1325 void
1326 SPDesktop::layoutWidget()
1327 {
1328 _widget->layout();
1329 }
1331 void
1332 SPDesktop::destroyWidget()
1333 {
1334 _widget->destroy();
1335 }
1337 bool
1338 SPDesktop::shutdown()
1339 {
1340 return _widget->shutdown();
1341 }
1343 bool SPDesktop::onDeleteUI (GdkEventAny*)
1344 {
1345 if(shutdown())
1346 return true;
1348 destroyWidget();
1349 return false;
1350 }
1352 /**
1353 * onWindowStateEvent
1354 *
1355 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1356 * Since GTK doesn't have a way to query this state information directly, we
1357 * record it for the desktop here, and also possibly trigger a layout.
1358 */
1359 bool
1360 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1361 {
1362 // Record the desktop window's state
1363 window_state = event->new_window_state;
1365 // Layout may differ depending on full-screen mode or not
1366 GdkWindowState changed = event->changed_mask;
1367 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1368 layoutWidget();
1369 }
1371 return false;
1372 }
1374 void
1375 SPDesktop::setToolboxFocusTo (gchar const *label)
1376 {
1377 _widget->setToolboxFocusTo (label);
1378 }
1380 void
1381 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1382 {
1383 _widget->setToolboxAdjustmentValue (id, val);
1384 }
1386 void
1387 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1388 {
1389 _widget->setToolboxSelectOneValue (id, val);
1390 }
1392 bool
1393 SPDesktop::isToolboxButtonActive (gchar const *id)
1394 {
1395 return _widget->isToolboxButtonActive (id);
1396 }
1398 void
1399 SPDesktop::emitToolSubselectionChanged(gpointer data)
1400 {
1401 _tool_subselection_changed.emit(data);
1402 inkscape_subselection_changed (this);
1403 }
1405 void
1406 SPDesktop::updateNow()
1407 {
1408 sp_canvas_update_now(canvas);
1409 }
1411 void
1412 SPDesktop::enableInteraction()
1413 {
1414 _widget->enableInteraction();
1415 }
1417 void SPDesktop::disableInteraction()
1418 {
1419 _widget->disableInteraction();
1420 }
1422 void SPDesktop::setWaitingCursor()
1423 {
1424 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1425 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1426 gdk_cursor_unref(waiting);
1427 // GDK needs the flush for the cursor change to take effect
1428 gdk_flush();
1429 waiting_cursor = true;
1430 }
1432 void SPDesktop::clearWaitingCursor()
1433 {
1434 if (waiting_cursor)
1435 sp_event_context_update_cursor(sp_desktop_event_context(this));
1436 }
1438 void SPDesktop::toggleColorProfAdjust()
1439 {
1440 _widget->toggleColorProfAdjust();
1441 }
1443 void SPDesktop::toggleGrids()
1444 {
1445 if (namedview->grids) {
1446 if(gridgroup) {
1447 showGrids(!grids_visible);
1448 }
1449 } else {
1450 //there is no grid present at the moment. add a rectangular grid and make it visible
1451 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1452 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1453 showGrids(true);
1454 }
1455 }
1457 void SPDesktop::showGrids(bool show, bool dirty_document)
1458 {
1459 grids_visible = show;
1460 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1461 if (show) {
1462 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1463 } else {
1464 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1465 }
1466 }
1468 void SPDesktop::toggleSnapGlobal()
1469 {
1470 bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1471 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1472 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1473 }
1475 //----------------------------------------------------------------------
1476 // Callback implementations. The virtual ones are connected by the view.
1478 void
1479 SPDesktop::onPositionSet (double x, double y)
1480 {
1481 _widget->viewSetPosition (Geom::Point(x,y));
1482 }
1484 void
1485 SPDesktop::onResized (double /*x*/, double /*y*/)
1486 {
1487 // Nothing called here
1488 }
1490 /**
1491 * Redraw callback; queues Gtk redraw; connected by View.
1492 */
1493 void
1494 SPDesktop::onRedrawRequested ()
1495 {
1496 if (main) {
1497 _widget->requestCanvasUpdate();
1498 }
1499 }
1501 void
1502 SPDesktop::updateCanvasNow()
1503 {
1504 _widget->requestCanvasUpdateAndWait();
1505 }
1507 /**
1508 * Associate document with desktop.
1509 */
1510 void
1511 SPDesktop::setDocument (SPDocument *doc)
1512 {
1513 if (this->doc() && doc) {
1514 namedview->hide(this);
1515 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1516 }
1518 if (_layer_hierarchy) {
1519 _layer_hierarchy->clear();
1520 delete _layer_hierarchy;
1521 }
1522 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1523 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1524 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1525 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1526 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1528 /* setup EventLog */
1529 event_log = new Inkscape::EventLog(doc);
1530 doc->addUndoObserver(*event_log);
1532 _commit_connection.disconnect();
1533 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1535 /// \todo fixme: This condition exists to make sure the code
1536 /// inside is NOT called on initialization, only on replacement. But there
1537 /// are surely more safe methods to accomplish this.
1538 // TODO since the comment had reversed logic, check the intent of this block of code:
1539 if (drawing) {
1540 NRArenaItem *ai = 0;
1542 namedview = sp_document_namedview (doc, NULL);
1543 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1544 number = namedview->getViewCount();
1546 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1547 SP_CANVAS_ARENA (drawing)->arena,
1548 dkey,
1549 SP_ITEM_SHOW_DISPLAY);
1550 if (ai) {
1551 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1552 }
1553 namedview->show(this);
1554 /* Ugly hack */
1555 activate_guides (true);
1556 /* Ugly hack */
1557 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1558 }
1560 _document_replaced_signal.emit (this, doc);
1562 View::setDocument (doc);
1563 }
1565 void
1566 SPDesktop::onStatusMessage
1567 (Inkscape::MessageType type, gchar const *message)
1568 {
1569 if (_widget) {
1570 _widget->setMessage(type, message);
1571 }
1572 }
1574 void
1575 SPDesktop::onDocumentURISet (gchar const* uri)
1576 {
1577 _widget->setTitle(uri);
1578 }
1580 /**
1581 * Resized callback.
1582 */
1583 void
1584 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1585 {
1586 _doc2dt[5] = height;
1587 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1588 Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1589 SP_CTRLRECT(page)->setRectangle(a);
1590 SP_CTRLRECT(page_border)->setRectangle(a);
1591 }
1594 void
1595 SPDesktop::_onActivate (SPDesktop* dt)
1596 {
1597 if (!dt->_widget) return;
1598 dt->_widget->activateDesktop();
1599 }
1601 void
1602 SPDesktop::_onDeactivate (SPDesktop* dt)
1603 {
1604 if (!dt->_widget) return;
1605 dt->_widget->deactivateDesktop();
1606 }
1608 void
1609 SPDesktop::_onSelectionModified
1610 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1611 {
1612 if (!dt->_widget) return;
1613 dt->_widget->updateScrollbars (dt->_d2w.descrim());
1614 }
1616 static void
1617 _onSelectionChanged
1618 (Inkscape::Selection *selection, SPDesktop *desktop)
1619 {
1620 /** \todo
1621 * only change the layer for single selections, or what?
1622 * This seems reasonable -- for multiple selections there can be many
1623 * different layers involved.
1624 */
1625 SPItem *item=selection->singleItem();
1626 if (item) {
1627 SPObject *layer=desktop->layerForObject(item);
1628 if ( layer && layer != desktop->currentLayer() ) {
1629 desktop->setCurrentLayer(layer);
1630 }
1631 }
1632 }
1634 /**
1635 * Calls event handler of current event context.
1636 * \param arena Unused
1637 * \todo fixme
1638 */
1639 static gint
1640 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1641 {
1642 if (ai) {
1643 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1644 return sp_event_context_item_handler (desktop->event_context, spi, event);
1645 } else {
1646 return sp_event_context_root_handler (desktop->event_context, event);
1647 }
1648 }
1650 static void
1651 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1652 g_return_if_fail(SP_IS_GROUP(layer));
1653 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1654 }
1656 /// Callback
1657 static void
1658 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1659 g_return_if_fail(SP_IS_GROUP(layer));
1660 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1661 }
1663 /// Callback
1664 static void
1665 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1666 SPDesktop *desktop)
1667 {
1668 desktop->_layer_changed_signal.emit (bottom);
1669 }
1671 /// Called when document is starting to be rebuilt.
1672 static void
1673 _reconstruction_start (SPDesktop * desktop)
1674 {
1675 // printf("Desktop, starting reconstruction\n");
1676 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1677 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1679 /*
1680 GSList const * selection_objs = desktop->selection->list();
1681 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1683 }
1684 */
1685 desktop->selection->clear();
1687 // printf("Desktop, starting reconstruction end\n");
1688 }
1690 /// Called when document rebuild is finished.
1691 static void
1692 _reconstruction_finish (SPDesktop * desktop)
1693 {
1694 // printf("Desktop, finishing reconstruction\n");
1695 if (desktop->_reconstruction_old_layer_id == NULL)
1696 return;
1698 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1699 if (newLayer != NULL)
1700 desktop->setCurrentLayer(newLayer);
1702 g_free(desktop->_reconstruction_old_layer_id);
1703 desktop->_reconstruction_old_layer_id = NULL;
1704 // printf("Desktop, finishing reconstruction end\n");
1705 return;
1706 }
1708 /**
1709 * Namedview_modified callback.
1710 */
1711 static void
1712 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1713 {
1714 SPNamedView *nv=SP_NAMEDVIEW(obj);
1716 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1718 /* Show/hide page background */
1719 if (nv->pagecolor & 0xff) {
1720 sp_canvas_item_show (desktop->table);
1721 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1722 sp_canvas_item_move_to_z (desktop->table, 0);
1723 } else {
1724 sp_canvas_item_hide (desktop->table);
1725 }
1727 /* Show/hide page border */
1728 if (nv->showborder) {
1729 // show
1730 sp_canvas_item_show (desktop->page_border);
1731 // set color and shadow
1732 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1733 if (nv->pageshadow) {
1734 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1735 }
1736 // place in the z-order stack
1737 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1738 sp_canvas_item_move_to_z (desktop->page_border, 2);
1739 } else {
1740 int order = sp_canvas_item_order (desktop->page_border);
1741 int morder = sp_canvas_item_order (desktop->drawing);
1742 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1743 morder - order);
1744 }
1745 } else {
1746 sp_canvas_item_hide (desktop->page_border);
1747 if (nv->pageshadow) {
1748 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1749 }
1750 }
1752 /* Show/hide page shadow */
1753 if (nv->showpageshadow && nv->pageshadow) {
1754 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1755 } else {
1756 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1757 }
1759 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1760 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1761 (SP_RGBA32_R_U(nv->pagecolor) +
1762 SP_RGBA32_G_U(nv->pagecolor) +
1763 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1764 // the background color is light or transparent, use black outline
1765 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1766 } else { // use white outline
1767 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1768 }
1769 }
1770 }
1772 Geom::Matrix SPDesktop::w2d() const
1773 {
1774 return _w2d;
1775 }
1777 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1778 {
1779 return p * _w2d;
1780 }
1782 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1783 {
1784 return p * _d2w;
1785 }
1787 Geom::Matrix SPDesktop::doc2dt() const
1788 {
1789 return _doc2dt;
1790 }
1792 Geom::Matrix SPDesktop::dt2doc() const
1793 {
1794 // doc2dt is its own inverse
1795 return _doc2dt;
1796 }
1798 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1799 {
1800 return p * _doc2dt;
1801 }
1803 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1804 {
1805 return p * dt2doc();
1806 }
1809 /**
1810 * Pop event context from desktop's context stack. Never used.
1811 */
1812 // void
1813 // SPDesktop::pop_event_context (unsigned int key)
1814 // {
1815 // SPEventContext *ec = NULL;
1816 //
1817 // if (event_context && event_context->key == key) {
1818 // g_return_if_fail (event_context);
1819 // g_return_if_fail (event_context->next);
1820 // ec = event_context;
1821 // sp_event_context_deactivate (ec);
1822 // event_context = ec->next;
1823 // sp_event_context_activate (event_context);
1824 // _event_context_changed_signal.emit (this, ec);
1825 // }
1826 //
1827 // SPEventContext *ref = event_context;
1828 // while (ref && ref->next && ref->next->key != key)
1829 // ref = ref->next;
1830 //
1831 // if (ref && ref->next) {
1832 // ec = ref->next;
1833 // ref->next = ec->next;
1834 // }
1835 //
1836 // if (ec) {
1837 // sp_event_context_finish (ec);
1838 // g_object_unref (G_OBJECT (ec));
1839 // }
1840 // }
1842 /*
1843 Local Variables:
1844 mode:c++
1845 c-file-style:"stroustrup"
1846 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1847 indent-tabs-mode:nil
1848 fill-column:99
1849 End:
1850 */
1851 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :