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 _saved_display_mode(Inkscape::RENDERMODE_NORMAL),
152 _widget( 0 ),
153 _inkscape( 0 ),
154 _guides_message_context( 0 ),
155 _active( false ),
156 _w2d(),
157 _d2w(),
158 _doc2dt( Geom::Scale(1, -1) ),
159 grids_visible( false )
160 {
161 _d2w.setIdentity();
162 _w2d.setIdentity();
164 selection = Inkscape::GC::release( new Inkscape::Selection(this) );
165 }
167 void
168 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
169 {
170 // Temporary workaround for link order issues:
171 Inkscape::DeviceManager::getManager().getDevices();
172 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
174 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
176 current = prefs->getStyle("/desktop/style");
178 namedview = nv;
179 canvas = aCanvas;
181 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
182 /* Kill flicker */
183 sp_document_ensure_up_to_date (document);
185 /* Setup Dialog Manager */
186 _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
188 dkey = sp_item_display_key_new (1);
190 /* Connect document */
191 setDocument (document);
193 number = namedview->getViewCount();
196 /* Setup Canvas */
197 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
199 SPCanvasGroup *root = sp_canvas_root (canvas);
201 /* Setup adminstrative layers */
202 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
203 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
204 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
205 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
207 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
208 SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
209 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
210 sp_canvas_item_move_to_z (table, 0);
212 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
213 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
214 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
216 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
217 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
219 SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
221 if (prefs->getBool("/options/startmode/outline")) {
222 // Start in outline mode
223 setDisplayModeOutline();
224 } else {
225 // Start in normal mode, default
226 setDisplayModeNormal();
227 }
229 // The order in which these canvas items are added determines the z-order. It's therefore
230 // important to add the tempgroup (which will contain the snapindicator) before adding the
231 // controls. Only this way one will be able to quickly (before the snap indicator has
232 // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
233 // will not work (the snap indicator is on top of the node handler; is the snapindicator
234 // being selected? or does it intercept some of the events that should have gone to the
235 // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
236 gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
237 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
238 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
239 tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
240 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
242 /* Push select tool to the bottom of stack */
243 /** \todo
244 * FIXME: this is the only call to this. Everything else seems to just
245 * call "set" instead of "push". Can we assume that there is only one
246 * context ever?
247 */
248 push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
250 // display rect and zoom are now handled in sp_desktop_widget_realize()
252 Geom::Rect const d(Geom::Point(0.0, 0.0),
253 Geom::Point(sp_document_width(document), sp_document_height(document)));
255 SP_CTRLRECT(page)->setRectangle(d);
256 SP_CTRLRECT(page_border)->setRectangle(d);
258 /* the following sets the page shadow on the canvas
259 It was originally set to 5, which is really cheesy!
260 It now is an attribute in the document's namedview. If a value of
261 0 is used, then the constructor for a shadow is not initialized.
262 */
264 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
265 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
266 }
269 /* Connect event for page resize */
270 _doc2dt[5] = sp_document_height (document);
271 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
273 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
275 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
276 SP_CANVAS_ARENA (drawing)->arena,
277 dkey,
278 SP_ITEM_SHOW_DISPLAY);
279 if (ai) {
280 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
281 }
283 namedview->show(this);
284 /* Ugly hack */
285 activate_guides (true);
286 /* Ugly hack */
287 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
289 /* Set up notification of rebuilding the document, this allows
290 for saving object related settings in the document. */
291 _reconstruction_start_connection =
292 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
293 _reconstruction_finish_connection =
294 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
295 _reconstruction_old_layer_id = NULL;
297 // ?
298 // sp_active_desktop_set (desktop);
299 _inkscape = INKSCAPE;
301 _activate_connection = _activate_signal.connect(
302 sigc::bind(
303 sigc::ptr_fun(_onActivate),
304 this
305 )
306 );
307 _deactivate_connection = _deactivate_signal.connect(
308 sigc::bind(
309 sigc::ptr_fun(_onDeactivate),
310 this
311 )
312 );
314 _sel_modified_connection = selection->connectModified(
315 sigc::bind(
316 sigc::ptr_fun(&_onSelectionModified),
317 this
318 )
319 );
320 _sel_changed_connection = selection->connectChanged(
321 sigc::bind(
322 sigc::ptr_fun(&_onSelectionChanged),
323 this
324 )
325 );
328 /* setup LayerManager */
329 // (Setting up after the connections are all in place, as it may use some of them)
330 layer_manager = new Inkscape::LayerManager( this );
332 showGrids(namedview->grids_visible, false);
334 temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
335 snapindicator = new Inkscape::Display::SnapIndicator ( this );
336 }
339 void SPDesktop::destroy()
340 {
341 if (snapindicator) {
342 delete snapindicator;
343 snapindicator = NULL;
344 }
345 if (temporary_item_list) {
346 delete temporary_item_list;
347 temporary_item_list = NULL;
348 }
350 if (selection) {
351 delete selection;
352 selection = NULL;
353 }
355 namedview->hide(this);
357 _activate_connection.disconnect();
358 _deactivate_connection.disconnect();
359 _sel_modified_connection.disconnect();
360 _sel_changed_connection.disconnect();
361 _modified_connection.disconnect();
362 _commit_connection.disconnect();
363 _reconstruction_start_connection.disconnect();
364 _reconstruction_finish_connection.disconnect();
366 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
367 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
368 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
370 while (event_context) {
371 SPEventContext *ec = event_context;
372 event_context = ec->next;
373 sp_event_context_finish (ec);
374 g_object_unref (G_OBJECT (ec));
375 }
377 if (_layer_hierarchy) {
378 delete _layer_hierarchy;
379 // _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
380 }
382 if (layer_manager) {
383 delete layer_manager;
384 layer_manager = NULL;
385 }
387 if (_inkscape) {
388 _inkscape = NULL;
389 }
391 if (drawing) {
392 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
393 drawing = NULL;
394 }
396 delete _guides_message_context;
397 _guides_message_context = NULL;
399 g_list_free (zooms_past);
400 g_list_free (zooms_future);
401 }
403 SPDesktop::~SPDesktop() {}
405 //--------------------------------------------------------------------
406 /* Public methods */
409 /* These methods help for temporarily showing things on-canvas.
410 * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
411 * is when you want to prematurely remove the item from the canvas, by calling
412 * desktop->remove_temporary_canvasitem(tempitem).
413 */
414 /** Note that lifetime is measured in milliseconds
415 * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
416 * delete the object for you and the reference will become invalid without you knowing it.
417 * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
418 * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
419 * because the object might be deleted already without you knowing it.
420 * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
421 */
422 Inkscape::Display::TemporaryItem *
423 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
424 {
425 if (move_to_bottom) {
426 sp_canvas_item_move_to_z(item, 0);
427 }
429 return temporary_item_list->add_item(item, lifetime);
430 }
432 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
433 */
434 void
435 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
436 {
437 // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
438 if (tempitem && temporary_item_list) {
439 temporary_item_list->delete_item(tempitem);
440 }
441 }
443 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
444 SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
445 canvas->rendermode = mode;
446 _display_mode = mode;
447 if (mode != Inkscape::RENDERMODE_OUTLINE) {
448 _saved_display_mode = _display_mode;
449 }
450 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
451 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
452 }
454 void SPDesktop::displayModeToggle() {
455 if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
456 _setDisplayMode(_saved_display_mode);
457 } else {
458 _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
459 }
460 }
462 /**
463 * Returns current root (=bottom) layer.
464 */
465 SPObject *SPDesktop::currentRoot() const
466 {
467 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
468 }
470 /**
471 * Returns current top layer.
472 */
473 SPObject *SPDesktop::currentLayer() const
474 {
475 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
476 }
478 /**
479 * Sets the current layer of the desktop.
480 *
481 * Make \a object the top layer.
482 */
483 void SPDesktop::setCurrentLayer(SPObject *object) {
484 g_return_if_fail(SP_IS_GROUP(object));
485 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
486 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
487 _layer_hierarchy->setBottom(object);
488 }
490 void SPDesktop::toggleLayerSolo(SPObject *object) {
491 g_return_if_fail(SP_IS_GROUP(object));
492 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
494 bool othersShowing = false;
495 std::vector<SPObject*> layers;
496 for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
497 layers.push_back(obj);
498 othersShowing |= !SP_ITEM(obj)->isHidden();
499 }
500 for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
501 layers.push_back(obj);
502 othersShowing |= !SP_ITEM(obj)->isHidden();
503 }
506 if ( SP_ITEM(object)->isHidden() ) {
507 SP_ITEM(object)->setHidden(false);
508 }
510 for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
511 SP_ITEM(*it)->setHidden(othersShowing);
512 }
513 }
515 /**
516 * Return layer that contains \a object.
517 */
518 SPObject *SPDesktop::layerForObject(SPObject *object) {
519 g_return_val_if_fail(object != NULL, NULL);
521 SPObject *root=currentRoot();
522 object = SP_OBJECT_PARENT(object);
523 while ( object && object != root && !isLayer(object) ) {
524 object = SP_OBJECT_PARENT(object);
525 }
526 return object;
527 }
529 /**
530 * True if object is a layer.
531 */
532 bool SPDesktop::isLayer(SPObject *object) const {
533 return ( SP_IS_GROUP(object)
534 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
535 == SPGroup::LAYER ) );
536 }
538 /**
539 * True if desktop viewport fully contains \a item's bbox.
540 */
541 bool SPDesktop::isWithinViewport (SPItem *item) const
542 {
543 Geom::Rect const viewport = get_display_area();
544 Geom::OptRect const bbox = sp_item_bbox_desktop(item);
545 if (bbox) {
546 return viewport.contains(*bbox);
547 } else {
548 return true;
549 }
550 }
552 ///
553 bool SPDesktop::itemIsHidden(SPItem const *item) const {
554 return item->isHidden(this->dkey);
555 }
557 /**
558 * Set activate property of desktop; emit signal if changed.
559 */
560 void
561 SPDesktop::set_active (bool new_active)
562 {
563 if (new_active != _active) {
564 _active = new_active;
565 if (new_active) {
566 _activate_signal.emit();
567 } else {
568 _deactivate_signal.emit();
569 }
570 }
571 }
573 /**
574 * Set activate status of current desktop's named view.
575 */
576 void
577 SPDesktop::activate_guides(bool activate)
578 {
579 guides_active = activate;
580 namedview->activateGuides(this, activate);
581 }
583 /**
584 * Make desktop switch documents.
585 */
586 void
587 SPDesktop::change_document (SPDocument *theDocument)
588 {
589 g_return_if_fail (theDocument != NULL);
591 /* unselect everything before switching documents */
592 selection->clear();
594 setDocument (theDocument);
596 /* update the rulers, connect the desktop widget's signal to the new namedview etc.
597 (this can probably be done in a better way) */
598 Gtk::Window *parent = this->getToplevel();
599 g_assert(parent != NULL);
600 SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
601 if (dtw) dtw->desktop = this;
602 sp_desktop_widget_update_namedview(dtw);
604 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
605 _document_replaced_signal.emit (this, theDocument);
606 }
608 /**
609 * Make desktop switch event contexts.
610 */
611 void
612 SPDesktop::set_event_context (GtkType type, const gchar *config)
613 {
614 SPEventContext *ec;
615 while (event_context) {
616 ec = event_context;
617 sp_event_context_deactivate (ec);
618 event_context = ec->next;
619 sp_event_context_finish (ec);
620 g_object_unref (G_OBJECT (ec));
621 }
623 ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
624 ec->next = event_context;
625 event_context = ec;
626 sp_event_context_activate (ec);
627 _event_context_changed_signal.emit (this, ec);
628 }
630 /**
631 * Push event context onto desktop's context stack.
632 */
633 void
634 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
635 {
636 SPEventContext *ref, *ec;
638 if (event_context && event_context->key == key) return;
639 ref = event_context;
640 while (ref && ref->next && ref->next->key != key) ref = ref->next;
641 if (ref && ref->next) {
642 ec = ref->next;
643 ref->next = ec->next;
644 sp_event_context_finish (ec);
645 g_object_unref (G_OBJECT (ec));
646 }
648 if (event_context) sp_event_context_deactivate (event_context);
649 ec = sp_event_context_new (type, this, config, key);
650 ec->next = event_context;
651 event_context = ec;
652 sp_event_context_activate (ec);
653 _event_context_changed_signal.emit (this, ec);
654 }
656 /**
657 * Sets the coordinate status to a given point
658 */
659 void
660 SPDesktop::set_coordinate_status (Geom::Point p) {
661 _widget->setCoordinateStatus(p);
662 }
664 /**
665 * \see sp_document_item_from_list_at_point_bottom()
666 */
667 SPItem *
668 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
669 {
670 g_return_val_if_fail (doc() != NULL, NULL);
671 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
672 }
674 /**
675 * \see sp_document_item_at_point()
676 */
677 SPItem *
678 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
679 {
680 g_return_val_if_fail (doc() != NULL, NULL);
681 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
682 }
684 /**
685 * \see sp_document_group_at_point()
686 */
687 SPItem *
688 SPDesktop::group_at_point (Geom::Point const p) const
689 {
690 g_return_val_if_fail (doc() != NULL, NULL);
691 return sp_document_group_at_point (doc(), dkey, p);
692 }
694 /**
695 * \brief Returns the mouse point in document coordinates; if mouse is
696 * outside the canvas, returns the center of canvas viewpoint
697 */
698 Geom::Point
699 SPDesktop::point() const
700 {
701 Geom::Point p = _widget->getPointer();
702 Geom::Point pw = sp_canvas_window_to_world (canvas, p);
703 p = w2d(pw);
705 Geom::Rect const r = canvas->getViewbox();
707 Geom::Point r0 = w2d(r.min());
708 Geom::Point r1 = w2d(r.max());
710 if (p[Geom::X] >= r0[Geom::X] &&
711 p[Geom::X] <= r1[Geom::X] &&
712 p[Geom::Y] >= r1[Geom::Y] &&
713 p[Geom::Y] <= r0[Geom::Y])
714 {
715 return p;
716 } else {
717 return (r0 + r1) / 2;
718 }
719 }
721 /**
722 * Put current zoom data in history list.
723 */
724 void
725 SPDesktop::push_current_zoom (GList **history)
726 {
727 Geom::Rect const area = get_display_area();
729 NRRect *old_zoom = g_new(NRRect, 1);
730 old_zoom->x0 = area.min()[Geom::X];
731 old_zoom->x1 = area.max()[Geom::X];
732 old_zoom->y0 = area.min()[Geom::Y];
733 old_zoom->y1 = area.max()[Geom::Y];
734 if ( *history == NULL
735 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
736 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
737 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
738 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
739 {
740 *history = g_list_prepend (*history, old_zoom);
741 }
742 }
744 /**
745 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
746 */
747 void
748 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
749 {
750 g_assert(_widget);
752 // save the zoom
753 if (log) {
754 push_current_zoom(&zooms_past);
755 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
756 g_list_free (zooms_future);
757 zooms_future = NULL;
758 }
760 double const cx = 0.5 * (x0 + x1);
761 double const cy = 0.5 * (y0 + y1);
763 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
764 Geom::Rect viewbox = canvas->getViewbox();
765 viewbox.expandBy(-border);
767 double scale = _d2w.descrim();
768 double newscale;
769 if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
770 newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
771 } else {
772 newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
773 }
775 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
777 int clear = FALSE;
778 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
779 /* Set zoom factors */
780 _d2w = Geom::Scale(newscale, -newscale);
781 _w2d = Geom::Scale(1/newscale, 1/-newscale);
782 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
783 clear = TRUE;
784 }
786 /* Calculate top left corner (in document pixels) */
787 x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
788 y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
790 /* Scroll */
791 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
793 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
794 sp_box3d_context_update_lines(event_context);
796 _widget->updateRulers();
797 _widget->updateScrollbars(_d2w.descrim());
798 _widget->updateZoom();
799 }
801 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
802 {
803 set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
804 }
806 /**
807 * Return viewbox dimensions.
808 */
809 Geom::Rect SPDesktop::get_display_area() const
810 {
811 Geom::Rect const viewbox = canvas->getViewbox();
813 double const scale = _d2w[0];
815 return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
816 Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
817 }
819 /**
820 * Revert back to previous zoom if possible.
821 */
822 void
823 SPDesktop::prev_zoom()
824 {
825 if (zooms_past == NULL) {
826 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
827 return;
828 }
830 // push current zoom into forward zooms list
831 push_current_zoom (&zooms_future);
833 // restore previous zoom
834 set_display_area (((NRRect *) zooms_past->data)->x0,
835 ((NRRect *) zooms_past->data)->y0,
836 ((NRRect *) zooms_past->data)->x1,
837 ((NRRect *) zooms_past->data)->y1,
838 0, false);
840 // remove the just-added zoom from the past zooms list
841 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
842 }
844 /**
845 * Set zoom to next in list.
846 */
847 void
848 SPDesktop::next_zoom()
849 {
850 if (zooms_future == NULL) {
851 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
852 return;
853 }
855 // push current zoom into past zooms list
856 push_current_zoom (&zooms_past);
858 // restore next zoom
859 set_display_area (((NRRect *) zooms_future->data)->x0,
860 ((NRRect *) zooms_future->data)->y0,
861 ((NRRect *) zooms_future->data)->x1,
862 ((NRRect *) zooms_future->data)->y1,
863 0, false);
865 // remove the just-used zoom from the zooms_future list
866 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
867 }
869 #include "tools-switch.h"
870 #include "node-context.h"
871 #include "shape-editor.h"
872 #include "nodepath.h"
874 /** \brief Performs a quick zoom into what the user is working on
875 \param enable Whether we're going in or out of quick zoom
877 */
878 void
879 SPDesktop::zoom_quick (bool enable)
880 {
881 if (enable == _quick_zoom_enabled) {
882 return;
883 }
885 if (enable == true) {
886 _quick_zoom_stored_area = get_display_area();
887 bool zoomed = false;
889 if (!zoomed) {
890 SPItem * singleItem = selection->singleItem();
891 if (singleItem != NULL && tools_isactive(this, TOOLS_NODES)) {
893 Inkscape::NodePath::Path * nodepath = event_context->shape_editor->get_nodepath();
894 // printf("I've got a nodepath, crazy\n");
896 if (nodepath) {
897 Geom::Rect nodes;
898 bool firstnode = true;
900 if (nodepath->selected) {
901 for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
902 Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
903 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
904 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
905 if (node->selected) {
906 // printf("\tSelected node\n");
907 if (firstnode) {
908 nodes = Geom::Rect(node->pos, node->pos);
909 firstnode = false;
910 } else {
911 nodes.expandTo(node->pos);
912 }
914 if (node->p.other != NULL) {
915 /* Include previous node pos */
916 nodes.expandTo(node->p.other->pos);
918 /* Include previous handle */
919 if (!sp_node_side_is_line(node, &node->p)) {
920 nodes.expandTo(node->p.pos);
921 }
922 }
924 if (node->n.other != NULL) {
925 /* Include previous node pos */
926 nodes.expandTo(node->n.other->pos);
928 /* Include previous handle */
929 if (!sp_node_side_is_line(node, &node->n)) {
930 nodes.expandTo(node->n.pos);
931 }
932 }
933 }
934 }
935 }
937 if (!firstnode && nodes.area() * 2.0 < _quick_zoom_stored_area.area()) {
938 set_display_area(nodes, 10);
939 zoomed = true;
940 }
941 }
942 }
943 }
944 }
946 if (!zoomed) {
947 Geom::OptRect const d = selection->bounds();
948 if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
949 set_display_area(*d, 10);
950 zoomed = true;
951 }
952 }
954 if (!zoomed) {
955 zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
956 zoomed = true;
957 }
958 } else {
959 set_display_area(_quick_zoom_stored_area, 0);
960 }
962 _quick_zoom_enabled = enable;
963 return;
964 }
966 /**
967 * Zoom to point with absolute zoom factor.
968 */
969 void
970 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
971 {
972 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
974 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
975 // this check prevents "sliding" when trying to zoom in at maximum zoom;
976 /// \todo someone please fix calculations properly and remove this hack
977 if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
978 return;
980 Geom::Rect const viewbox = canvas->getViewbox();
982 double const width2 = viewbox.dimensions()[Geom::X] / zoom;
983 double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
985 set_display_area(cx - px * width2,
986 cy - py * height2,
987 cx + (1 - px) * width2,
988 cy + (1 - py) * height2,
989 0.0);
990 }
992 /**
993 * Zoom to center with absolute zoom factor.
994 */
995 void
996 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
997 {
998 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
999 }
1001 /**
1002 * Zoom to point with relative zoom factor.
1003 */
1004 void
1005 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
1006 {
1007 Geom::Rect const area = get_display_area();
1009 if (cx < area.min()[Geom::X]) {
1010 cx = area.min()[Geom::X];
1011 }
1012 if (cx > area.max()[Geom::X]) {
1013 cx = area.max()[Geom::X];
1014 }
1015 if (cy < area.min()[Geom::Y]) {
1016 cy = area.min()[Geom::Y];
1017 }
1018 if (cy > area.max()[Geom::Y]) {
1019 cy = area.max()[Geom::Y];
1020 }
1022 gdouble const scale = _d2w.descrim() * zoom;
1023 double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1024 double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
1026 zoom_absolute_keep_point(cx, cy, px, py, scale);
1027 }
1029 /**
1030 * Zoom to center with relative zoom factor.
1031 */
1032 void
1033 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1034 {
1035 gdouble scale = _d2w.descrim() * zoom;
1036 zoom_absolute (cx, cy, scale);
1037 }
1039 /**
1040 * Set display area to origin and current document dimensions.
1041 */
1042 void
1043 SPDesktop::zoom_page()
1044 {
1045 Geom::Rect d(Geom::Point(0, 0),
1046 Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1048 if (d.minExtent() < 1.0) {
1049 return;
1050 }
1052 set_display_area(d, 10);
1053 }
1055 /**
1056 * Set display area to current document width.
1057 */
1058 void
1059 SPDesktop::zoom_page_width()
1060 {
1061 Geom::Rect const a = get_display_area();
1063 if (sp_document_width(doc()) < 1.0) {
1064 return;
1065 }
1067 Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1068 Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1070 set_display_area(d, 10);
1071 }
1073 /**
1074 * Zoom to selection.
1075 */
1076 void
1077 SPDesktop::zoom_selection()
1078 {
1079 Geom::OptRect const d = selection->bounds();
1081 if ( !d || d->minExtent() < 0.1 ) {
1082 return;
1083 }
1085 set_display_area(*d, 10);
1086 }
1088 /**
1089 * Tell widget to let zoom widget grab keyboard focus.
1090 */
1091 void
1092 SPDesktop::zoom_grab_focus()
1093 {
1094 _widget->letZoomGrabFocus();
1095 }
1097 /**
1098 * Zoom to whole drawing.
1099 */
1100 void
1101 SPDesktop::zoom_drawing()
1102 {
1103 g_return_if_fail (doc() != NULL);
1104 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1105 g_return_if_fail (docitem != NULL);
1107 Geom::OptRect d = sp_item_bbox_desktop(docitem);
1109 /* Note that the second condition here indicates that
1110 ** there are no items in the drawing.
1111 */
1112 if ( !d || d->minExtent() < 0.1 ) {
1113 return;
1114 }
1116 set_display_area(*d, 10);
1117 }
1119 /**
1120 * Scroll canvas by specific coordinate amount in svg coordinates.
1121 */
1122 void
1123 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1124 {
1125 double scale = _d2w.descrim();
1126 scroll_world(dx*scale, dy*scale, is_scrolling);
1127 }
1129 /**
1130 * Scroll canvas by specific coordinate amount.
1131 */
1132 void
1133 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1134 {
1135 g_assert(_widget);
1137 Geom::Rect const viewbox = canvas->getViewbox();
1139 sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1141 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1142 sp_box3d_context_update_lines(event_context);
1144 _widget->updateRulers();
1145 _widget->updateScrollbars(_d2w.descrim());
1146 }
1148 bool
1149 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1150 {
1151 using Geom::X;
1152 using Geom::Y;
1154 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1155 gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1157 // autoscrolldistance is in screen pixels, but the display area is in document units
1158 autoscrolldistance /= _d2w.descrim();
1159 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1160 Geom::Rect dbox = get_display_area();
1161 dbox.expandBy(-autoscrolldistance);
1163 if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1164 !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y]) ) {
1166 Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1168 gdouble x_to;
1169 if (p[X] < dbox.min()[X])
1170 x_to = dbox.min()[X];
1171 else if (p[X] > dbox.max()[X])
1172 x_to = dbox.max()[X];
1173 else
1174 x_to = p[X];
1176 gdouble y_to;
1177 if (p[Y] < dbox.min()[Y])
1178 y_to = dbox.min()[Y];
1179 else if (p[Y] > dbox.max()[Y])
1180 y_to = dbox.max()[Y];
1181 else
1182 y_to = p[Y];
1184 Geom::Point const d_dt(x_to, y_to);
1185 Geom::Point const d_w( d_dt * _d2w );
1186 Geom::Point const moved_w( d_w - s_w );
1188 if (autoscrollspeed == 0)
1189 autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1191 if (autoscrollspeed != 0)
1192 scroll_world (autoscrollspeed * moved_w);
1194 return true;
1195 }
1196 return false;
1197 }
1199 bool
1200 SPDesktop::is_iconified()
1201 {
1202 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1203 }
1205 void
1206 SPDesktop::iconify()
1207 {
1208 _widget->setIconified();
1209 }
1211 bool
1212 SPDesktop::is_maximized()
1213 {
1214 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1215 }
1217 void
1218 SPDesktop::maximize()
1219 {
1220 _widget->setMaximized();
1221 }
1223 bool
1224 SPDesktop::is_fullscreen()
1225 {
1226 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1227 }
1229 void
1230 SPDesktop::fullscreen()
1231 {
1232 _widget->setFullscreen();
1233 }
1235 /** \brief Checks to see if the user is working in focused mode
1237 Returns the value of \c _focusMode
1238 */
1239 bool
1240 SPDesktop::is_focusMode()
1241 {
1242 return _focusMode;
1243 }
1245 /** \brief Changes whether the user is in focus mode or not
1246 \param mode Which mode the view should be in
1248 */
1249 void
1250 SPDesktop::focusMode (bool mode)
1251 {
1252 if (mode == _focusMode) { return; }
1254 _focusMode = mode;
1256 layoutWidget();
1257 //sp_desktop_widget_layout(SPDesktopWidget);
1259 return;
1260 }
1262 void
1263 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1264 {
1265 _widget->getGeometry (x, y, w, h);
1266 }
1268 void
1269 SPDesktop::setWindowPosition (Geom::Point p)
1270 {
1271 _widget->setPosition (p);
1272 }
1274 void
1275 SPDesktop::setWindowSize (gint w, gint h)
1276 {
1277 _widget->setSize (w, h);
1278 }
1280 void
1281 SPDesktop::setWindowTransient (void *p, int transient_policy)
1282 {
1283 _widget->setTransient (p, transient_policy);
1284 }
1286 Gtk::Window*
1287 SPDesktop::getToplevel( )
1288 {
1289 return _widget->getWindow();
1290 }
1292 void
1293 SPDesktop::presentWindow()
1294 {
1295 _widget->present();
1296 }
1298 bool
1299 SPDesktop::warnDialog (gchar *text)
1300 {
1301 return _widget->warnDialog (text);
1302 }
1304 void
1305 SPDesktop::toggleRulers()
1306 {
1307 _widget->toggleRulers();
1308 }
1310 void
1311 SPDesktop::toggleScrollbars()
1312 {
1313 _widget->toggleScrollbars();
1314 }
1316 void
1317 SPDesktop::layoutWidget()
1318 {
1319 _widget->layout();
1320 }
1322 void
1323 SPDesktop::destroyWidget()
1324 {
1325 _widget->destroy();
1326 }
1328 bool
1329 SPDesktop::shutdown()
1330 {
1331 return _widget->shutdown();
1332 }
1334 bool SPDesktop::onDeleteUI (GdkEventAny*)
1335 {
1336 if(shutdown())
1337 return true;
1339 destroyWidget();
1340 return false;
1341 }
1343 /**
1344 * onWindowStateEvent
1345 *
1346 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1347 * Since GTK doesn't have a way to query this state information directly, we
1348 * record it for the desktop here, and also possibly trigger a layout.
1349 */
1350 bool
1351 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1352 {
1353 // Record the desktop window's state
1354 window_state = event->new_window_state;
1356 // Layout may differ depending on full-screen mode or not
1357 GdkWindowState changed = event->changed_mask;
1358 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1359 layoutWidget();
1360 }
1362 return false;
1363 }
1365 void
1366 SPDesktop::setToolboxFocusTo (gchar const *label)
1367 {
1368 _widget->setToolboxFocusTo (label);
1369 }
1371 void
1372 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1373 {
1374 _widget->setToolboxAdjustmentValue (id, val);
1375 }
1377 void
1378 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1379 {
1380 _widget->setToolboxSelectOneValue (id, val);
1381 }
1383 bool
1384 SPDesktop::isToolboxButtonActive (gchar const *id)
1385 {
1386 return _widget->isToolboxButtonActive (id);
1387 }
1389 void
1390 SPDesktop::emitToolSubselectionChanged(gpointer data)
1391 {
1392 _tool_subselection_changed.emit(data);
1393 inkscape_subselection_changed (this);
1394 }
1396 void
1397 SPDesktop::updateNow()
1398 {
1399 sp_canvas_update_now(canvas);
1400 }
1402 void
1403 SPDesktop::enableInteraction()
1404 {
1405 _widget->enableInteraction();
1406 }
1408 void SPDesktop::disableInteraction()
1409 {
1410 _widget->disableInteraction();
1411 }
1413 void SPDesktop::setWaitingCursor()
1414 {
1415 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1416 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1417 gdk_cursor_unref(waiting);
1418 // GDK needs the flush for the cursor change to take effect
1419 gdk_flush();
1420 waiting_cursor = true;
1421 }
1423 void SPDesktop::clearWaitingCursor()
1424 {
1425 if (waiting_cursor)
1426 sp_event_context_update_cursor(sp_desktop_event_context(this));
1427 }
1429 void SPDesktop::toggleColorProfAdjust()
1430 {
1431 _widget->toggleColorProfAdjust();
1432 }
1434 void SPDesktop::toggleGrids()
1435 {
1436 if (namedview->grids) {
1437 if(gridgroup) {
1438 showGrids(!grids_visible);
1439 }
1440 } else {
1441 //there is no grid present at the moment. add a rectangular grid and make it visible
1442 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1443 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1444 showGrids(true);
1445 }
1446 }
1448 void SPDesktop::showGrids(bool show, bool dirty_document)
1449 {
1450 grids_visible = show;
1451 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1452 if (show) {
1453 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1454 } else {
1455 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1456 }
1457 }
1459 void SPDesktop::toggleSnapGlobal()
1460 {
1461 bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1462 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1463 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1464 }
1466 //----------------------------------------------------------------------
1467 // Callback implementations. The virtual ones are connected by the view.
1469 void
1470 SPDesktop::onPositionSet (double x, double y)
1471 {
1472 _widget->viewSetPosition (Geom::Point(x,y));
1473 }
1475 void
1476 SPDesktop::onResized (double /*x*/, double /*y*/)
1477 {
1478 // Nothing called here
1479 }
1481 /**
1482 * Redraw callback; queues Gtk redraw; connected by View.
1483 */
1484 void
1485 SPDesktop::onRedrawRequested ()
1486 {
1487 if (main) {
1488 _widget->requestCanvasUpdate();
1489 }
1490 }
1492 void
1493 SPDesktop::updateCanvasNow()
1494 {
1495 _widget->requestCanvasUpdateAndWait();
1496 }
1498 /**
1499 * Associate document with desktop.
1500 */
1501 void
1502 SPDesktop::setDocument (SPDocument *doc)
1503 {
1504 if (this->doc() && doc) {
1505 namedview->hide(this);
1506 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1507 }
1509 if (_layer_hierarchy) {
1510 _layer_hierarchy->clear();
1511 delete _layer_hierarchy;
1512 }
1513 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1514 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1515 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1516 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1517 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1519 /* setup EventLog */
1520 event_log = new Inkscape::EventLog(doc);
1521 doc->addUndoObserver(*event_log);
1523 _commit_connection.disconnect();
1524 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1526 /// \todo fixme: This condition exists to make sure the code
1527 /// inside is NOT called on initialization, only on replacement. But there
1528 /// are surely more safe methods to accomplish this.
1529 // TODO since the comment had reversed logic, check the intent of this block of code:
1530 if (drawing) {
1531 NRArenaItem *ai = 0;
1533 namedview = sp_document_namedview (doc, NULL);
1534 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1535 number = namedview->getViewCount();
1537 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1538 SP_CANVAS_ARENA (drawing)->arena,
1539 dkey,
1540 SP_ITEM_SHOW_DISPLAY);
1541 if (ai) {
1542 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1543 }
1544 namedview->show(this);
1545 /* Ugly hack */
1546 activate_guides (true);
1547 /* Ugly hack */
1548 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1549 }
1551 _document_replaced_signal.emit (this, doc);
1553 View::setDocument (doc);
1554 }
1556 void
1557 SPDesktop::onStatusMessage
1558 (Inkscape::MessageType type, gchar const *message)
1559 {
1560 if (_widget) {
1561 _widget->setMessage(type, message);
1562 }
1563 }
1565 void
1566 SPDesktop::onDocumentURISet (gchar const* uri)
1567 {
1568 _widget->setTitle(uri);
1569 }
1571 /**
1572 * Resized callback.
1573 */
1574 void
1575 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1576 {
1577 _doc2dt[5] = height;
1578 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1579 Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1580 SP_CTRLRECT(page)->setRectangle(a);
1581 SP_CTRLRECT(page_border)->setRectangle(a);
1582 }
1585 void
1586 SPDesktop::_onActivate (SPDesktop* dt)
1587 {
1588 if (!dt->_widget) return;
1589 dt->_widget->activateDesktop();
1590 }
1592 void
1593 SPDesktop::_onDeactivate (SPDesktop* dt)
1594 {
1595 if (!dt->_widget) return;
1596 dt->_widget->deactivateDesktop();
1597 }
1599 void
1600 SPDesktop::_onSelectionModified
1601 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1602 {
1603 if (!dt->_widget) return;
1604 dt->_widget->updateScrollbars (dt->_d2w.descrim());
1605 }
1607 static void
1608 _onSelectionChanged
1609 (Inkscape::Selection *selection, SPDesktop *desktop)
1610 {
1611 /** \todo
1612 * only change the layer for single selections, or what?
1613 * This seems reasonable -- for multiple selections there can be many
1614 * different layers involved.
1615 */
1616 SPItem *item=selection->singleItem();
1617 if (item) {
1618 SPObject *layer=desktop->layerForObject(item);
1619 if ( layer && layer != desktop->currentLayer() ) {
1620 desktop->setCurrentLayer(layer);
1621 }
1622 }
1623 }
1625 /**
1626 * Calls event handler of current event context.
1627 * \param arena Unused
1628 * \todo fixme
1629 */
1630 static gint
1631 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1632 {
1633 if (ai) {
1634 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1635 return sp_event_context_item_handler (desktop->event_context, spi, event);
1636 } else {
1637 return sp_event_context_root_handler (desktop->event_context, event);
1638 }
1639 }
1641 static void
1642 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1643 g_return_if_fail(SP_IS_GROUP(layer));
1644 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1645 }
1647 /// Callback
1648 static void
1649 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1650 g_return_if_fail(SP_IS_GROUP(layer));
1651 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1652 }
1654 /// Callback
1655 static void
1656 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1657 SPDesktop *desktop)
1658 {
1659 desktop->_layer_changed_signal.emit (bottom);
1660 }
1662 /// Called when document is starting to be rebuilt.
1663 static void
1664 _reconstruction_start (SPDesktop * desktop)
1665 {
1666 // printf("Desktop, starting reconstruction\n");
1667 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1668 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1670 /*
1671 GSList const * selection_objs = desktop->selection->list();
1672 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1674 }
1675 */
1676 desktop->selection->clear();
1678 // printf("Desktop, starting reconstruction end\n");
1679 }
1681 /// Called when document rebuild is finished.
1682 static void
1683 _reconstruction_finish (SPDesktop * desktop)
1684 {
1685 // printf("Desktop, finishing reconstruction\n");
1686 if (desktop->_reconstruction_old_layer_id == NULL)
1687 return;
1689 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1690 if (newLayer != NULL)
1691 desktop->setCurrentLayer(newLayer);
1693 g_free(desktop->_reconstruction_old_layer_id);
1694 desktop->_reconstruction_old_layer_id = NULL;
1695 // printf("Desktop, finishing reconstruction end\n");
1696 return;
1697 }
1699 /**
1700 * Namedview_modified callback.
1701 */
1702 static void
1703 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1704 {
1705 SPNamedView *nv=SP_NAMEDVIEW(obj);
1707 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1709 /* Show/hide page background */
1710 if (nv->pagecolor & 0xff) {
1711 sp_canvas_item_show (desktop->table);
1712 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1713 sp_canvas_item_move_to_z (desktop->table, 0);
1714 } else {
1715 sp_canvas_item_hide (desktop->table);
1716 }
1718 /* Show/hide page border */
1719 if (nv->showborder) {
1720 // show
1721 sp_canvas_item_show (desktop->page_border);
1722 // set color and shadow
1723 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1724 if (nv->pageshadow) {
1725 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1726 }
1727 // place in the z-order stack
1728 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1729 sp_canvas_item_move_to_z (desktop->page_border, 2);
1730 } else {
1731 int order = sp_canvas_item_order (desktop->page_border);
1732 int morder = sp_canvas_item_order (desktop->drawing);
1733 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1734 morder - order);
1735 }
1736 } else {
1737 sp_canvas_item_hide (desktop->page_border);
1738 if (nv->pageshadow) {
1739 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1740 }
1741 }
1743 /* Show/hide page shadow */
1744 if (nv->showpageshadow && nv->pageshadow) {
1745 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1746 } else {
1747 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1748 }
1750 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1751 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1752 (SP_RGBA32_R_U(nv->pagecolor) +
1753 SP_RGBA32_G_U(nv->pagecolor) +
1754 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1755 // the background color is light or transparent, use black outline
1756 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1757 } else { // use white outline
1758 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1759 }
1760 }
1761 }
1763 Geom::Matrix SPDesktop::w2d() const
1764 {
1765 return _w2d;
1766 }
1768 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1769 {
1770 return p * _w2d;
1771 }
1773 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1774 {
1775 return p * _d2w;
1776 }
1778 Geom::Matrix SPDesktop::doc2dt() const
1779 {
1780 return _doc2dt;
1781 }
1783 Geom::Matrix SPDesktop::dt2doc() const
1784 {
1785 // doc2dt is its own inverse
1786 return _doc2dt;
1787 }
1789 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1790 {
1791 return p * _doc2dt;
1792 }
1794 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1795 {
1796 return p * dt2doc();
1797 }
1800 /**
1801 * Pop event context from desktop's context stack. Never used.
1802 */
1803 // void
1804 // SPDesktop::pop_event_context (unsigned int key)
1805 // {
1806 // SPEventContext *ec = NULL;
1807 //
1808 // if (event_context && event_context->key == key) {
1809 // g_return_if_fail (event_context);
1810 // g_return_if_fail (event_context->next);
1811 // ec = event_context;
1812 // sp_event_context_deactivate (ec);
1813 // event_context = ec->next;
1814 // sp_event_context_activate (event_context);
1815 // _event_context_changed_signal.emit (this, ec);
1816 // }
1817 //
1818 // SPEventContext *ref = event_context;
1819 // while (ref && ref->next && ref->next->key != key)
1820 // ref = ref->next;
1821 //
1822 // if (ref && ref->next) {
1823 // ec = ref->next;
1824 // ref->next = ec->next;
1825 // }
1826 //
1827 // if (ec) {
1828 // sp_event_context_finish (ec);
1829 // g_object_unref (G_OBJECT (ec));
1830 // }
1831 // }
1833 /*
1834 Local Variables:
1835 mode:c++
1836 c-file-style:"stroustrup"
1837 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1838 indent-tabs-mode:nil
1839 fill-column:99
1840 End:
1841 */
1842 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :