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