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