1 /** \file
2 * Editable view implementation
3 *
4 * Authors:
5 * Lauris Kaplinski <lauris@kaplinski.com>
6 * MenTaLguY <mental@rydia.net>
7 * bulia byak <buliabyak@users.sf.net>
8 * Ralf Stephan <ralf@ark.in-berlin.de>
9 * John Bintz <jcoswell@coswellproductions.org>
10 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
11 * Jon A. Cruz <jon@joncruz.org>
12 * Abhishek Sharma
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 "display/sp-canvas-group.h"
84 #include "ui/dialog/dialog-manager.h"
85 #include "xml/repr.h"
86 #include "message-context.h"
87 #include "device-manager.h"
88 #include "layer-fns.h"
89 #include "layer-manager.h"
90 #include "event-log.h"
91 #include "display/canvas-grid.h"
92 #include "widgets/desktop-widget.h"
93 #include "box3d-context.h"
94 #include "desktop-style.h"
96 // TODO those includes are only for node tool quick zoom. Remove them after fixing it.
97 #include "ui/tool/node-tool.h"
98 #include "ui/tool/control-point-selection.h"
100 #include "display/sp-canvas.h"
102 namespace Inkscape { namespace XML { class Node; }}
104 // Callback declarations
105 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
106 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
107 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
108 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
109 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
110 static void _reconstruction_start(SPDesktop * desktop);
111 static void _reconstruction_finish(SPDesktop * desktop);
112 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
114 /**
115 * Return new desktop object.
116 * \pre namedview != NULL.
117 * \pre canvas != NULL.
118 */
119 SPDesktop::SPDesktop() :
120 _dlg_mgr( 0 ),
121 namedview( 0 ),
122 canvas( 0 ),
123 selection( 0 ),
124 event_context( 0 ),
125 layer_manager( 0 ),
126 event_log( 0 ),
127 temporary_item_list( 0 ),
128 snapindicator( 0 ),
129 acetate( 0 ),
130 main( 0 ),
131 gridgroup( 0 ),
132 guides( 0 ),
133 drawing( 0 ),
134 sketch( 0 ),
135 controls( 0 ),
136 tempgroup ( 0 ),
137 table( 0 ),
138 page( 0 ),
139 page_border( 0 ),
140 current( 0 ),
141 _focusMode(false),
142 zooms_past( 0 ),
143 zooms_future( 0 ),
144 dkey( 0 ),
145 number( 0 ),
146 window_state(0),
147 interaction_disabled_counter( 0 ),
148 waiting_cursor( false ),
149 guides_active( false ),
150 gr_item( 0 ),
151 gr_point_type( 0 ),
152 gr_point_i( 0 ),
153 gr_fill_or_stroke( true ),
154 _layer_hierarchy( 0 ),
155 _reconstruction_old_layer_id( 0 ),
156 _display_mode(Inkscape::RENDERMODE_NORMAL),
157 _widget( 0 ),
158 _inkscape( 0 ),
159 _guides_message_context( 0 ),
160 _active( false ),
161 _w2d(),
162 _d2w(),
163 _doc2dt( Geom::Scale(1, -1) ),
164 grids_visible( false )
165 {
166 _d2w.setIdentity();
167 _w2d.setIdentity();
169 selection = Inkscape::GC::release( new Inkscape::Selection(this) );
170 }
172 void
173 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWidgetInterface *widget)
174 {
175 _widget = widget;
177 // Temporary workaround for link order issues:
178 Inkscape::DeviceManager::getManager().getDevices();
179 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
181 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
183 current = prefs->getStyle("/desktop/style");
185 namedview = nv;
186 canvas = aCanvas;
188 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
189 /* Kill flicker */
190 document->ensureUpToDate();
192 /* Setup Dialog Manager */
193 _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
195 dkey = SPItem::display_key_new(1);
197 /* Connect document */
198 setDocument (document);
200 number = namedview->getViewCount();
203 /* Setup Canvas */
204 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
206 SPCanvasGroup *root = sp_canvas_root (canvas);
208 /* Setup adminstrative layers */
209 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
210 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
211 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
212 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
214 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
215 SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
216 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
217 sp_canvas_item_move_to_z (table, 0);
219 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
220 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
221 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
223 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
224 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
226 SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
228 if (prefs->getBool("/options/startmode/outline")) {
229 // Start in outline mode
230 setDisplayModeOutline();
231 } else {
232 // Start in normal mode, default
233 setDisplayModeNormal();
234 }
236 // The order in which these canvas items are added determines the z-order. It's therefore
237 // important to add the tempgroup (which will contain the snapindicator) before adding the
238 // controls. Only this way one will be able to quickly (before the snap indicator has
239 // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
240 // will not work (the snap indicator is on top of the node handler; is the snapindicator
241 // being selected? or does it intercept some of the events that should have gone to the
242 // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
243 gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
244 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
245 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
246 tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
247 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
249 /* Push select tool to the bottom of stack */
250 /** \todo
251 * FIXME: this is the only call to this. Everything else seems to just
252 * call "set" instead of "push". Can we assume that there is only one
253 * context ever?
254 */
255 push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
257 // display rect and zoom are now handled in sp_desktop_widget_realize()
259 Geom::Rect const d(Geom::Point(0.0, 0.0),
260 Geom::Point(document->getWidth(), document->getHeight()));
262 SP_CTRLRECT(page)->setRectangle(d);
263 SP_CTRLRECT(page_border)->setRectangle(d);
265 /* the following sets the page shadow on the canvas
266 It was originally set to 5, which is really cheesy!
267 It now is an attribute in the document's namedview. If a value of
268 0 is used, then the constructor for a shadow is not initialized.
269 */
271 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
272 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
273 }
276 /* Connect event for page resize */
277 _doc2dt[5] = document->getHeight();
278 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
280 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
282 NRArenaItem *ai = SP_ITEM(document->getRoot())->invoke_show(
283 SP_CANVAS_ARENA (drawing)->arena,
284 dkey,
285 SP_ITEM_SHOW_DISPLAY);
286 if (ai) {
287 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
288 }
290 namedview->show(this);
291 /* Ugly hack */
292 activate_guides (true);
293 /* Ugly hack */
294 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
296 /* Set up notification of rebuilding the document, this allows
297 for saving object related settings in the document. */
298 _reconstruction_start_connection =
299 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
300 _reconstruction_finish_connection =
301 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
302 _reconstruction_old_layer_id = NULL;
304 // ?
305 // sp_active_desktop_set (desktop);
306 _inkscape = INKSCAPE;
308 _activate_connection = _activate_signal.connect(
309 sigc::bind(
310 sigc::ptr_fun(_onActivate),
311 this
312 )
313 );
314 _deactivate_connection = _deactivate_signal.connect(
315 sigc::bind(
316 sigc::ptr_fun(_onDeactivate),
317 this
318 )
319 );
321 _sel_modified_connection = selection->connectModified(
322 sigc::bind(
323 sigc::ptr_fun(&_onSelectionModified),
324 this
325 )
326 );
327 _sel_changed_connection = selection->connectChanged(
328 sigc::bind(
329 sigc::ptr_fun(&_onSelectionChanged),
330 this
331 )
332 );
335 /* setup LayerManager */
336 // (Setting up after the connections are all in place, as it may use some of them)
337 layer_manager = new Inkscape::LayerManager( this );
339 showGrids(namedview->grids_visible, false);
341 temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
342 snapindicator = new Inkscape::Display::SnapIndicator ( this );
343 }
346 void SPDesktop::destroy()
347 {
348 if (snapindicator) {
349 delete snapindicator;
350 snapindicator = NULL;
351 }
352 if (temporary_item_list) {
353 delete temporary_item_list;
354 temporary_item_list = NULL;
355 }
357 if (selection) {
358 delete selection;
359 selection = NULL;
360 }
362 namedview->hide(this);
364 _activate_connection.disconnect();
365 _deactivate_connection.disconnect();
366 _sel_modified_connection.disconnect();
367 _sel_changed_connection.disconnect();
368 _modified_connection.disconnect();
369 _commit_connection.disconnect();
370 _reconstruction_start_connection.disconnect();
371 _reconstruction_finish_connection.disconnect();
373 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
374 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
375 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
377 while (event_context) {
378 SPEventContext *ec = event_context;
379 event_context = ec->next;
380 sp_event_context_finish (ec);
381 g_object_unref (G_OBJECT (ec));
382 }
384 if (_layer_hierarchy) {
385 delete _layer_hierarchy;
386 // _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
387 }
389 if (layer_manager) {
390 delete layer_manager;
391 layer_manager = NULL;
392 }
394 if (_inkscape) {
395 _inkscape = NULL;
396 }
398 if (drawing) {
399 SP_ITEM(doc()->getRoot())->invoke_hide(dkey);
400 drawing = NULL;
401 }
403 delete _guides_message_context;
404 _guides_message_context = NULL;
406 g_list_free (zooms_past);
407 g_list_free (zooms_future);
408 }
410 SPDesktop::~SPDesktop() {}
412 //--------------------------------------------------------------------
413 /* Public methods */
416 /* These methods help for temporarily showing things on-canvas.
417 * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
418 * is when you want to prematurely remove the item from the canvas, by calling
419 * desktop->remove_temporary_canvasitem(tempitem).
420 */
421 /** Note that lifetime is measured in milliseconds
422 * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
423 * delete the object for you and the reference will become invalid without you knowing it.
424 * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
425 * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
426 * because the object might be deleted already without you knowing it.
427 * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
428 */
429 Inkscape::Display::TemporaryItem *
430 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
431 {
432 if (move_to_bottom) {
433 sp_canvas_item_move_to_z(item, 0);
434 }
436 return temporary_item_list->add_item(item, lifetime);
437 }
439 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
440 */
441 void
442 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
443 {
444 // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
445 if (tempitem && temporary_item_list) {
446 temporary_item_list->delete_item(tempitem);
447 }
448 }
450 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
451 SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
452 canvas->rendermode = mode;
453 _display_mode = mode;
454 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
455 _widget->setTitle( sp_desktop_document(this)->getName() );
456 }
458 void SPDesktop::displayModeToggle() {
459 switch (_display_mode) {
460 case Inkscape::RENDERMODE_NORMAL:
461 _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
462 break;
463 case Inkscape::RENDERMODE_NO_FILTERS:
464 _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
465 break;
466 case Inkscape::RENDERMODE_OUTLINE:
467 _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
468 break;
469 // case Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW:
470 default:
471 _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
472 }
473 }
475 /**
476 * Returns current root (=bottom) layer.
477 */
478 SPObject *SPDesktop::currentRoot() const
479 {
480 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
481 }
483 /**
484 * Returns current top layer.
485 */
486 SPObject *SPDesktop::currentLayer() const
487 {
488 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
489 }
491 /**
492 * Sets the current layer of the desktop.
493 *
494 * Make \a object the top layer.
495 */
496 void SPDesktop::setCurrentLayer(SPObject *object) {
497 g_return_if_fail(SP_IS_GROUP(object));
498 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
499 // printf("Set Layer to ID: %s\n", object->getId());
500 _layer_hierarchy->setBottom(object);
501 }
503 void SPDesktop::toggleLayerSolo(SPObject *object) {
504 g_return_if_fail(SP_IS_GROUP(object));
505 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
507 bool othersShowing = false;
508 std::vector<SPObject*> layers;
509 for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
510 layers.push_back(obj);
511 othersShowing |= !SP_ITEM(obj)->isHidden();
512 }
513 for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
514 layers.push_back(obj);
515 othersShowing |= !SP_ITEM(obj)->isHidden();
516 }
519 if ( SP_ITEM(object)->isHidden() ) {
520 SP_ITEM(object)->setHidden(false);
521 }
523 for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
524 SP_ITEM(*it)->setHidden(othersShowing);
525 }
526 }
528 /**
529 * Return layer that contains \a object.
530 */
531 SPObject *SPDesktop::layerForObject(SPObject *object) {
532 g_return_val_if_fail(object != NULL, NULL);
534 SPObject *root=currentRoot();
535 object = SP_OBJECT_PARENT(object);
536 while ( object && object != root && !isLayer(object) ) {
537 object = SP_OBJECT_PARENT(object);
538 }
539 return object;
540 }
542 /**
543 * True if object is a layer.
544 */
545 bool SPDesktop::isLayer(SPObject *object) const {
546 return ( SP_IS_GROUP(object)
547 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
548 == SPGroup::LAYER ) );
549 }
551 /**
552 * True if desktop viewport fully contains \a item's bbox.
553 */
554 bool SPDesktop::isWithinViewport (SPItem *item) const
555 {
556 Geom::Rect const viewport = get_display_area();
557 Geom::OptRect const bbox = item->getBboxDesktop();
558 if (bbox) {
559 return viewport.contains(*bbox);
560 } else {
561 return true;
562 }
563 }
565 ///
566 bool SPDesktop::itemIsHidden(SPItem const *item) const {
567 return item->isHidden(this->dkey);
568 }
570 /**
571 * Set activate property of desktop; emit signal if changed.
572 */
573 void
574 SPDesktop::set_active (bool new_active)
575 {
576 if (new_active != _active) {
577 _active = new_active;
578 if (new_active) {
579 _activate_signal.emit();
580 } else {
581 _deactivate_signal.emit();
582 }
583 }
584 }
586 /**
587 * Set activate status of current desktop's named view.
588 */
589 void
590 SPDesktop::activate_guides(bool activate)
591 {
592 guides_active = activate;
593 namedview->activateGuides(this, activate);
594 }
596 /**
597 * Make desktop switch documents.
598 */
599 void
600 SPDesktop::change_document (SPDocument *theDocument)
601 {
602 g_return_if_fail (theDocument != NULL);
604 /* unselect everything before switching documents */
605 selection->clear();
607 setDocument (theDocument);
609 /* update the rulers, connect the desktop widget's signal to the new namedview etc.
610 (this can probably be done in a better way) */
611 Gtk::Window *parent = this->getToplevel();
612 g_assert(parent != NULL);
613 SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
614 if (dtw) {
615 dtw->desktop = this;
616 }
617 dtw->updateNamedview();
619 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
620 _document_replaced_signal.emit (this, theDocument);
621 }
623 /**
624 * Make desktop switch event contexts.
625 */
626 void
627 SPDesktop::set_event_context (GtkType type, const gchar *config)
628 {
629 SPEventContext *ec;
630 while (event_context) {
631 ec = event_context;
632 sp_event_context_deactivate (ec);
633 // we have to keep event_context valid during destruction - otherwise writing
634 // destructors is next to impossible
635 SPEventContext *next = ec->next;
636 sp_event_context_finish (ec);
637 g_object_unref (G_OBJECT (ec));
638 event_context = next;
639 }
641 // The event_context will be null. This means that it will be impossible
642 // to process any event invoked by the lines below. See for example bug
643 // LP #622350. Cutting and undoing again in the node tool resets the event
644 // context to the node tool. In this bug the line bellow invokes GDK_LEAVE_NOTIFY
645 // events which cannot be handled and must be discarded.
646 ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
647 ec->next = event_context;
648 event_context = ec;
649 // Now the event_context has been set again and we can process all events again
650 sp_event_context_activate (ec);
651 _event_context_changed_signal.emit (this, ec);
652 }
654 /**
655 * Push event context onto desktop's context stack.
656 */
657 void
658 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
659 {
660 SPEventContext *ref, *ec;
662 if (event_context && event_context->key == key) return;
663 ref = event_context;
664 while (ref && ref->next && ref->next->key != key) ref = ref->next;
665 if (ref && ref->next) {
666 ec = ref->next;
667 ref->next = ec->next;
668 sp_event_context_finish (ec);
669 g_object_unref (G_OBJECT (ec));
670 }
672 if (event_context) sp_event_context_deactivate (event_context);
673 ec = sp_event_context_new (type, this, config, key);
674 ec->next = event_context;
675 event_context = ec;
676 sp_event_context_activate (ec);
677 _event_context_changed_signal.emit (this, ec);
678 }
680 /**
681 * Sets the coordinate status to a given point
682 */
683 void
684 SPDesktop::set_coordinate_status (Geom::Point p) {
685 _widget->setCoordinateStatus(p);
686 }
688 /**
689 * \see SPDocument::getItemFromListAtPointBottom()
690 */
691 SPItem *SPDesktop::getItemFromListAtPointBottom(const GSList *list, Geom::Point const p) const
692 {
693 g_return_val_if_fail (doc() != NULL, NULL);
694 return SPDocument::getItemFromListAtPointBottom(dkey, SP_GROUP (doc()->root), list, p);
695 }
697 /**
698 * \see SPDocument::getItemAtPoint()
699 */
700 SPItem *SPDesktop::getItemAtPoint(Geom::Point const p, bool into_groups, SPItem *upto) const
701 {
702 g_return_val_if_fail (doc() != NULL, NULL);
703 return doc()->getItemAtPoint( dkey, p, into_groups, upto);
704 }
706 /**
707 * \see SPDocument::getGroupAtPoint()
708 */
709 SPItem *SPDesktop::getGroupAtPoint(Geom::Point const p) const
710 {
711 g_return_val_if_fail (doc() != NULL, NULL);
712 return doc()->getGroupAtPoint(dkey, p);
713 }
715 /**
716 * \brief Returns the mouse point in document coordinates; if mouse is
717 * outside the canvas, returns the center of canvas viewpoint
718 */
719 Geom::Point
720 SPDesktop::point() const
721 {
722 Geom::Point p = _widget->getPointer();
723 Geom::Point pw = sp_canvas_window_to_world (canvas, p);
724 p = w2d(pw);
726 Geom::Rect const r = canvas->getViewbox();
728 Geom::Point r0 = w2d(r.min());
729 Geom::Point r1 = w2d(r.max());
731 if (p[Geom::X] >= r0[Geom::X] &&
732 p[Geom::X] <= r1[Geom::X] &&
733 p[Geom::Y] >= r1[Geom::Y] &&
734 p[Geom::Y] <= r0[Geom::Y])
735 {
736 return p;
737 } else {
738 return (r0 + r1) / 2;
739 }
740 }
742 /**
743 * Put current zoom data in history list.
744 */
745 void
746 SPDesktop::push_current_zoom (GList **history)
747 {
748 Geom::Rect const area = get_display_area();
750 NRRect *old_zoom = g_new(NRRect, 1);
751 old_zoom->x0 = area.min()[Geom::X];
752 old_zoom->x1 = area.max()[Geom::X];
753 old_zoom->y0 = area.min()[Geom::Y];
754 old_zoom->y1 = area.max()[Geom::Y];
755 if ( *history == NULL
756 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
757 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
758 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
759 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
760 {
761 *history = g_list_prepend (*history, old_zoom);
762 }
763 }
765 /**
766 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
767 */
768 void
769 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
770 {
771 g_assert(_widget);
773 // save the zoom
774 if (log) {
775 push_current_zoom(&zooms_past);
776 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
777 g_list_free (zooms_future);
778 zooms_future = NULL;
779 }
781 double const cx = 0.5 * (x0 + x1);
782 double const cy = 0.5 * (y0 + y1);
784 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
785 Geom::Rect viewbox = canvas->getViewbox();
786 viewbox.expandBy(-border);
788 double scale = _d2w.descrim();
789 double newscale;
790 if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
791 newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
792 } else {
793 newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
794 }
796 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
798 int clear = FALSE;
799 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
800 // zoom changed - set new zoom factors
801 _d2w = Geom::Scale(newscale, -newscale);
802 _w2d = Geom::Scale(1/newscale, 1/-newscale);
803 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
804 clear = TRUE;
805 signal_zoom_changed.emit(_d2w.descrim());
806 }
808 /* Calculate top left corner (in document pixels) */
809 x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
810 y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
812 /* Scroll */
813 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
815 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
816 sp_box3d_context_update_lines(event_context);
818 _widget->updateRulers();
819 _widget->updateScrollbars(_d2w.descrim());
820 _widget->updateZoom();
821 }
823 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
824 {
825 set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
826 }
828 /**
829 * Return viewbox dimensions.
830 */
831 Geom::Rect SPDesktop::get_display_area() const
832 {
833 Geom::Rect const viewbox = canvas->getViewbox();
835 double const scale = _d2w[0];
837 return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
838 Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
839 }
841 /**
842 * Revert back to previous zoom if possible.
843 */
844 void
845 SPDesktop::prev_zoom()
846 {
847 if (zooms_past == NULL) {
848 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
849 return;
850 }
852 // push current zoom into forward zooms list
853 push_current_zoom (&zooms_future);
855 // restore previous zoom
856 set_display_area (((NRRect *) zooms_past->data)->x0,
857 ((NRRect *) zooms_past->data)->y0,
858 ((NRRect *) zooms_past->data)->x1,
859 ((NRRect *) zooms_past->data)->y1,
860 0, false);
862 // remove the just-added zoom from the past zooms list
863 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
864 }
866 /**
867 * Set zoom to next in list.
868 */
869 void
870 SPDesktop::next_zoom()
871 {
872 if (zooms_future == NULL) {
873 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
874 return;
875 }
877 // push current zoom into past zooms list
878 push_current_zoom (&zooms_past);
880 // restore next zoom
881 set_display_area (((NRRect *) zooms_future->data)->x0,
882 ((NRRect *) zooms_future->data)->y0,
883 ((NRRect *) zooms_future->data)->x1,
884 ((NRRect *) zooms_future->data)->y1,
885 0, false);
887 // remove the just-used zoom from the zooms_future list
888 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
889 }
891 /** \brief Performs a quick zoom into what the user is working on
892 \param enable Whether we're going in or out of quick zoom
894 */
895 void
896 SPDesktop::zoom_quick (bool enable)
897 {
898 if (enable == _quick_zoom_enabled) {
899 return;
900 }
902 if (enable == true) {
903 _quick_zoom_stored_area = get_display_area();
904 bool zoomed = false;
906 // TODO This needs to migrate into the node tool, but currently the design
907 // of this method is sufficiently wrong to prevent this.
908 if (!zoomed && INK_IS_NODE_TOOL(event_context)) {
909 InkNodeTool *nt = static_cast<InkNodeTool*>(event_context);
910 if (!nt->_selected_nodes->empty()) {
911 Geom::Rect nodes = *nt->_selected_nodes->bounds();
912 double area = nodes.area();
913 // do not zoom if a single cusp node is selected aand the bounds
914 // have zero area.
915 if (!Geom::are_near(area, 0) && area * 2.0 < _quick_zoom_stored_area.area()) {
916 set_display_area(nodes, true);
917 zoomed = true;
918 }
919 }
920 }
922 if (!zoomed) {
923 Geom::OptRect const d = selection->bounds();
924 if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
925 set_display_area(*d, true);
926 zoomed = true;
927 }
928 }
930 if (!zoomed) {
931 zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
932 zoomed = true;
933 }
934 } else {
935 set_display_area(_quick_zoom_stored_area, false);
936 }
938 _quick_zoom_enabled = enable;
939 return;
940 }
942 /**
943 * Zoom to point with absolute zoom factor.
944 */
945 void
946 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
947 {
948 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
950 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
951 // this check prevents "sliding" when trying to zoom in at maximum zoom;
952 /// \todo someone please fix calculations properly and remove this hack
953 if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
954 return;
956 Geom::Rect const viewbox = canvas->getViewbox();
958 double const width2 = viewbox.dimensions()[Geom::X] / zoom;
959 double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
961 set_display_area(cx - px * width2,
962 cy - py * height2,
963 cx + (1 - px) * width2,
964 cy + (1 - py) * height2,
965 0.0);
966 }
968 /**
969 * Apply the desktop's current style or the tool style to the object.
970 */
971 void SPDesktop::applyCurrentOrToolStyle(SPObject *obj, Glib::ustring const &tool_path, bool with_text)
972 {
973 SPCSSAttr *css_current = sp_desktop_get_style(this, with_text);
974 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
976 if (prefs->getBool(tool_path + "/usecurrent") && css_current) {
977 obj->setCSS(css_current,"style");
978 } else {
979 SPCSSAttr *css = prefs->getInheritedStyle(tool_path + "/style");
980 obj->setCSS(css,"style");
981 sp_repr_css_attr_unref(css);
982 }
983 if (css_current) {
984 sp_repr_css_attr_unref(css_current);
985 }
986 }
988 /**
989 * Zoom to center with absolute zoom factor.
990 */
991 void
992 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
993 {
994 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
995 }
997 /**
998 * Zoom to point with relative zoom factor.
999 */
1000 void
1001 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
1002 {
1003 Geom::Rect const area = get_display_area();
1005 if (cx < area.min()[Geom::X]) {
1006 cx = area.min()[Geom::X];
1007 }
1008 if (cx > area.max()[Geom::X]) {
1009 cx = area.max()[Geom::X];
1010 }
1011 if (cy < area.min()[Geom::Y]) {
1012 cy = area.min()[Geom::Y];
1013 }
1014 if (cy > area.max()[Geom::Y]) {
1015 cy = area.max()[Geom::Y];
1016 }
1018 gdouble const scale = _d2w.descrim() * zoom;
1019 double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1020 double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
1022 zoom_absolute_keep_point(cx, cy, px, py, scale);
1023 }
1025 /**
1026 * Zoom to center with relative zoom factor.
1027 */
1028 void
1029 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1030 {
1031 gdouble scale = _d2w.descrim() * zoom;
1032 zoom_absolute (cx, cy, scale);
1033 }
1035 /**
1036 * Set display area to origin and current document dimensions.
1037 */
1038 void
1039 SPDesktop::zoom_page()
1040 {
1041 Geom::Rect d(Geom::Point(0, 0),
1042 Geom::Point(doc()->getWidth(), doc()->getHeight()));
1044 if (d.minExtent() < 1.0) {
1045 return;
1046 }
1048 set_display_area(d, 10);
1049 }
1051 /**
1052 * Set display area to current document width.
1053 */
1054 void
1055 SPDesktop::zoom_page_width()
1056 {
1057 Geom::Rect const a = get_display_area();
1059 if (doc()->getWidth() < 1.0) {
1060 return;
1061 }
1063 Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1064 Geom::Point(doc()->getWidth(), a.midpoint()[Geom::Y]));
1066 set_display_area(d, 10);
1067 }
1069 /**
1070 * Zoom to selection.
1071 */
1072 void
1073 SPDesktop::zoom_selection()
1074 {
1075 Geom::OptRect const d = selection->bounds();
1077 if ( !d || d->minExtent() < 0.1 ) {
1078 return;
1079 }
1081 set_display_area(*d, 10);
1082 }
1084 /**
1085 * Tell widget to let zoom widget grab keyboard focus.
1086 */
1087 void
1088 SPDesktop::zoom_grab_focus()
1089 {
1090 _widget->letZoomGrabFocus();
1091 }
1093 /**
1094 * Zoom to whole drawing.
1095 */
1096 void
1097 SPDesktop::zoom_drawing()
1098 {
1099 g_return_if_fail (doc() != NULL);
1100 SPItem *docitem = SP_ITEM(doc()->getRoot());
1101 g_return_if_fail (docitem != NULL);
1103 Geom::OptRect d = docitem->getBboxDesktop();
1105 /* Note that the second condition here indicates that
1106 ** there are no items in the drawing.
1107 */
1108 if ( !d || d->minExtent() < 0.1 ) {
1109 return;
1110 }
1112 set_display_area(*d, 10);
1113 }
1115 /**
1116 * Scroll canvas by specific coordinate amount in svg coordinates.
1117 */
1118 void
1119 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1120 {
1121 double scale = _d2w.descrim();
1122 scroll_world(dx*scale, dy*scale, is_scrolling);
1123 }
1125 /**
1126 * Scroll canvas by specific coordinate amount.
1127 */
1128 void
1129 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1130 {
1131 g_assert(_widget);
1133 Geom::Rect const viewbox = canvas->getViewbox();
1135 sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1137 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1138 sp_box3d_context_update_lines(event_context);
1140 _widget->updateRulers();
1141 _widget->updateScrollbars(_d2w.descrim());
1142 }
1144 bool
1145 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1146 {
1147 using Geom::X;
1148 using Geom::Y;
1150 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1151 gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1153 // autoscrolldistance is in screen pixels, but the display area is in document units
1154 autoscrolldistance /= _d2w.descrim();
1155 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1156 Geom::Rect dbox = get_display_area();
1157 dbox.expandBy(-autoscrolldistance);
1159 if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1160 !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y]) ) {
1162 Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1164 gdouble x_to;
1165 if (p[X] < dbox.min()[X])
1166 x_to = dbox.min()[X];
1167 else if (p[X] > dbox.max()[X])
1168 x_to = dbox.max()[X];
1169 else
1170 x_to = p[X];
1172 gdouble y_to;
1173 if (p[Y] < dbox.min()[Y])
1174 y_to = dbox.min()[Y];
1175 else if (p[Y] > dbox.max()[Y])
1176 y_to = dbox.max()[Y];
1177 else
1178 y_to = p[Y];
1180 Geom::Point const d_dt(x_to, y_to);
1181 Geom::Point const d_w( d_dt * _d2w );
1182 Geom::Point const moved_w( d_w - s_w );
1184 if (autoscrollspeed == 0)
1185 autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1187 if (autoscrollspeed != 0)
1188 scroll_world (autoscrollspeed * moved_w);
1190 return true;
1191 }
1192 return false;
1193 }
1195 bool
1196 SPDesktop::is_iconified()
1197 {
1198 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1199 }
1201 void
1202 SPDesktop::iconify()
1203 {
1204 _widget->setIconified();
1205 }
1207 bool
1208 SPDesktop::is_maximized()
1209 {
1210 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1211 }
1213 void
1214 SPDesktop::maximize()
1215 {
1216 _widget->setMaximized();
1217 }
1219 bool
1220 SPDesktop::is_fullscreen()
1221 {
1222 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1223 }
1225 void
1226 SPDesktop::fullscreen()
1227 {
1228 _widget->setFullscreen();
1229 }
1231 /** \brief Checks to see if the user is working in focused mode
1233 Returns the value of \c _focusMode
1234 */
1235 bool
1236 SPDesktop::is_focusMode()
1237 {
1238 return _focusMode;
1239 }
1241 /** \brief Changes whether the user is in focus mode or not
1242 \param mode Which mode the view should be in
1244 */
1245 void
1246 SPDesktop::focusMode (bool mode)
1247 {
1248 if (mode == _focusMode) { return; }
1250 _focusMode = mode;
1252 layoutWidget();
1253 //sp_desktop_widget_layout(SPDesktopWidget);
1255 return;
1256 }
1258 void
1259 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1260 {
1261 _widget->getGeometry (x, y, w, h);
1262 }
1264 void
1265 SPDesktop::setWindowPosition (Geom::Point p)
1266 {
1267 _widget->setPosition (p);
1268 }
1270 void
1271 SPDesktop::setWindowSize (gint w, gint h)
1272 {
1273 _widget->setSize (w, h);
1274 }
1276 void
1277 SPDesktop::setWindowTransient (void *p, int transient_policy)
1278 {
1279 _widget->setTransient (p, transient_policy);
1280 }
1282 Gtk::Window*
1283 SPDesktop::getToplevel( )
1284 {
1285 return _widget->getWindow();
1286 }
1288 void
1289 SPDesktop::presentWindow()
1290 {
1291 _widget->present();
1292 }
1294 bool
1295 SPDesktop::warnDialog (gchar *text)
1296 {
1297 return _widget->warnDialog (text);
1298 }
1300 void
1301 SPDesktop::toggleRulers()
1302 {
1303 _widget->toggleRulers();
1304 }
1306 void
1307 SPDesktop::toggleScrollbars()
1308 {
1309 _widget->toggleScrollbars();
1310 }
1312 void
1313 SPDesktop::layoutWidget()
1314 {
1315 _widget->layout();
1316 }
1318 void
1319 SPDesktop::destroyWidget()
1320 {
1321 _widget->destroy();
1322 }
1324 bool
1325 SPDesktop::shutdown()
1326 {
1327 return _widget->shutdown();
1328 }
1330 bool SPDesktop::onDeleteUI (GdkEventAny*)
1331 {
1332 if(shutdown())
1333 return true;
1335 destroyWidget();
1336 return false;
1337 }
1339 /**
1340 * onWindowStateEvent
1341 *
1342 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1343 * Since GTK doesn't have a way to query this state information directly, we
1344 * record it for the desktop here, and also possibly trigger a layout.
1345 */
1346 bool
1347 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1348 {
1349 // Record the desktop window's state
1350 window_state = event->new_window_state;
1352 // Layout may differ depending on full-screen mode or not
1353 GdkWindowState changed = event->changed_mask;
1354 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1355 layoutWidget();
1356 }
1358 return false;
1359 }
1361 void
1362 SPDesktop::setToolboxFocusTo (gchar const *label)
1363 {
1364 _widget->setToolboxFocusTo (label);
1365 }
1367 void
1368 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1369 {
1370 _widget->setToolboxAdjustmentValue (id, val);
1371 }
1373 void
1374 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1375 {
1376 _widget->setToolboxSelectOneValue (id, val);
1377 }
1379 bool
1380 SPDesktop::isToolboxButtonActive (gchar const *id)
1381 {
1382 return _widget->isToolboxButtonActive (id);
1383 }
1385 void
1386 SPDesktop::emitToolSubselectionChanged(gpointer data)
1387 {
1388 _tool_subselection_changed.emit(data);
1389 inkscape_subselection_changed (this);
1390 }
1392 void
1393 SPDesktop::updateNow()
1394 {
1395 sp_canvas_update_now(canvas);
1396 }
1398 void
1399 SPDesktop::enableInteraction()
1400 {
1401 _widget->enableInteraction();
1402 }
1404 void SPDesktop::disableInteraction()
1405 {
1406 _widget->disableInteraction();
1407 }
1409 void SPDesktop::setWaitingCursor()
1410 {
1411 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1412 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1413 gdk_cursor_unref(waiting);
1414 // GDK needs the flush for the cursor change to take effect
1415 gdk_flush();
1416 waiting_cursor = true;
1417 }
1419 void SPDesktop::clearWaitingCursor()
1420 {
1421 if (waiting_cursor)
1422 sp_event_context_update_cursor(sp_desktop_event_context(this));
1423 }
1425 void SPDesktop::toggleColorProfAdjust()
1426 {
1427 _widget->toggleColorProfAdjust();
1428 }
1430 void SPDesktop::toggleGrids()
1431 {
1432 if (namedview->grids) {
1433 if(gridgroup) {
1434 showGrids(!grids_visible);
1435 }
1436 } else {
1437 //there is no grid present at the moment. add a rectangular grid and make it visible
1438 namedview->writeNewGrid(sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1439 showGrids(true);
1440 }
1441 }
1443 void SPDesktop::showGrids(bool show, bool dirty_document)
1444 {
1445 grids_visible = show;
1446 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1447 if (show) {
1448 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1449 } else {
1450 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1451 }
1452 }
1454 void SPDesktop::toggleSnapGlobal()
1455 {
1456 bool v = namedview->getSnapGlobal();
1457 namedview->setSnapGlobal(!v);
1458 }
1460 //----------------------------------------------------------------------
1461 // Callback implementations. The virtual ones are connected by the view.
1463 void
1464 SPDesktop::onPositionSet (double x, double y)
1465 {
1466 _widget->viewSetPosition (Geom::Point(x,y));
1467 }
1469 void
1470 SPDesktop::onResized (double /*x*/, double /*y*/)
1471 {
1472 // Nothing called here
1473 }
1475 /**
1476 * Redraw callback; queues Gtk redraw; connected by View.
1477 */
1478 void
1479 SPDesktop::onRedrawRequested ()
1480 {
1481 if (main) {
1482 _widget->requestCanvasUpdate();
1483 }
1484 }
1486 void
1487 SPDesktop::updateCanvasNow()
1488 {
1489 _widget->requestCanvasUpdateAndWait();
1490 }
1492 /**
1493 * Associate document with desktop.
1494 */
1495 void
1496 SPDesktop::setDocument (SPDocument *doc)
1497 {
1498 if (this->doc() && doc) {
1499 namedview->hide(this);
1500 SP_ITEM(this->doc()->getRoot())->invoke_hide(dkey);
1501 }
1503 if (_layer_hierarchy) {
1504 _layer_hierarchy->clear();
1505 delete _layer_hierarchy;
1506 }
1507 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1508 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1509 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1510 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1511 _layer_hierarchy->setTop(doc->getRoot());
1513 /* setup EventLog */
1514 event_log = new Inkscape::EventLog(doc);
1515 doc->addUndoObserver(*event_log);
1517 _commit_connection.disconnect();
1518 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1520 /// \todo fixme: This condition exists to make sure the code
1521 /// inside is NOT called on initialization, only on replacement. But there
1522 /// are surely more safe methods to accomplish this.
1523 // TODO since the comment had reversed logic, check the intent of this block of code:
1524 if (drawing) {
1525 NRArenaItem *ai = 0;
1527 namedview = sp_document_namedview (doc, NULL);
1528 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1529 number = namedview->getViewCount();
1531 ai = SP_ITEM(doc->getRoot())->invoke_show(
1532 SP_CANVAS_ARENA (drawing)->arena,
1533 dkey,
1534 SP_ITEM_SHOW_DISPLAY);
1535 if (ai) {
1536 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1537 }
1538 namedview->show(this);
1539 /* Ugly hack */
1540 activate_guides (true);
1541 /* Ugly hack */
1542 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1543 }
1545 _document_replaced_signal.emit (this, doc);
1547 View::setDocument (doc);
1548 }
1550 void
1551 SPDesktop::onStatusMessage
1552 (Inkscape::MessageType type, gchar const *message)
1553 {
1554 if (_widget) {
1555 _widget->setMessage(type, message);
1556 }
1557 }
1559 void
1560 SPDesktop::onDocumentURISet (gchar const* uri)
1561 {
1562 _widget->setTitle(uri);
1563 }
1565 /**
1566 * Resized callback.
1567 */
1568 void
1569 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1570 {
1571 _doc2dt[5] = height;
1572 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1573 Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1574 SP_CTRLRECT(page)->setRectangle(a);
1575 SP_CTRLRECT(page_border)->setRectangle(a);
1576 }
1579 void
1580 SPDesktop::_onActivate (SPDesktop* dt)
1581 {
1582 if (!dt->_widget) return;
1583 dt->_widget->activateDesktop();
1584 }
1586 void
1587 SPDesktop::_onDeactivate (SPDesktop* dt)
1588 {
1589 if (!dt->_widget) return;
1590 dt->_widget->deactivateDesktop();
1591 }
1593 void
1594 SPDesktop::_onSelectionModified
1595 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1596 {
1597 if (!dt->_widget) return;
1598 dt->_widget->updateScrollbars (dt->_d2w.descrim());
1599 }
1601 static void
1602 _onSelectionChanged
1603 (Inkscape::Selection *selection, SPDesktop *desktop)
1604 {
1605 /** \todo
1606 * only change the layer for single selections, or what?
1607 * This seems reasonable -- for multiple selections there can be many
1608 * different layers involved.
1609 */
1610 SPItem *item=selection->singleItem();
1611 if (item) {
1612 SPObject *layer=desktop->layerForObject(item);
1613 if ( layer && layer != desktop->currentLayer() ) {
1614 desktop->setCurrentLayer(layer);
1615 }
1616 }
1617 }
1619 /**
1620 * Calls event handler of current event context.
1621 * \param arena Unused
1622 * \todo fixme
1623 */
1624 static gint
1625 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1626 {
1627 if (ai) {
1628 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1629 return sp_event_context_item_handler (desktop->event_context, spi, event);
1630 } else {
1631 return sp_event_context_root_handler (desktop->event_context, event);
1632 }
1633 }
1635 static void
1636 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1637 g_return_if_fail(SP_IS_GROUP(layer));
1638 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1639 }
1641 /// Callback
1642 static void
1643 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1644 g_return_if_fail(SP_IS_GROUP(layer));
1645 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1646 }
1648 /// Callback
1649 static void
1650 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1651 SPDesktop *desktop)
1652 {
1653 desktop->_layer_changed_signal.emit (bottom);
1654 }
1656 /// Called when document is starting to be rebuilt.
1657 static void
1658 _reconstruction_start (SPDesktop * desktop)
1659 {
1660 // printf("Desktop, starting reconstruction\n");
1661 desktop->_reconstruction_old_layer_id = g_strdup(desktop->currentLayer()->getId());
1662 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1664 /*
1665 GSList const * selection_objs = desktop->selection->list();
1666 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1668 }
1669 */
1670 desktop->selection->clear();
1672 // printf("Desktop, starting reconstruction end\n");
1673 }
1675 /// Called when document rebuild is finished.
1676 static void
1677 _reconstruction_finish (SPDesktop * desktop)
1678 {
1679 // printf("Desktop, finishing reconstruction\n");
1680 if (desktop->_reconstruction_old_layer_id == NULL)
1681 return;
1683 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1684 if (newLayer != NULL)
1685 desktop->setCurrentLayer(newLayer);
1687 g_free(desktop->_reconstruction_old_layer_id);
1688 desktop->_reconstruction_old_layer_id = NULL;
1689 // printf("Desktop, finishing reconstruction end\n");
1690 return;
1691 }
1693 /**
1694 * Namedview_modified callback.
1695 */
1696 static void
1697 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1698 {
1699 SPNamedView *nv=SP_NAMEDVIEW(obj);
1701 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1703 /* Show/hide page background */
1704 if (nv->pagecolor & 0xff) {
1705 sp_canvas_item_show (desktop->table);
1706 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1707 sp_canvas_item_move_to_z (desktop->table, 0);
1708 } else {
1709 sp_canvas_item_hide (desktop->table);
1710 }
1712 /* Show/hide page border */
1713 if (nv->showborder) {
1714 // show
1715 sp_canvas_item_show (desktop->page_border);
1716 // set color and shadow
1717 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1718 if (nv->pageshadow) {
1719 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1720 }
1721 // place in the z-order stack
1722 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1723 sp_canvas_item_move_to_z (desktop->page_border, 2);
1724 } else {
1725 int order = sp_canvas_item_order (desktop->page_border);
1726 int morder = sp_canvas_item_order (desktop->drawing);
1727 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1728 morder - order);
1729 }
1730 } else {
1731 sp_canvas_item_hide (desktop->page_border);
1732 if (nv->pageshadow) {
1733 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1734 }
1735 }
1737 /* Show/hide page shadow */
1738 if (nv->showpageshadow && nv->pageshadow) {
1739 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1740 } else {
1741 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1742 }
1744 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1745 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1746 (SP_RGBA32_R_U(nv->pagecolor) +
1747 SP_RGBA32_G_U(nv->pagecolor) +
1748 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1749 // the background color is light or transparent, use black outline
1750 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1751 } else { // use white outline
1752 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1753 }
1754 }
1755 }
1757 Geom::Matrix SPDesktop::w2d() const
1758 {
1759 return _w2d;
1760 }
1762 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1763 {
1764 return p * _w2d;
1765 }
1767 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1768 {
1769 return p * _d2w;
1770 }
1772 Geom::Matrix SPDesktop::doc2dt() const
1773 {
1774 return _doc2dt;
1775 }
1777 Geom::Matrix SPDesktop::dt2doc() const
1778 {
1779 // doc2dt is its own inverse
1780 return _doc2dt;
1781 }
1783 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1784 {
1785 return p * _doc2dt;
1786 }
1788 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1789 {
1790 return p * dt2doc();
1791 }
1794 /*
1795 * Pop event context from desktop's context stack. Never used.
1796 */
1797 // void
1798 // SPDesktop::pop_event_context (unsigned int key)
1799 // {
1800 // SPEventContext *ec = NULL;
1801 //
1802 // if (event_context && event_context->key == key) {
1803 // g_return_if_fail (event_context);
1804 // g_return_if_fail (event_context->next);
1805 // ec = event_context;
1806 // sp_event_context_deactivate (ec);
1807 // event_context = ec->next;
1808 // sp_event_context_activate (event_context);
1809 // _event_context_changed_signal.emit (this, ec);
1810 // }
1811 //
1812 // SPEventContext *ref = event_context;
1813 // while (ref && ref->next && ref->next->key != key)
1814 // ref = ref->next;
1815 //
1816 // if (ref && ref->next) {
1817 // ec = ref->next;
1818 // ref->next = ec->next;
1819 // }
1820 //
1821 // if (ec) {
1822 // sp_event_context_finish (ec);
1823 // g_object_unref (G_OBJECT (ec));
1824 // }
1825 // }
1827 /*
1828 Local Variables:
1829 mode:c++
1830 c-file-style:"stroustrup"
1831 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1832 indent-tabs-mode:nil
1833 fill-column:99
1834 End:
1835 */
1836 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :