1 #define __SP_DESKTOP_C__
3 /** \file
4 * Editable view implementation
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * MenTaLguY <mental@rydia.net>
9 * bulia byak <buliabyak@users.sf.net>
10 * Ralf Stephan <ralf@ark.in-berlin.de>
11 * John Bintz <jcoswell@coswellproductions.org>
12 *
13 * Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl>
14 * Copyright (C) 2006 John Bintz
15 * Copyright (C) 2004 MenTaLguY
16 * Copyright (C) 1999-2002 Lauris Kaplinski
17 * Copyright (C) 2000-2001 Ximian, Inc.
18 *
19 * Released under GNU GPL, read the file 'COPYING' for more information
20 */
22 /** \class SPDesktop
23 * SPDesktop is a subclass of View, implementing an editable document
24 * canvas. It is extensively used by many UI controls that need certain
25 * visual representations of their own.
26 *
27 * SPDesktop provides a certain set of SPCanvasItems, serving as GUI
28 * layers of different control objects. The one containing the whole
29 * document is the drawing layer. In addition to it, there are grid,
30 * guide, sketch and control layers. The sketch layer is used for
31 * temporary drawing objects, before the real objects in document are
32 * created. The control layer contains editing knots, rubberband and
33 * similar non-document UI objects.
34 *
35 * Each SPDesktop is associated with a SPNamedView node of the document
36 * tree. Currently, all desktops are created from a single main named
37 * view, but in the future there may be support for different ones.
38 * SPNamedView serves as an in-document container for desktop-related
39 * data, like grid and guideline placement, snapping options and so on.
40 *
41 * Associated with each SPDesktop are the two most important editing
42 * related objects - SPSelection and SPEventContext.
43 *
44 * Sodipodi keeps track of the active desktop and invokes notification
45 * signals whenever it changes. UI elements can use these to update their
46 * display to the selection of the currently active editing window.
47 * (Lauris Kaplinski)
48 */
50 #ifdef HAVE_CONFIG_H
51 # include "config.h"
52 #endif
54 #include <glibmm/i18n.h>
55 #include <sigc++/functors/mem_fun.h>
57 #include "macros.h"
58 #include "inkscape-private.h"
59 #include "desktop.h"
60 #include "desktop-events.h"
61 #include "desktop-handles.h"
62 #include "document.h"
63 #include "message-stack.h"
64 #include "selection.h"
65 #include "select-context.h"
66 #include "sp-namedview.h"
67 #include "color.h"
68 #include "sp-item-group.h"
69 #include "prefs-utils.h"
70 #include "object-hierarchy.h"
71 #include "helper/units.h"
72 #include "display/canvas-arena.h"
73 #include "display/nr-arena.h"
74 #include "display/gnome-canvas-acetate.h"
75 #include "display/sodipodi-ctrlrect.h"
76 #include "display/sp-canvas-util.h"
77 #include "libnr/nr-matrix-div.h"
78 #include "libnr/nr-rect-ops.h"
79 #include "ui/dialog/dialog-manager.h"
80 #include "xml/repr.h"
81 #include "message-context.h"
82 #include "layer-manager.h"
83 #include "event-log.h"
85 namespace Inkscape { namespace XML { class Node; }}
87 // Callback declarations
88 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
89 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
90 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
91 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
92 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
93 static void _reconstruction_start(SPDesktop * desktop);
94 static void _reconstruction_finish(SPDesktop * desktop);
95 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
96 static void _update_snap_distances (SPDesktop *desktop);
98 /**
99 * Return new desktop object.
100 * \pre namedview != NULL.
101 * \pre canvas != NULL.
102 */
103 SPDesktop::SPDesktop()
104 {
105 _dlg_mgr = NULL;
106 _widget = 0;
107 namedview = NULL;
108 selection = NULL;
109 acetate = NULL;
110 main = NULL;
111 grid = NULL;
112 guides = NULL;
113 drawing = NULL;
114 sketch = NULL;
115 controls = NULL;
116 event_context = 0;
117 layer_manager = 0;
119 _d2w.set_identity();
120 _w2d.set_identity();
121 _doc2dt = NR::Matrix(NR::scale(1, -1));
123 guides_active = false;
125 zooms_past = NULL;
126 zooms_future = NULL;
128 is_fullscreen = false;
130 gr_item = NULL;
131 gr_point_num = 0;
132 gr_fill_or_stroke = true;
134 _layer_hierarchy = NULL;
135 _active = false;
137 selection = Inkscape::GC::release (new Inkscape::Selection (this));
138 }
140 void
141 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
143 {
144 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
146 current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
148 namedview = nv;
149 canvas = aCanvas;
151 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
152 /* Kill flicker */
153 sp_document_ensure_up_to_date (document);
155 /* Setup Dialog Manager */
156 _dlg_mgr = new Inkscape::UI::Dialog::DialogManager();
158 dkey = sp_item_display_key_new (1);
160 /* Connect document */
161 setDocument (document);
163 number = namedview->getViewCount();
166 /* Setup Canvas */
167 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
169 SPCanvasGroup *root = sp_canvas_root (canvas);
171 /* Setup adminstrative layers */
172 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
173 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
174 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
175 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
177 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
178 SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
179 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
180 sp_canvas_item_move_to_z (table, 0);
182 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
183 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
184 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
186 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
187 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
189 SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
191 // Start always in normal mode
192 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
193 canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
195 grid = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
196 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
197 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
198 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
200 /* Push select tool to the bottom of stack */
201 /** \todo
202 * FIXME: this is the only call to this. Everything else seems to just
203 * call "set" instead of "push". Can we assume that there is only one
204 * context ever?
205 */
206 push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
208 // display rect and zoom are now handled in sp_desktop_widget_realize()
210 NR::Rect const d(NR::Point(0.0, 0.0),
211 NR::Point(sp_document_width(document), sp_document_height(document)));
213 SP_CTRLRECT(page)->setRectangle(d);
214 SP_CTRLRECT(page_border)->setRectangle(d);
216 /* the following sets the page shadow on the canvas
217 It was originally set to 5, which is really cheesy!
218 It now is an attribute in the document's namedview. If a value of
219 0 is used, then the constructor for a shadow is not initialized.
220 */
222 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
223 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
224 }
227 /* Connect event for page resize */
228 _doc2dt[5] = sp_document_height (document);
229 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
231 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
233 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
234 SP_CANVAS_ARENA (drawing)->arena,
235 dkey,
236 SP_ITEM_SHOW_DISPLAY);
237 if (ai) {
238 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
239 nr_arena_item_unref (ai);
240 }
242 namedview->show(this);
243 /* Ugly hack */
244 activate_guides (true);
245 /* Ugly hack */
246 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
248 /* Set up notification of rebuilding the document, this allows
249 for saving object related settings in the document. */
250 _reconstruction_start_connection =
251 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
252 _reconstruction_finish_connection =
253 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
254 _reconstruction_old_layer_id = NULL;
256 _commit_connection = document->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
258 // ?
259 // sp_active_desktop_set (desktop);
260 _inkscape = INKSCAPE;
262 _activate_connection = _activate_signal.connect(
263 sigc::bind(
264 sigc::ptr_fun(_onActivate),
265 this
266 )
267 );
268 _deactivate_connection = _deactivate_signal.connect(
269 sigc::bind(
270 sigc::ptr_fun(_onDeactivate),
271 this
272 )
273 );
275 _sel_modified_connection = selection->connectModified(
276 sigc::bind(
277 sigc::ptr_fun(&_onSelectionModified),
278 this
279 )
280 );
281 _sel_changed_connection = selection->connectChanged(
282 sigc::bind(
283 sigc::ptr_fun(&_onSelectionChanged),
284 this
285 )
286 );
289 /* setup LayerManager */
290 // (Setting up after the connections are all in place, as it may use some of them)
291 layer_manager = new Inkscape::LayerManager( this );
292 }
295 void SPDesktop::destroy()
296 {
297 _activate_connection.disconnect();
298 _deactivate_connection.disconnect();
299 _sel_modified_connection.disconnect();
300 _sel_changed_connection.disconnect();
301 _modified_connection.disconnect();
302 _commit_connection.disconnect();
303 _reconstruction_start_connection.disconnect();
304 _reconstruction_finish_connection.disconnect();
306 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
307 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
308 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
310 while (event_context) {
311 SPEventContext *ec = event_context;
312 event_context = ec->next;
313 sp_event_context_finish (ec);
314 g_object_unref (G_OBJECT (ec));
315 }
317 if (_layer_hierarchy) {
318 delete _layer_hierarchy;
319 }
321 if (_inkscape) {
322 _inkscape = NULL;
323 }
325 if (drawing) {
326 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
327 drawing = NULL;
328 }
330 delete _guides_message_context;
331 _guides_message_context = NULL;
333 g_list_free (zooms_past);
334 g_list_free (zooms_future);
335 }
337 SPDesktop::~SPDesktop() {}
339 //--------------------------------------------------------------------
340 /* Public methods */
342 void SPDesktop::setDisplayModeNormal()
343 {
344 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
345 canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
346 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
347 }
349 void SPDesktop::setDisplayModeOutline()
350 {
351 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
352 canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
353 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
354 }
356 /**
357 * Returns current root (=bottom) layer.
358 */
359 SPObject *SPDesktop::currentRoot() const
360 {
361 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
362 }
364 /**
365 * Returns current top layer.
366 */
367 SPObject *SPDesktop::currentLayer() const
368 {
369 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
370 }
372 /**
373 * Sets the current layer of the desktop.
374 *
375 * Make \a object the top layer.
376 */
377 void SPDesktop::setCurrentLayer(SPObject *object) {
378 g_return_if_fail(SP_IS_GROUP(object));
379 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
380 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
381 _layer_hierarchy->setBottom(object);
382 }
384 /**
385 * Return layer that contains \a object.
386 */
387 SPObject *SPDesktop::layerForObject(SPObject *object) {
388 g_return_val_if_fail(object != NULL, NULL);
390 SPObject *root=currentRoot();
391 object = SP_OBJECT_PARENT(object);
392 while ( object && object != root && !isLayer(object) ) {
393 object = SP_OBJECT_PARENT(object);
394 }
395 return object;
396 }
398 /**
399 * True if object is a layer.
400 */
401 bool SPDesktop::isLayer(SPObject *object) const {
402 return ( SP_IS_GROUP(object)
403 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
404 == SPGroup::LAYER ) );
405 }
407 /**
408 * True if desktop viewport fully contains \a item's bbox.
409 */
410 bool SPDesktop::isWithinViewport (SPItem *item) const
411 {
412 NR::Rect const viewport = get_display_area();
413 NR::Rect const bbox = sp_item_bbox_desktop(item);
414 return viewport.contains(bbox);
415 }
417 ///
418 bool SPDesktop::itemIsHidden(SPItem const *item) const {
419 return item->isHidden(this->dkey);
420 }
422 /**
423 * Set activate property of desktop; emit signal if changed.
424 */
425 void
426 SPDesktop::set_active (bool new_active)
427 {
428 if (new_active != _active) {
429 _active = new_active;
430 if (new_active) {
431 _activate_signal.emit();
432 } else {
433 _deactivate_signal.emit();
434 }
435 }
436 }
438 /**
439 * Set activate status of current desktop's named view.
440 */
441 void
442 SPDesktop::activate_guides(bool activate)
443 {
444 guides_active = activate;
445 namedview->activateGuides(this, activate);
446 }
448 /**
449 * Make desktop switch documents.
450 */
451 void
452 SPDesktop::change_document (SPDocument *theDocument)
453 {
454 g_return_if_fail (theDocument != NULL);
456 /* unselect everything before switching documents */
457 selection->clear();
459 setDocument (theDocument);
460 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
461 _document_replaced_signal.emit (this, theDocument);
462 }
464 /**
465 * Make desktop switch event contexts.
466 */
467 void
468 SPDesktop::set_event_context (GtkType type, const gchar *config)
469 {
470 SPEventContext *ec;
471 while (event_context) {
472 ec = event_context;
473 sp_event_context_deactivate (ec);
474 event_context = ec->next;
475 sp_event_context_finish (ec);
476 g_object_unref (G_OBJECT (ec));
477 }
479 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
480 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
481 ec->next = event_context;
482 event_context = ec;
483 sp_event_context_activate (ec);
484 _event_context_changed_signal.emit (this, ec);
485 }
487 /**
488 * Push event context onto desktop's context stack.
489 */
490 void
491 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
492 {
493 SPEventContext *ref, *ec;
494 Inkscape::XML::Node *repr;
496 if (event_context && event_context->key == key) return;
497 ref = event_context;
498 while (ref && ref->next && ref->next->key != key) ref = ref->next;
499 if (ref && ref->next) {
500 ec = ref->next;
501 ref->next = ec->next;
502 sp_event_context_finish (ec);
503 g_object_unref (G_OBJECT (ec));
504 }
506 if (event_context) sp_event_context_deactivate (event_context);
507 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
508 ec = sp_event_context_new (type, this, repr, key);
509 ec->next = event_context;
510 event_context = ec;
511 sp_event_context_activate (ec);
512 _event_context_changed_signal.emit (this, ec);
513 }
515 /**
516 * Sets the coordinate status to a given point
517 */
518 void
519 SPDesktop::set_coordinate_status (NR::Point p) {
520 _widget->setCoordinateStatus(p);
521 }
523 /**
524 * \see sp_document_item_from_list_at_point_bottom()
525 */
526 SPItem *
527 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
528 {
529 g_return_val_if_fail (doc() != NULL, NULL);
530 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
531 }
533 /**
534 * \see sp_document_item_at_point()
535 */
536 SPItem *
537 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
538 {
539 g_return_val_if_fail (doc() != NULL, NULL);
540 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
541 }
543 /**
544 * \see sp_document_group_at_point()
545 */
546 SPItem *
547 SPDesktop::group_at_point (NR::Point const p) const
548 {
549 g_return_val_if_fail (doc() != NULL, NULL);
550 return sp_document_group_at_point (doc(), dkey, p);
551 }
553 /**
554 * \brief Returns the mouse point in document coordinates; if mouse is
555 * outside the canvas, returns the center of canvas viewpoint
556 */
557 NR::Point
558 SPDesktop::point() const
559 {
560 NR::Point p = _widget->getPointer();
561 NR::Point pw = sp_canvas_window_to_world (canvas, p);
562 p = w2d(pw);
564 NR::Rect const r = canvas->getViewbox();
566 NR::Point r0 = w2d(r.min());
567 NR::Point r1 = w2d(r.max());
569 if (p[NR::X] >= r0[NR::X] &&
570 p[NR::X] <= r1[NR::X] &&
571 p[NR::Y] >= r1[NR::Y] &&
572 p[NR::Y] <= r0[NR::Y])
573 {
574 return p;
575 } else {
576 return (r0 + r1) / 2;
577 }
578 }
580 /**
581 * Put current zoom data in history list.
582 */
583 void
584 SPDesktop::push_current_zoom (GList **history)
585 {
586 NR::Rect const area = get_display_area();
588 NRRect *old_zoom = g_new(NRRect, 1);
589 old_zoom->x0 = area.min()[NR::X];
590 old_zoom->x1 = area.max()[NR::X];
591 old_zoom->y0 = area.min()[NR::Y];
592 old_zoom->y1 = area.max()[NR::Y];
593 if ( *history == NULL
594 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
595 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
596 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
597 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
598 {
599 *history = g_list_prepend (*history, old_zoom);
600 }
601 }
603 /**
604 * Set viewbox.
605 */
606 void
607 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
608 {
609 g_assert(_widget);
611 // save the zoom
612 if (log) {
613 push_current_zoom(&zooms_past);
614 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
615 g_list_free (zooms_future);
616 zooms_future = NULL;
617 }
619 double const cx = 0.5 * (x0 + x1);
620 double const cy = 0.5 * (y0 + y1);
622 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
624 double scale = expansion(_d2w);
625 double newscale;
626 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
627 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
628 } else {
629 newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
630 }
632 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
634 int clear = FALSE;
635 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
636 /* Set zoom factors */
637 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
638 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
639 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
640 clear = TRUE;
641 }
643 /* Calculate top left corner */
644 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
645 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
647 /* Scroll */
648 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
650 _widget->updateRulers();
651 _widget->updateScrollbars(expansion(_d2w));
652 _widget->updateZoom();
653 }
655 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
656 {
657 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
658 }
660 /**
661 * Return viewbox dimensions.
662 */
663 NR::Rect SPDesktop::get_display_area() const
664 {
665 NR::Rect const viewbox = canvas->getViewbox();
667 double const scale = _d2w[0];
669 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
670 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
671 }
673 /**
674 * Revert back to previous zoom if possible.
675 */
676 void
677 SPDesktop::prev_zoom()
678 {
679 if (zooms_past == NULL) {
680 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
681 return;
682 }
684 // push current zoom into forward zooms list
685 push_current_zoom (&zooms_future);
687 // restore previous zoom
688 set_display_area (((NRRect *) zooms_past->data)->x0,
689 ((NRRect *) zooms_past->data)->y0,
690 ((NRRect *) zooms_past->data)->x1,
691 ((NRRect *) zooms_past->data)->y1,
692 0, false);
694 // remove the just-added zoom from the past zooms list
695 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
696 }
698 /**
699 * Set zoom to next in list.
700 */
701 void
702 SPDesktop::next_zoom()
703 {
704 if (zooms_future == NULL) {
705 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
706 return;
707 }
709 // push current zoom into past zooms list
710 push_current_zoom (&zooms_past);
712 // restore next zoom
713 set_display_area (((NRRect *) zooms_future->data)->x0,
714 ((NRRect *) zooms_future->data)->y0,
715 ((NRRect *) zooms_future->data)->x1,
716 ((NRRect *) zooms_future->data)->y1,
717 0, false);
719 // remove the just-used zoom from the zooms_future list
720 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
721 }
723 /**
724 * Zoom to point with absolute zoom factor.
725 */
726 void
727 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
728 {
729 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
731 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
732 // this check prevents "sliding" when trying to zoom in at maximum zoom;
733 /// \todo someone please fix calculations properly and remove this hack
734 if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
735 return;
737 NR::Rect const viewbox = canvas->getViewbox();
739 double const width2 = viewbox.dimensions()[NR::X] / zoom;
740 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
742 set_display_area(cx - px * width2,
743 cy - py * height2,
744 cx + (1 - px) * width2,
745 cy + (1 - py) * height2,
746 0.0);
747 }
749 /**
750 * Zoom to center with absolute zoom factor.
751 */
752 void
753 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
754 {
755 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
756 }
758 /**
759 * Zoom to point with relative zoom factor.
760 */
761 void
762 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
763 {
764 NR::Rect const area = get_display_area();
766 if (cx < area.min()[NR::X]) {
767 cx = area.min()[NR::X];
768 }
769 if (cx > area.max()[NR::X]) {
770 cx = area.max()[NR::X];
771 }
772 if (cy < area.min()[NR::Y]) {
773 cy = area.min()[NR::Y];
774 }
775 if (cy > area.max()[NR::Y]) {
776 cy = area.max()[NR::Y];
777 }
779 gdouble const scale = expansion(_d2w) * zoom;
780 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
781 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
783 zoom_absolute_keep_point(cx, cy, px, py, scale);
784 }
786 /**
787 * Zoom to center with relative zoom factor.
788 */
789 void
790 SPDesktop::zoom_relative (double cx, double cy, double zoom)
791 {
792 gdouble scale = expansion(_d2w) * zoom;
793 zoom_absolute (cx, cy, scale);
794 }
796 /**
797 * Set display area to origin and current document dimensions.
798 */
799 void
800 SPDesktop::zoom_page()
801 {
802 NR::Rect d(NR::Point(0, 0),
803 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
805 if (d.dimensions()[NR::X] < 1.0 || d.dimensions()[NR::Y] < 1.0) {
806 return;
807 }
809 set_display_area(d, 10);
810 }
812 /**
813 * Set display area to current document width.
814 */
815 void
816 SPDesktop::zoom_page_width()
817 {
818 NR::Rect const a = get_display_area();
820 if (sp_document_width(doc()) < 1.0) {
821 return;
822 }
824 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
825 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
827 set_display_area(d, 10);
828 }
830 /**
831 * Zoom to selection.
832 */
833 void
834 SPDesktop::zoom_selection()
835 {
836 NR::Rect const d = selection->bounds();
838 if (d.dimensions()[NR::X] < 0.1 || d.dimensions()[NR::Y] < 0.1) {
839 return;
840 }
842 set_display_area(d, 10);
843 }
845 /**
846 * Tell widget to let zoom widget grab keyboard focus.
847 */
848 void
849 SPDesktop::zoom_grab_focus()
850 {
851 _widget->letZoomGrabFocus();
852 }
854 /**
855 * Zoom to whole drawing.
856 */
857 void
858 SPDesktop::zoom_drawing()
859 {
860 g_return_if_fail (doc() != NULL);
861 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
862 g_return_if_fail (docitem != NULL);
864 NR::Rect d = sp_item_bbox_desktop(docitem);
866 /* Note that the second condition here indicates that
867 ** there are no items in the drawing.
868 */
869 if ( d.dimensions()[NR::X] < 1.0 || d.dimensions()[NR::Y] < 1.0 ) {
870 return;
871 }
873 set_display_area(d, 10);
874 }
876 /**
877 * Scroll canvas by specific coordinate amount.
878 */
879 void
880 SPDesktop::scroll_world (double dx, double dy)
881 {
882 g_assert(_widget);
884 NR::Rect const viewbox = canvas->getViewbox();
886 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE);
888 _widget->updateRulers();
889 _widget->updateScrollbars(expansion(_d2w));
890 }
892 bool
893 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
894 {
895 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
897 // autoscrolldistance is in screen pixels, but the display area is in document units
898 autoscrolldistance /= expansion(_d2w);
899 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
901 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
902 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
904 NR::Point const s_w( (*p) * _d2w );
906 gdouble x_to;
907 if ((*p)[NR::X] < dbox.min()[NR::X])
908 x_to = dbox.min()[NR::X];
909 else if ((*p)[NR::X] > dbox.max()[NR::X])
910 x_to = dbox.max()[NR::X];
911 else
912 x_to = (*p)[NR::X];
914 gdouble y_to;
915 if ((*p)[NR::Y] < dbox.min()[NR::Y])
916 y_to = dbox.min()[NR::Y];
917 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
918 y_to = dbox.max()[NR::Y];
919 else
920 y_to = (*p)[NR::Y];
922 NR::Point const d_dt(x_to, y_to);
923 NR::Point const d_w( d_dt * _d2w );
924 NR::Point const moved_w( d_w - s_w );
926 if (autoscrollspeed == 0)
927 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
929 if (autoscrollspeed != 0)
930 scroll_world (autoscrollspeed * moved_w);
932 return true;
933 }
934 return false;
935 }
937 void
938 SPDesktop::fullscreen()
939 {
940 _widget->setFullscreen();
941 }
943 void
944 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
945 {
946 _widget->getGeometry (x, y, w, h);
947 }
949 void
950 SPDesktop::setWindowPosition (NR::Point p)
951 {
952 _widget->setPosition (p);
953 }
955 void
956 SPDesktop::setWindowSize (gint w, gint h)
957 {
958 _widget->setSize (w, h);
959 }
961 void
962 SPDesktop::setWindowTransient (void *p, int transient_policy)
963 {
964 _widget->setTransient (p, transient_policy);
965 }
967 void
968 SPDesktop::presentWindow()
969 {
970 _widget->present();
971 }
973 bool
974 SPDesktop::warnDialog (gchar *text)
975 {
976 return _widget->warnDialog (text);
977 }
979 void
980 SPDesktop::toggleRulers()
981 {
982 _widget->toggleRulers();
983 }
985 void
986 SPDesktop::toggleScrollbars()
987 {
988 _widget->toggleScrollbars();
989 }
991 void
992 SPDesktop::layoutWidget()
993 {
994 _widget->layout();
995 }
997 void
998 SPDesktop::destroyWidget()
999 {
1000 _widget->destroy();
1001 }
1003 bool
1004 SPDesktop::shutdown()
1005 {
1006 return _widget->shutdown();
1007 }
1009 void
1010 SPDesktop::setToolboxFocusTo (gchar const *label)
1011 {
1012 _widget->setToolboxFocusTo (label);
1013 }
1015 void
1016 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1017 {
1018 _widget->setToolboxAdjustmentValue (id, val);
1019 }
1021 bool
1022 SPDesktop::isToolboxButtonActive (gchar const *id)
1023 {
1024 return _widget->isToolboxButtonActive (id);
1025 }
1027 void
1028 SPDesktop::emitToolSubselectionChanged(gpointer data)
1029 {
1030 _tool_subselection_changed.emit(data);
1031 inkscape_subselection_changed (this);
1032 }
1034 void
1035 SPDesktop::updateNow()
1036 {
1037 sp_canvas_update_now(canvas);
1038 }
1040 void
1041 SPDesktop::enableInteraction()
1042 {
1043 _widget->enableInteraction();
1044 }
1046 void SPDesktop::disableInteraction()
1047 {
1048 _widget->disableInteraction();
1049 }
1051 //----------------------------------------------------------------------
1052 // Callback implementations. The virtual ones are connected by the view.
1054 void
1055 SPDesktop::onPositionSet (double x, double y)
1056 {
1057 _widget->viewSetPosition (NR::Point(x,y));
1058 }
1060 void
1061 SPDesktop::onResized (double x, double y)
1062 {
1063 // Nothing called here
1064 }
1066 /**
1067 * Redraw callback; queues Gtk redraw; connected by View.
1068 */
1069 void
1070 SPDesktop::onRedrawRequested ()
1071 {
1072 if (main) {
1073 _widget->requestCanvasUpdate();
1074 }
1075 }
1077 void
1078 SPDesktop::updateCanvasNow()
1079 {
1080 _widget->requestCanvasUpdateAndWait();
1081 }
1083 /**
1084 * Associate document with desktop.
1085 */
1086 /// \todo fixme: refactor SPDesktop::init to use setDocument
1087 void
1088 SPDesktop::setDocument (SPDocument *doc)
1089 {
1090 if (this->doc() && doc) {
1091 namedview->hide(this);
1092 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1093 }
1095 if (_layer_hierarchy) {
1096 _layer_hierarchy->clear();
1097 delete _layer_hierarchy;
1098 }
1099 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1100 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1101 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1102 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1103 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1105 /* setup EventLog */
1106 event_log = new Inkscape::EventLog(doc);
1107 doc->addUndoObserver(*event_log);
1109 _commit_connection.disconnect();
1110 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1112 /// \todo fixme: This condition exists to make sure the code
1113 /// inside is called only once on initialization. But there
1114 /// are surely more safe methods to accomplish this.
1115 if (drawing) {
1116 NRArenaItem *ai;
1118 namedview = sp_document_namedview (doc, NULL);
1119 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1120 number = namedview->getViewCount();
1122 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1123 SP_CANVAS_ARENA (drawing)->arena,
1124 dkey,
1125 SP_ITEM_SHOW_DISPLAY);
1126 if (ai) {
1127 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1128 nr_arena_item_unref (ai);
1129 }
1130 namedview->show(this);
1131 /* Ugly hack */
1132 activate_guides (true);
1133 /* Ugly hack */
1134 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1135 }
1137 _document_replaced_signal.emit (this, doc);
1139 View::setDocument (doc);
1140 }
1142 void
1143 SPDesktop::onStatusMessage
1144 (Inkscape::MessageType type, gchar const *message)
1145 {
1146 if (_widget) {
1147 _widget->setMessage(type, message);
1148 }
1149 }
1151 void
1152 SPDesktop::onDocumentURISet (gchar const* uri)
1153 {
1154 _widget->setTitle(uri);
1155 }
1157 /**
1158 * Resized callback.
1159 */
1160 void
1161 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1162 {
1163 _doc2dt[5] = height;
1164 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1165 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1166 SP_CTRLRECT(page)->setRectangle(a);
1167 SP_CTRLRECT(page_border)->setRectangle(a);
1168 }
1171 void
1172 SPDesktop::_onActivate (SPDesktop* dt)
1173 {
1174 if (!dt->_widget) return;
1175 dt->_widget->activateDesktop();
1176 }
1178 void
1179 SPDesktop::_onDeactivate (SPDesktop* dt)
1180 {
1181 if (!dt->_widget) return;
1182 dt->_widget->deactivateDesktop();
1183 }
1185 void
1186 SPDesktop::_onSelectionModified
1187 (Inkscape::Selection *selection, guint flags, SPDesktop *dt)
1188 {
1189 if (!dt->_widget) return;
1190 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1191 }
1193 static void
1194 _onSelectionChanged
1195 (Inkscape::Selection *selection, SPDesktop *desktop)
1196 {
1197 /** \todo
1198 * only change the layer for single selections, or what?
1199 * This seems reasonable -- for multiple selections there can be many
1200 * different layers involved.
1201 */
1202 SPItem *item=selection->singleItem();
1203 if (item) {
1204 SPObject *layer=desktop->layerForObject(item);
1205 if ( layer && layer != desktop->currentLayer() ) {
1206 desktop->setCurrentLayer(layer);
1207 }
1208 }
1209 }
1211 /**
1212 * Calls event handler of current event context.
1213 * \param arena Unused
1214 * \todo fixme
1215 */
1216 static gint
1217 _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1218 {
1219 if (ai) {
1220 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1221 return sp_event_context_item_handler (desktop->event_context, spi, event);
1222 } else {
1223 return sp_event_context_root_handler (desktop->event_context, event);
1224 }
1225 }
1227 static void
1228 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1229 g_return_if_fail(SP_IS_GROUP(layer));
1230 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1231 }
1233 /// Callback
1234 static void
1235 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1236 g_return_if_fail(SP_IS_GROUP(layer));
1237 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1238 }
1240 /// Callback
1241 static void
1242 _layer_hierarchy_changed(SPObject *top, SPObject *bottom,
1243 SPDesktop *desktop)
1244 {
1245 desktop->_layer_changed_signal.emit (bottom);
1246 }
1248 /// Called when document is starting to be rebuilt.
1249 static void
1250 _reconstruction_start (SPDesktop * desktop)
1251 {
1252 // printf("Desktop, starting reconstruction\n");
1253 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1254 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1256 /*
1257 GSList const * selection_objs = desktop->selection->list();
1258 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1260 }
1261 */
1262 desktop->selection->clear();
1264 // printf("Desktop, starting reconstruction end\n");
1265 }
1267 /// Called when document rebuild is finished.
1268 static void
1269 _reconstruction_finish (SPDesktop * desktop)
1270 {
1271 // printf("Desktop, finishing reconstruction\n");
1272 if (desktop->_reconstruction_old_layer_id == NULL)
1273 return;
1275 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1276 if (newLayer != NULL)
1277 desktop->setCurrentLayer(newLayer);
1279 g_free(desktop->_reconstruction_old_layer_id);
1280 desktop->_reconstruction_old_layer_id = NULL;
1281 // printf("Desktop, finishing reconstruction end\n");
1282 return;
1283 }
1285 /**
1286 * Namedview_modified callback.
1287 */
1288 static void
1289 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1290 {
1291 SPNamedView *nv=SP_NAMEDVIEW(obj);
1293 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1295 /* Recalculate snap distances */
1296 /* FIXME: why is the desktop getting involved in setting up something
1297 ** that is entirely to do with the namedview?
1298 */
1299 _update_snap_distances (desktop);
1301 /* Show/hide page background */
1302 if (nv->pagecolor & 0xff) {
1303 sp_canvas_item_show (desktop->table);
1304 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1305 sp_canvas_item_move_to_z (desktop->table, 0);
1306 } else {
1307 sp_canvas_item_hide (desktop->table);
1308 }
1310 /* Show/hide page border */
1311 if (nv->showborder) {
1312 // show
1313 sp_canvas_item_show (desktop->page_border);
1314 // set color and shadow
1315 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1316 if (nv->pageshadow) {
1317 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1318 }
1319 // place in the z-order stack
1320 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1321 sp_canvas_item_move_to_z (desktop->page_border, 2);
1322 } else {
1323 int order = sp_canvas_item_order (desktop->page_border);
1324 int morder = sp_canvas_item_order (desktop->drawing);
1325 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1326 morder - order);
1327 }
1328 } else {
1329 sp_canvas_item_hide (desktop->page_border);
1330 if (nv->pageshadow) {
1331 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1332 }
1333 }
1335 /* Show/hide page shadow */
1336 if (nv->showpageshadow && nv->pageshadow) {
1337 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1338 } else {
1339 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1340 }
1342 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1343 (SP_RGBA32_R_U(nv->pagecolor) +
1344 SP_RGBA32_G_U(nv->pagecolor) +
1345 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1346 // the background color is light or transparent, use black outline
1347 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = 0xff;
1348 } else { // use white outline
1349 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = 0xffffffff;
1350 }
1351 }
1352 }
1354 /**
1355 * Callback to reset snapper's distances.
1356 */
1357 static void
1358 _update_snap_distances (SPDesktop *desktop)
1359 {
1360 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1362 SPNamedView &nv = *desktop->namedview;
1364 nv.snap_manager.grid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1365 *nv.gridtoleranceunit,
1366 px));
1367 nv.snap_manager.axonomgrid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1368 *nv.gridtoleranceunit,
1369 px));
1370 nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1371 *nv.guidetoleranceunit,
1372 px));
1373 nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1374 *nv.objecttoleranceunit,
1375 px));
1376 }
1379 NR::Matrix SPDesktop::w2d() const
1380 {
1381 return _w2d;
1382 }
1384 NR::Point SPDesktop::w2d(NR::Point const &p) const
1385 {
1386 return p * _w2d;
1387 }
1389 NR::Point SPDesktop::d2w(NR::Point const &p) const
1390 {
1391 return p * _d2w;
1392 }
1394 NR::Matrix SPDesktop::doc2dt() const
1395 {
1396 return _doc2dt;
1397 }
1399 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1400 {
1401 return p * _doc2dt;
1402 }
1404 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1405 {
1406 return p / _doc2dt;
1407 }
1410 /**
1411 * Pop event context from desktop's context stack. Never used.
1412 */
1413 // void
1414 // SPDesktop::pop_event_context (unsigned int key)
1415 // {
1416 // SPEventContext *ec = NULL;
1417 //
1418 // if (event_context && event_context->key == key) {
1419 // g_return_if_fail (event_context);
1420 // g_return_if_fail (event_context->next);
1421 // ec = event_context;
1422 // sp_event_context_deactivate (ec);
1423 // event_context = ec->next;
1424 // sp_event_context_activate (event_context);
1425 // _event_context_changed_signal.emit (this, ec);
1426 // }
1427 //
1428 // SPEventContext *ref = event_context;
1429 // while (ref && ref->next && ref->next->key != key)
1430 // ref = ref->next;
1431 //
1432 // if (ref && ref->next) {
1433 // ec = ref->next;
1434 // ref->next = ec->next;
1435 // }
1436 //
1437 // if (ec) {
1438 // sp_event_context_finish (ec);
1439 // g_object_unref (G_OBJECT (ec));
1440 // }
1441 // }
1443 /*
1444 Local Variables:
1445 mode:c++
1446 c-file-style:"stroustrup"
1447 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1448 indent-tabs-mode:nil
1449 fill-column:99
1450 End:
1451 */
1452 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :