Code

Add tracking of skew factor and display in warning message.
[inkscape.git] / src / display / sp-canvas.cpp
1 #define __SP_CANVAS_C__
3 /** \file
4  * Port of GnomeCanvas for Inkscape needs
5  *
6  * Authors:
7  *   Federico Mena <federico@nuclecu.unam.mx>
8  *   Raph Levien <raph@gimp.org>
9  *   Lauris Kaplinski <lauris@kaplinski.com>
10  *   fred
11  *   bbyak
12  *
13  * Copyright (C) 1998 The Free Software Foundation
14  * Copyright (C) 2002-2006 authors
15  *
16  * Released under GNU GPL, read the file 'COPYING' for more information
17  */
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <libnr/nr-pixblock.h>
25 #include <gtk/gtkmain.h>
26 #include <gtk/gtksignal.h>
27 #include <gtk/gtkversion.h>
29 #include <gtkmm.h>
31 #include <helper/sp-marshal.h>
32 #include <display/sp-canvas.h>
33 #include "display-forward.h"
34 #include <libnr/nr-matrix-fns.h>
35 #include <libnr/nr-matrix-ops.h>
36 #include <libnr/nr-convex-hull.h>
37 #include "prefs-utils.h"
38 #include "inkscape.h"
39 #include "sodipodi-ctrlrect.h"
40 #if ENABLE_LCMS
41 #include "color-profile-fns.h"
42 #endif // ENABLE_LCMS
43 #include "display/rendermode.h"
44 #include "libnr/nr-blit.h"
45 #include "display/inkscape-cairo.h"
46 #include "debug/gdk-event-latency-tracker.h"
48 using Inkscape::Debug::GdkEventLatencyTracker;
50 // GTK_CHECK_VERSION returns false on failure
51 #define HAS_GDK_EVENT_REQUEST_MOTIONS GTK_CHECK_VERSION(2, 12, 0)
53 // gtk_check_version returns non-NULL on failure
54 static bool const HAS_BROKEN_MOTION_HINTS =
55   true || gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS;
57 // Define this to visualize the regions to be redrawn
58 //#define DEBUG_REDRAW 1;
60 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
61 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
62 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
63 #define TILE_SIZE 16
65 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
67 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
69 enum {
70     SP_CANVAS_ITEM_VISIBLE = 1 << 7,
71     SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
72     SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
73 };
75 /**
76  * A group of Items.
77  */
78 struct SPCanvasGroup {
79     SPCanvasItem item;
81     GList *items, *last;
82 };
84 /**
85  * The SPCanvasGroup vtable.
86  */
87 struct SPCanvasGroupClass {
88     SPCanvasItemClass parent_class;
89 };
91 /**
92  * The SPCanvas vtable.
93  */
94 struct SPCanvasClass {
95     GtkWidgetClass parent_class;
96 };
98 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
99 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
101 /* SPCanvasItem */
103 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
106 static void sp_canvas_request_update (SPCanvas *canvas);
108 static void track_latency(GdkEvent const *event);
109 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
110 static void sp_canvas_item_init (SPCanvasItem *item);
111 static void sp_canvas_item_dispose (GObject *object);
112 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
114 static int emit_event (SPCanvas *canvas, GdkEvent *event);
116 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
118 static GtkObjectClass *item_parent_class;
120 /**
121  * Registers the SPCanvasItem class with Glib and returns its type number.
122  */
123 GType
124 sp_canvas_item_get_type (void)
126     static GType type = 0;
127     if (!type) {
128         static GTypeInfo const info = {
129             sizeof (SPCanvasItemClass),
130             NULL, NULL,
131             (GClassInitFunc) sp_canvas_item_class_init,
132             NULL, NULL,
133             sizeof (SPCanvasItem),
134             0,
135             (GInstanceInitFunc) sp_canvas_item_init,
136             NULL
137         };
138         type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
139     }
141     return type;
144 /**
145  * Initializes the SPCanvasItem vtable and the "event" signal.
146  */
147 static void
148 sp_canvas_item_class_init (SPCanvasItemClass *klass)
150     GObjectClass *object_class = (GObjectClass *) klass;
152     /* fixme: Derive from GObject */
153     item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
155     item_signals[ITEM_EVENT] = g_signal_new ("event",
156                                              G_TYPE_FROM_CLASS (klass),
157                                              G_SIGNAL_RUN_LAST,
158                                              ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
159                                              NULL, NULL,
160                                              sp_marshal_BOOLEAN__POINTER,
161                                              G_TYPE_BOOLEAN, 1,
162                                              GDK_TYPE_EVENT);
164     object_class->dispose = sp_canvas_item_dispose;
167 /**
168  * Callback for initialization of SPCanvasItem.
169  */
170 static void
171 sp_canvas_item_init (SPCanvasItem *item)
173     item->flags |= SP_CANVAS_ITEM_VISIBLE;
174     item->xform = NR::Matrix(NR::identity());
177 /**
178  * Constructs new SPCanvasItem on SPCanvasGroup.
179  */
180 SPCanvasItem *
181 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
183     va_list args;
185     g_return_val_if_fail (parent != NULL, NULL);
186     g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
187     g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
189     SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
191     va_start (args, first_arg_name);
192     sp_canvas_item_construct (item, parent, first_arg_name, args);
193     va_end (args);
195     return item;
198 /**
199  * Sets up the newly created SPCanvasItem.
200  *
201  * We make it static for encapsulation reasons since it was nowhere used.
202  */
203 static void
204 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
206     g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
207     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
209     item->parent = SP_CANVAS_ITEM (parent);
210     item->canvas = item->parent->canvas;
212     g_object_set_valist (G_OBJECT (item), first_arg_name, args);
214     group_add (SP_CANVAS_GROUP (item->parent), item);
216     sp_canvas_item_request_update (item);
219 /**
220  * Helper function that requests redraw only if item's visible flag is set.
221  */
222 static void
223 redraw_if_visible (SPCanvasItem *item)
225     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
226         int x0 = (int)(item->x1);
227         int x1 = (int)(item->x2);
228         int y0 = (int)(item->y1);
229         int y1 = (int)(item->y2);
231         if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
232             sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
233         }
234     }
237 /**
238  * Callback that removes item from all referers and destroys it.
239  */
240 static void
241 sp_canvas_item_dispose (GObject *object)
243     SPCanvasItem *item = SP_CANVAS_ITEM (object);
245     // Hack: if this is a ctrlrect, move it to 0,0;
246     // this redraws only the stroke of the rect to be deleted,
247     // avoiding redraw of the entire area
248     if (SP_IS_CTRLRECT(item)) {
249         SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
250         SP_CTRLRECT(object)->update(item->xform, 0);
251     } else {
252         redraw_if_visible (item);
253     }
254     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
256     if (item == item->canvas->current_item) {
257         item->canvas->current_item = NULL;
258         item->canvas->need_repick = TRUE;
259     }
261     if (item == item->canvas->new_current_item) {
262         item->canvas->new_current_item = NULL;
263         item->canvas->need_repick = TRUE;
264     }
266     if (item == item->canvas->grabbed_item) {
267         item->canvas->grabbed_item = NULL;
268         gdk_pointer_ungrab (GDK_CURRENT_TIME);
269     }
271     if (item == item->canvas->focused_item)
272         item->canvas->focused_item = NULL;
274     if (item->parent) {
275         group_remove (SP_CANVAS_GROUP (item->parent), item);
276     }
278     G_OBJECT_CLASS (item_parent_class)->dispose (object);
281 /**
282  * Helper function to update item and its children.
283  *
284  * NB! affine is parent2canvas.
285  */
286 static void
287 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
289     /* Apply the child item's transform */
290     NR::Matrix child_affine = item->xform * affine;
292     /* apply object flags to child flags */
293     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
295     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
296         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
298     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
299         child_flags |= SP_CANVAS_UPDATE_AFFINE;
301     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
302         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
303             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
304     }
306     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
307     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
310 /**
311  * Helper function to invoke the point method of the item.
312  *
313  * The argument x, y should be in the parent's item-relative coordinate
314  * system.  This routine applies the inverse of the item's transform,
315  * maintaining the affine invariant.
316  */
317 static double
318 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
320     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
321         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
323     return NR_HUGE;
326 /**
327  * Makes the item's affine transformation matrix be equal to the specified
328  * matrix.
329  *
330  * @item: A canvas item.
331  * @affine: An affine transformation matrix.
332  */
333 void
334 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine)
336     item->xform = affine;
338     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
339         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
340         if (item->parent != NULL) {
341             sp_canvas_item_request_update (item->parent);
342         } else {
343             sp_canvas_request_update (item->canvas);
344         }
345     }
347     item->canvas->need_repick = TRUE;
350 /**
351  * Convenience function to reorder items in a group's child list.
352  *
353  * This puts the specified link after the "before" link.
354  */
355 static void
356 put_item_after (GList *link, GList *before)
358     if (link == before)
359         return;
361     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
363     if (before == NULL) {
364         if (link == parent->items) return;
366         link->prev->next = link->next;
368         if (link->next) {
369             link->next->prev = link->prev;
370         } else {
371             parent->last = link->prev;
372         }
374         link->prev = before;
375         link->next = parent->items;
376         link->next->prev = link;
377         parent->items = link;
378     } else {
379         if ((link == parent->last) && (before == parent->last->prev))
380             return;
382         if (link->next)
383             link->next->prev = link->prev;
385         if (link->prev)
386             link->prev->next = link->next;
387         else {
388             parent->items = link->next;
389             parent->items->prev = NULL;
390         }
392         link->prev = before;
393         link->next = before->next;
395         link->prev->next = link;
397         if (link->next)
398             link->next->prev = link;
399         else
400             parent->last = link;
401     }
405 /**
406  * Raises the item in its parent's stack by the specified number of positions.
407  *
408  * \param item A canvas item.
409  * \param positions Number of steps to raise the item.
410  *
411  * If the number of positions is greater than the distance to the top of the
412  * stack, then the item is put at the top.
413  */
414 void
415 sp_canvas_item_raise (SPCanvasItem *item, int positions)
417     g_return_if_fail (item != NULL);
418     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
419     g_return_if_fail (positions >= 0);
421     if (!item->parent || positions == 0)
422         return;
424     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
425     GList *link = g_list_find (parent->items, item);
426     g_assert (link != NULL);
428     GList *before;
429     for (before = link; positions && before; positions--)
430         before = before->next;
432     if (!before)
433         before = parent->last;
435     put_item_after (link, before);
437     redraw_if_visible (item);
438     item->canvas->need_repick = TRUE;
442 /**
443  * Lowers the item in its parent's stack by the specified number of positions.
444  *
445  * \param item A canvas item.
446  * \param positions Number of steps to lower the item.
447  *
448  * If the number of positions is greater than the distance to the bottom of the
449  * stack, then the item is put at the bottom.
450  **/
451 void
452 sp_canvas_item_lower (SPCanvasItem *item, int positions)
454     g_return_if_fail (item != NULL);
455     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
456     g_return_if_fail (positions >= 1);
458     if (!item->parent || positions == 0)
459         return;
461     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
462     GList *link = g_list_find (parent->items, item);
463     g_assert (link != NULL);
465     GList *before;
466     if (link->prev)
467         for (before = link->prev; positions && before; positions--)
468             before = before->prev;
469     else
470         before = NULL;
472     put_item_after (link, before);
474     redraw_if_visible (item);
475     item->canvas->need_repick = TRUE;
478 /**
479  * Sets visible flag on item and requests a redraw.
480  */
481 void
482 sp_canvas_item_show (SPCanvasItem *item)
484     g_return_if_fail (item != NULL);
485     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
487     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
488         return;
490     item->flags |= SP_CANVAS_ITEM_VISIBLE;
492     int x0 = (int)(item->x1);
493     int x1 = (int)(item->x2);
494     int y0 = (int)(item->y1);
495     int y1 = (int)(item->y2);
497     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
498         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
499         item->canvas->need_repick = TRUE;
500     }
503 /**
504  * Clears visible flag on item and requests a redraw.
505  */
506 void
507 sp_canvas_item_hide (SPCanvasItem *item)
509     g_return_if_fail (item != NULL);
510     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
512     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
513         return;
515     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
517     int x0 = (int)(item->x1);
518     int x1 = (int)(item->x2);
519     int y0 = (int)(item->y1);
520     int y1 = (int)(item->y2);
522     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
523         sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
524         item->canvas->need_repick = TRUE;
525     }
528 /**
529  * Grab item under cursor.
530  *
531  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
532  */
533 int
534 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
536     g_return_val_if_fail (item != NULL, -1);
537     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
538     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
540     if (item->canvas->grabbed_item)
541         return -1;
543     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
544         return -1;
546     if (HAS_BROKEN_MOTION_HINTS) {
547         event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
548     }
550     /* fixme: Top hack (Lauris) */
551     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
552     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
553     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
554                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
555                       NULL, cursor, etime);
557     item->canvas->grabbed_item = item;
558     item->canvas->grabbed_event_mask = event_mask;
559     item->canvas->current_item = item; /* So that events go to the grabbed item */
561     return 0;
564 /**
565  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
566  * mouse.
567  *
568  * \param item A canvas item that holds a grab.
569  * \param etime The timestamp for ungrabbing the mouse.
570  */
571 void
572 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
574     g_return_if_fail (item != NULL);
575     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
577     if (item->canvas->grabbed_item != item)
578         return;
580     item->canvas->grabbed_item = NULL;
582     gdk_pointer_ungrab (etime);
585 /**
586  * Returns the product of all transformation matrices from the root item down
587  * to the item.
588  */
589 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
591     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
593     NR::Matrix affine = NR::identity();
595     while (item) {
596         affine *= item->xform;
597         item = item->parent;
598     }
599     return affine;
602 /**
603  * Helper that returns true iff item is descendant of parent.
604  */
605 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
607     while (item) {
608         if (item == parent)
609             return true;
610         item = item->parent;
611     }
613     return false;
616 /**
617  * Focus canvas, and item under cursor if it is not already focussed.
618  */
619 void
620 sp_canvas_item_grab_focus (SPCanvasItem *item)
622     g_return_if_fail (item != NULL);
623     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
624     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
626     SPCanvasItem *focused_item = item->canvas->focused_item;
628     if (focused_item) {
629         GdkEvent ev;
630         ev.focus_change.type = GDK_FOCUS_CHANGE;
631         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
632         ev.focus_change.send_event = FALSE;
633         ev.focus_change.in = FALSE;
635         emit_event (item->canvas, &ev);
636     }
638     item->canvas->focused_item = item;
639     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
641     if (focused_item) {
642         GdkEvent ev;
643         ev.focus_change.type = GDK_FOCUS_CHANGE;
644         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
645         ev.focus_change.send_event = FALSE;
646         ev.focus_change.in = TRUE;
648         emit_event (item->canvas, &ev);
649     }
652 /**
653  * Requests that the canvas queue an update for the specified item.
654  *
655  * To be used only by item implementations.
656  */
657 void
658 sp_canvas_item_request_update (SPCanvasItem *item)
660     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
661         return;
663     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
665     if (item->parent != NULL) {
666         /* Recurse up the tree */
667         sp_canvas_item_request_update (item->parent);
668     } else {
669         /* Have reached the top of the tree, make sure the update call gets scheduled. */
670         sp_canvas_request_update (item->canvas);
671     }
674 /**
675  * Returns position of item in group.
676  */
677 gint sp_canvas_item_order (SPCanvasItem * item)
679     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
682 /* SPCanvasGroup */
684 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
685 static void sp_canvas_group_init (SPCanvasGroup *group);
686 static void sp_canvas_group_destroy (GtkObject *object);
688 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
689 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
690 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
692 static SPCanvasItemClass *group_parent_class;
694 /**
695  * Registers SPCanvasGroup class with Gtk and returns its type number.
696  */
697 GType sp_canvas_group_get_type(void)
699     static GType type = 0;
700     if (!type) {
701         GTypeInfo info = {
702             sizeof(SPCanvasGroupClass),
703             0, // base_init
704             0, // base_finalize
705             (GClassInitFunc)sp_canvas_group_class_init,
706             0, // class_finalize
707             0, // class_data
708             sizeof(SPCanvasGroup),
709             0, // n_preallocs
710             (GInstanceInitFunc)sp_canvas_group_init,
711             0 // value_table
712         };
713         type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
714     }
715     return type;
718 /**
719  * Class initialization function for SPCanvasGroupClass
720  */
721 static void
722 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
724     GtkObjectClass *object_class = (GtkObjectClass *) klass;
725     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
727     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
729     object_class->destroy = sp_canvas_group_destroy;
731     item_class->update = sp_canvas_group_update;
732     item_class->render = sp_canvas_group_render;
733     item_class->point = sp_canvas_group_point;
736 /**
737  * Callback. Empty.
738  */
739 static void
740 sp_canvas_group_init (SPCanvasGroup */*group*/)
742     /* Nothing here */
745 /**
746  * Callback that destroys all items in group and calls group's virtual
747  * destroy() function.
748  */
749 static void
750 sp_canvas_group_destroy (GtkObject *object)
752     g_return_if_fail (object != NULL);
753     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
755     SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
757     GList *list = group->items;
758     while (list) {
759         SPCanvasItem *child = (SPCanvasItem *)list->data;
760         list = list->next;
762         gtk_object_destroy (GTK_OBJECT (child));
763     }
765     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
766         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
769 /**
770  * Update handler for canvas groups
771  */
772 static void
773 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
775     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
776     NR::ConvexHull corners(NR::Point(0, 0));
777     bool empty=true;
779     for (GList *list = group->items; list; list = list->next) {
780         SPCanvasItem *i = (SPCanvasItem *)list->data;
782         sp_canvas_item_invoke_update (i, affine, flags);
784         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
785             if (empty) {
786                 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
787                 empty = false;
788             } else {
789                 corners.add(NR::Point(i->x1, i->y1));
790             }
791             corners.add(NR::Point(i->x2, i->y2));
792         }
793     }
795     NR::Maybe<NR::Rect> const bounds = corners.bounds();
796     if (bounds) {
797         item->x1 = bounds->min()[NR::X];
798         item->y1 = bounds->min()[NR::Y];
799         item->x2 = bounds->max()[NR::X];
800         item->y2 = bounds->max()[NR::Y];
801     } else {
802         // FIXME ?
803         item->x1 = item->x2 = item->y1 = item->y2 = 0;
804     }
807 /**
808  * Point handler for canvas groups.
809  */
810 static double
811 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
813     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
814     double const x = p[NR::X];
815     double const y = p[NR::Y];
816     int x1 = (int)(x - item->canvas->close_enough);
817     int y1 = (int)(y - item->canvas->close_enough);
818     int x2 = (int)(x + item->canvas->close_enough);
819     int y2 = (int)(y + item->canvas->close_enough);
821     double best = 0.0;
822     *actual_item = NULL;
824     double dist = 0.0;
826     for (GList *list = group->items; list; list = list->next) {
827         SPCanvasItem *child = (SPCanvasItem *)list->data;
829         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
830             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
832             int has_point;
833             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
834                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
835                 has_point = TRUE;
836             } else
837                 has_point = FALSE;
839             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
840                 best = dist;
841                 *actual_item = point_item;
842             }
843         }
844     }
846     return best;
849 /**
850  * Renders all visible canvas group items in buf rectangle.
851  */
852 static void
853 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
855     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
857     for (GList *list = group->items; list; list = list->next) {
858         SPCanvasItem *child = (SPCanvasItem *)list->data;
859         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
860             if ((child->x1 < buf->rect.x1) &&
861                 (child->y1 < buf->rect.y1) &&
862                 (child->x2 > buf->rect.x0) &&
863                 (child->y2 > buf->rect.y0)) {
864                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
865                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
866             }
867         }
868     }
871 /**
872  * Adds an item to a canvas group.
873  */
874 static void
875 group_add (SPCanvasGroup *group, SPCanvasItem *item)
877     gtk_object_ref (GTK_OBJECT (item));
878     gtk_object_sink (GTK_OBJECT (item));
880     if (!group->items) {
881         group->items = g_list_append (group->items, item);
882         group->last = group->items;
883     } else {
884         group->last = g_list_append (group->last, item)->next;
885     }
887     sp_canvas_item_request_update (item);
890 /**
891  * Removes an item from a canvas group
892  */
893 static void
894 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
896     g_return_if_fail (group != NULL);
897     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
898     g_return_if_fail (item != NULL);
900     for (GList *children = group->items; children; children = children->next) {
901         if (children->data == item) {
903             /* Unparent the child */
905             item->parent = NULL;
906             gtk_object_unref (GTK_OBJECT (item));
908             /* Remove it from the list */
910             if (children == group->last) group->last = children->prev;
912             group->items = g_list_remove_link (group->items, children);
913             g_list_free (children);
914             break;
915         }
916     }
919 /* SPCanvas */
921 static void sp_canvas_class_init (SPCanvasClass *klass);
922 static void sp_canvas_init (SPCanvas *canvas);
923 static void sp_canvas_destroy (GtkObject *object);
925 static void sp_canvas_realize (GtkWidget *widget);
926 static void sp_canvas_unrealize (GtkWidget *widget);
928 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
929 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
931 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
932 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
933 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
934 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
935 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
936 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
937 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
938 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
940 static GtkWidgetClass *canvas_parent_class;
942 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
943 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
944 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
945 static int do_update (SPCanvas *canvas);
947 /**
948  * Registers the SPCanvas class if necessary, and returns the type ID
949  * associated to it.
950  *
951  * \return The type ID of the SPCanvas class.
952  **/
953 GType sp_canvas_get_type(void)
955     static GType type = 0;
956     if (!type) {
957         GTypeInfo info = {
958             sizeof(SPCanvasClass),
959             0, // base_init
960             0, // base_finalize
961             (GClassInitFunc)sp_canvas_class_init,
962             0, // class_finalize
963             0, // class_data
964             sizeof(SPCanvas),
965             0, // n_preallocs
966             (GInstanceInitFunc)sp_canvas_init,
967             0 // value_table
968         };
969         type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
970     }
971     return type;
974 /**
975  * Class initialization function for SPCanvasClass.
976  */
977 static void
978 sp_canvas_class_init (SPCanvasClass *klass)
980     GtkObjectClass *object_class = (GtkObjectClass *) klass;
981     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
983     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
985     object_class->destroy = sp_canvas_destroy;
987     widget_class->realize = sp_canvas_realize;
988     widget_class->unrealize = sp_canvas_unrealize;
989     widget_class->size_request = sp_canvas_size_request;
990     widget_class->size_allocate = sp_canvas_size_allocate;
991     widget_class->button_press_event = sp_canvas_button;
992     widget_class->button_release_event = sp_canvas_button;
993     widget_class->motion_notify_event = sp_canvas_motion;
994     widget_class->scroll_event = sp_canvas_scroll;
995     widget_class->expose_event = sp_canvas_expose;
996     widget_class->key_press_event = sp_canvas_key;
997     widget_class->key_release_event = sp_canvas_key;
998     widget_class->enter_notify_event = sp_canvas_crossing;
999     widget_class->leave_notify_event = sp_canvas_crossing;
1000     widget_class->focus_in_event = sp_canvas_focus_in;
1001     widget_class->focus_out_event = sp_canvas_focus_out;
1004 /**
1005  * Callback: object initialization for SPCanvas.
1006  */
1007 static void
1008 sp_canvas_init (SPCanvas *canvas)
1010     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1011     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1012     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1014     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1015     canvas->pick_event.crossing.x = 0;
1016     canvas->pick_event.crossing.y = 0;
1018     /* Create the root item as a special case */
1019     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1020     canvas->root->canvas = canvas;
1022     gtk_object_ref (GTK_OBJECT (canvas->root));
1023     gtk_object_sink (GTK_OBJECT (canvas->root));
1025     canvas->need_repick = TRUE;
1027     // See comment at in sp-canvas.h.
1028     canvas->gen_all_enter_events = false;
1030     canvas->tiles=NULL;
1031     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1032     canvas->tileH=canvas->tileV=0;
1034     canvas->forced_redraw_count = 0;
1035     canvas->forced_redraw_limit = -1;
1037 #if ENABLE_LCMS
1038     canvas->enable_cms_display_adj = false;
1039     canvas->cms_key = new Glib::ustring("");
1040 #endif // ENABLE_LCMS
1042     canvas->is_scrolling = false;
1046 /**
1047  * Convenience function to remove the idle handler of a canvas.
1048  */
1049 static void
1050 remove_idle (SPCanvas *canvas)
1052     if (canvas->idle_id) {
1053         gtk_idle_remove (canvas->idle_id);
1054         canvas->idle_id = 0;
1055     }
1058 /*
1059  * Removes the transient state of the canvas (idle handler, grabs).
1060  */
1061 static void
1062 shutdown_transients (SPCanvas *canvas)
1064     /* We turn off the need_redraw flag, since if the canvas is mapped again
1065      * it will request a redraw anyways.  We do not turn off the need_update
1066      * flag, though, because updates are not queued when the canvas remaps
1067      * itself.
1068      */
1069     if (canvas->need_redraw) {
1070         canvas->need_redraw = FALSE;
1071     }
1072     if ( canvas->tiles ) g_free(canvas->tiles);
1073     canvas->tiles=NULL;
1074     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1075     canvas->tileH=canvas->tileV=0;
1077     if (canvas->grabbed_item) {
1078         canvas->grabbed_item = NULL;
1079         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1080     }
1082     remove_idle (canvas);
1085 /**
1086  * Destroy handler for SPCanvas.
1087  */
1088 static void
1089 sp_canvas_destroy (GtkObject *object)
1091     SPCanvas *canvas = SP_CANVAS (object);
1093     if (canvas->root) {
1094         gtk_object_unref (GTK_OBJECT (canvas->root));
1095         canvas->root = NULL;
1096     }
1098     shutdown_transients (canvas);
1100     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1101         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1104 static void track_latency(GdkEvent const *event) {
1105     GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1106     boost::optional<double> latency = tracker.process(event);
1107     if (latency && *latency > 2.0) {
1108         g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1109     }
1112 /**
1113  * Returns new canvas as widget.
1114  */
1115 GtkWidget *
1116 sp_canvas_new_aa (void)
1118     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1120     return (GtkWidget *) canvas;
1123 /**
1124  * The canvas widget's realize callback.
1125  */
1126 static void
1127 sp_canvas_realize (GtkWidget *widget)
1129     SPCanvas *canvas = SP_CANVAS (widget);
1131     GdkWindowAttr attributes;
1132     attributes.window_type = GDK_WINDOW_CHILD;
1133     attributes.x = widget->allocation.x;
1134     attributes.y = widget->allocation.y;
1135     attributes.width = widget->allocation.width;
1136     attributes.height = widget->allocation.height;
1137     attributes.wclass = GDK_INPUT_OUTPUT;
1138     attributes.visual = gdk_rgb_get_visual ();
1139     attributes.colormap = gdk_rgb_get_cmap ();
1140     attributes.event_mask = (gtk_widget_get_events (widget) |
1141                              GDK_EXPOSURE_MASK |
1142                              GDK_BUTTON_PRESS_MASK |
1143                              GDK_BUTTON_RELEASE_MASK |
1144                              GDK_POINTER_MOTION_MASK |
1145                              ( HAS_BROKEN_MOTION_HINTS ?
1146                                0 : GDK_POINTER_MOTION_HINT_MASK ) |
1147                              GDK_PROXIMITY_IN_MASK |
1148                              GDK_PROXIMITY_OUT_MASK |
1149                              GDK_KEY_PRESS_MASK |
1150                              GDK_KEY_RELEASE_MASK |
1151                              GDK_ENTER_NOTIFY_MASK |
1152                              GDK_LEAVE_NOTIFY_MASK |
1153                              GDK_FOCUS_CHANGE_MASK);
1154     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1156     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1157     gdk_window_set_user_data (widget->window, widget);
1159     if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1160         gtk_widget_set_events(widget, attributes.event_mask);
1162     widget->style = gtk_style_attach (widget->style, widget->window);
1164     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1166     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1169 /**
1170  * The canvas widget's unrealize callback.
1171  */
1172 static void
1173 sp_canvas_unrealize (GtkWidget *widget)
1175     SPCanvas *canvas = SP_CANVAS (widget);
1177     canvas->current_item = NULL;
1178     canvas->grabbed_item = NULL;
1179     canvas->focused_item = NULL;
1181     shutdown_transients (canvas);
1183     gdk_gc_destroy (canvas->pixmap_gc);
1184     canvas->pixmap_gc = NULL;
1186     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1187         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1190 /**
1191  * The canvas widget's size_request callback.
1192  */
1193 static void
1194 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1196     static_cast<void>(SP_CANVAS (widget));
1198     req->width = 256;
1199     req->height = 256;
1202 /**
1203  * The canvas widget's size_allocate callback.
1204  */
1205 static void
1206 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1208     SPCanvas *canvas = SP_CANVAS (widget);
1210     /* Schedule redraw of new region */
1211     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1212     if (allocation->width > widget->allocation.width) {
1213         sp_canvas_request_redraw (canvas,
1214                                   canvas->x0 + widget->allocation.width,
1215                                   0,
1216                                   canvas->x0 + allocation->width,
1217                                   canvas->y0 + allocation->height);
1218     }
1219     if (allocation->height > widget->allocation.height) {
1220         sp_canvas_request_redraw (canvas,
1221                                   0,
1222                                   canvas->y0 + widget->allocation.height,
1223                                   canvas->x0 + allocation->width,
1224                                   canvas->y0 + allocation->height);
1225     }
1227     widget->allocation = *allocation;
1228     
1229     if (GTK_WIDGET_REALIZED (widget)) {
1230         gdk_window_move_resize (widget->window,
1231                                 widget->allocation.x, widget->allocation.y,
1232                                 widget->allocation.width, widget->allocation.height);
1233     }
1236 /**
1237  * Helper that emits an event for an item in the canvas, be it the current
1238  * item, grabbed item, or focused item, as appropriate.
1239  */
1240 static int
1241 emit_event (SPCanvas *canvas, GdkEvent *event)
1243     guint mask;
1245     if (canvas->grabbed_item) {
1246         switch (event->type) {
1247         case GDK_ENTER_NOTIFY:
1248             mask = GDK_ENTER_NOTIFY_MASK;
1249             break;
1250         case GDK_LEAVE_NOTIFY:
1251             mask = GDK_LEAVE_NOTIFY_MASK;
1252             break;
1253         case GDK_MOTION_NOTIFY:
1254             mask = GDK_POINTER_MOTION_MASK;
1255             break;
1256         case GDK_BUTTON_PRESS:
1257         case GDK_2BUTTON_PRESS:
1258         case GDK_3BUTTON_PRESS:
1259             mask = GDK_BUTTON_PRESS_MASK;
1260             break;
1261         case GDK_BUTTON_RELEASE:
1262             mask = GDK_BUTTON_RELEASE_MASK;
1263             break;
1264         case GDK_KEY_PRESS:
1265             mask = GDK_KEY_PRESS_MASK;
1266             break;
1267         case GDK_KEY_RELEASE:
1268             mask = GDK_KEY_RELEASE_MASK;
1269             break;
1270         case GDK_SCROLL:
1271             mask = GDK_SCROLL;
1272             break;
1273         default:
1274             mask = 0;
1275             break;
1276         }
1278         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1279     }
1281     /* Convert to world coordinates -- we have two cases because of diferent
1282      * offsets of the fields in the event structures.
1283      */
1285     GdkEvent ev = *event;
1287     switch (ev.type) {
1288     case GDK_ENTER_NOTIFY:
1289     case GDK_LEAVE_NOTIFY:
1290         ev.crossing.x += canvas->x0;
1291         ev.crossing.y += canvas->y0;
1292         break;
1293     case GDK_MOTION_NOTIFY:
1294     case GDK_BUTTON_PRESS:
1295     case GDK_2BUTTON_PRESS:
1296     case GDK_3BUTTON_PRESS:
1297     case GDK_BUTTON_RELEASE:
1298         ev.motion.x += canvas->x0;
1299         ev.motion.y += canvas->y0;
1300         break;
1301     default:
1302         break;
1303     }
1305     /* Choose where we send the event */
1307     /* canvas->current_item becomes NULL in some cases under Win32
1308     ** (e.g. if the pointer leaves the window).  So this is a hack that
1309     ** Lauris applied to SP to get around the problem.
1310     */
1311     SPCanvasItem* item = NULL;
1312     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1313         item = canvas->grabbed_item;
1314     } else {
1315         item = canvas->current_item;
1316     }
1318     if (canvas->focused_item &&
1319         ((event->type == GDK_KEY_PRESS) ||
1320          (event->type == GDK_KEY_RELEASE) ||
1321          (event->type == GDK_FOCUS_CHANGE))) {
1322         item = canvas->focused_item;
1323     }
1325     /* The event is propagated up the hierarchy (for if someone connected to
1326      * a group instead of a leaf event), and emission is stopped if a
1327      * handler returns TRUE, just like for GtkWidget events.
1328      */
1330     gint finished = FALSE;
1332     while (item && !finished) {
1333         gtk_object_ref (GTK_OBJECT (item));
1334         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1335         SPCanvasItem *parent = item->parent;
1336         gtk_object_unref (GTK_OBJECT (item));
1337         item = parent;
1338     }
1340     return finished;
1343 /**
1344  * Helper that re-picks the current item in the canvas, based on the event's
1345  * coordinates and emits enter/leave events for items as appropriate.
1346  */
1347 static int
1348 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1350     int button_down = 0;
1351     double x, y;
1353     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1354         return FALSE;
1356     int retval = FALSE;
1358     if (canvas->gen_all_enter_events == false) {
1359         // If a button is down, we'll perform enter and leave events on the
1360         // current item, but not enter on any other item.  This is more or
1361         // less like X pointer grabbing for canvas items.
1362         //
1363         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1364                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1366         if (!button_down) canvas->left_grabbed_item = FALSE;
1367     }
1369     /* Save the event in the canvas.  This is used to synthesize enter and
1370      * leave events in case the current item changes.  It is also used to
1371      * re-pick the current item if the current one gets deleted.  Also,
1372      * synthesize an enter event.
1373      */
1374     if (event != &canvas->pick_event) {
1375         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1376             /* these fields have the same offsets in both types of events */
1378             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1379             canvas->pick_event.crossing.window     = event->motion.window;
1380             canvas->pick_event.crossing.send_event = event->motion.send_event;
1381             canvas->pick_event.crossing.subwindow  = NULL;
1382             canvas->pick_event.crossing.x          = event->motion.x;
1383             canvas->pick_event.crossing.y          = event->motion.y;
1384             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1385             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1386             canvas->pick_event.crossing.focus      = FALSE;
1387             canvas->pick_event.crossing.state      = event->motion.state;
1389             /* these fields don't have the same offsets in both types of events */
1391             if (event->type == GDK_MOTION_NOTIFY) {
1392                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1393                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1394             } else {
1395                 canvas->pick_event.crossing.x_root = event->button.x_root;
1396                 canvas->pick_event.crossing.y_root = event->button.y_root;
1397             }
1398         } else {
1399             canvas->pick_event = *event;
1400         }
1401     }
1403     /* Don't do anything else if this is a recursive call */
1404     if (canvas->in_repick) return retval;
1406     /* LeaveNotify means that there is no current item, so we don't look for one */
1407     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1408         /* these fields don't have the same offsets in both types of events */
1410         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1411             x = canvas->pick_event.crossing.x;
1412             y = canvas->pick_event.crossing.y;
1413         } else {
1414             x = canvas->pick_event.motion.x;
1415             y = canvas->pick_event.motion.y;
1416         }
1418         /* world coords */
1419         x += canvas->x0;
1420         y += canvas->y0;
1422         /* find the closest item */
1423         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1424             sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1425         } else {
1426             canvas->new_current_item = NULL;
1427         }
1428     } else {
1429         canvas->new_current_item = NULL;
1430     }
1432     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1433         return retval; /* current item did not change */
1434     }
1436     /* Synthesize events for old and new current items */
1438     if ((canvas->new_current_item != canvas->current_item)
1439         && (canvas->current_item != NULL)
1440         && !canvas->left_grabbed_item) {
1441         GdkEvent new_event;
1442         SPCanvasItem *item;
1444         item = canvas->current_item;
1446         new_event = canvas->pick_event;
1447         new_event.type = GDK_LEAVE_NOTIFY;
1449         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1450         new_event.crossing.subwindow = NULL;
1451         canvas->in_repick = TRUE;
1452         retval = emit_event (canvas, &new_event);
1453         canvas->in_repick = FALSE;
1454     }
1456     if (canvas->gen_all_enter_events == false) {
1457         // new_current_item may have been set to NULL during the call to
1458         // emit_event() above
1459         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1460             canvas->left_grabbed_item = TRUE;
1461             return retval;
1462         }
1463     }
1465     /* Handle the rest of cases */
1467     canvas->left_grabbed_item = FALSE;
1468     canvas->current_item = canvas->new_current_item;
1470     if (canvas->current_item != NULL) {
1471         GdkEvent new_event;
1473         new_event = canvas->pick_event;
1474         new_event.type = GDK_ENTER_NOTIFY;
1475         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1476         new_event.crossing.subwindow = NULL;
1477         retval = emit_event (canvas, &new_event);
1478     }
1480     return retval;
1483 /**
1484  * Button event handler for the canvas.
1485  */
1486 static gint
1487 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1489     SPCanvas *canvas = SP_CANVAS (widget);
1491     int retval = FALSE;
1493     /* dispatch normally regardless of the event's window if an item has
1494        has a pointer grab in effect */
1495     if (!canvas->grabbed_item &&
1496         event->window != SP_CANVAS_WINDOW (canvas))
1497         return retval;
1499     int mask;
1500     switch (event->button) {
1501     case 1:
1502         mask = GDK_BUTTON1_MASK;
1503         break;
1504     case 2:
1505         mask = GDK_BUTTON2_MASK;
1506         break;
1507     case 3:
1508         mask = GDK_BUTTON3_MASK;
1509         break;
1510     case 4:
1511         mask = GDK_BUTTON4_MASK;
1512         break;
1513     case 5:
1514         mask = GDK_BUTTON5_MASK;
1515         break;
1516     default:
1517         mask = 0;
1518     }
1520     switch (event->type) {
1521     case GDK_BUTTON_PRESS:
1522     case GDK_2BUTTON_PRESS:
1523     case GDK_3BUTTON_PRESS:
1524         /* Pick the current item as if the button were not pressed, and
1525          * then process the event.
1526          */
1527         canvas->state = event->state;
1528         pick_current_item (canvas, (GdkEvent *) event);
1529         canvas->state ^= mask;
1530         retval = emit_event (canvas, (GdkEvent *) event);
1531         break;
1533     case GDK_BUTTON_RELEASE:
1534         /* Process the event as if the button were pressed, then repick
1535          * after the button has been released
1536          */
1537         canvas->state = event->state;
1538         retval = emit_event (canvas, (GdkEvent *) event);
1539         event->state ^= mask;
1540         canvas->state = event->state;
1541         pick_current_item (canvas, (GdkEvent *) event);
1542         event->state ^= mask;
1543         break;
1545     default:
1546         g_assert_not_reached ();
1547     }
1549     return retval;
1552 /**
1553  * Scroll event handler for the canvas.
1554  *
1555  * \todo FIXME: generate motion events to re-select items.
1556  */
1557 static gint
1558 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1560     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1563 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1564     gdk_window_get_pointer(w, NULL, NULL, NULL);
1565 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1566     gdk_event_request_motions(event);
1567 #endif
1570 /**
1571  * Motion event handler for the canvas.
1572  */
1573 static int
1574 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1576     int status;
1577     SPCanvas *canvas = SP_CANVAS (widget);
1579     track_latency((GdkEvent *)event);
1581     if (event->window != SP_CANVAS_WINDOW (canvas))
1582         return FALSE;
1584     if (canvas->pixmap_gc == NULL) // canvas being deleted
1585         return FALSE;
1587     canvas->state = event->state;
1588     pick_current_item (canvas, (GdkEvent *) event);
1590     status = emit_event (canvas, (GdkEvent *) event);
1592     if (event->is_hint) {
1593         request_motions(widget->window, event);
1594     }
1596     return status;
1599 static void
1600 sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, int draw_x1, int draw_y1, int draw_x2, int draw_y2, int sw)
1602     GtkWidget *widget = GTK_WIDGET (canvas);
1604     SPCanvasBuf buf;
1605     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1606         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1607     } else {
1608         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1609     }
1611     // Mark the region clean
1612     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1614     buf.buf_rowstride = sw * 4; 
1615     buf.rect.x0 = x0;
1616     buf.rect.y0 = y0;
1617     buf.rect.x1 = x1;
1618     buf.rect.y1 = y1;
1619     buf.visible_rect.x0 = draw_x1;
1620     buf.visible_rect.y0 = draw_y1;
1621     buf.visible_rect.x1 = draw_x2;
1622     buf.visible_rect.y1 = draw_y2;
1623     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1624     buf.bg_color = (((color->red & 0xff00) << 8)
1625                     | (color->green & 0xff00)
1626                     | (color->blue >> 8));
1627     buf.is_empty = true;
1629     buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1631     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1632         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1633     }
1635 #if ENABLE_LCMS
1636     cmsHTRANSFORM transf = 0;
1637     long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1638     if ( fromDisplay ) {
1639         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1640     } else {
1641         transf = Inkscape::colorprofile_get_display_transform();
1642     }
1643 #endif // ENABLE_LCMS
1645     if (buf.is_empty) {
1646 #if ENABLE_LCMS
1647         if ( transf && canvas->enable_cms_display_adj ) {
1648             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1649         }
1650 #endif // ENABLE_LCMS
1651         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1652         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1653                             canvas->pixmap_gc,
1654                             TRUE,
1655                             x0 - canvas->x0, y0 - canvas->y0,
1656                             x1 - x0, y1 - y0);
1657     } else {
1659 #if ENABLE_LCMS
1660         if ( transf && canvas->enable_cms_display_adj ) {
1661             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1662                 guchar* p = buf.buf + (sw * 3) * yy;
1663                 cmsDoTransform( transf, p, p, (x1 - x0) );
1664             }
1665         }
1666 #endif // ENABLE_LCMS
1668 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1669 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1670 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1671 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1672 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1673 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1675 ///#define CANVAS_OUTPUT_VIA_CAIRO
1677 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1679         buf.cst = cairo_image_surface_create_for_data (
1680             buf.buf,
1681             CAIRO_FORMAT_ARGB32,  // unpacked, i.e. 32 bits! one byte is unused
1682             x1 - x0, y1 - y0,
1683             buf.buf_rowstride
1684             );
1685         cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1686         cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1687         cairo_paint (window_ct);
1688         cairo_destroy (window_ct);
1689         cairo_surface_finish (buf.cst);
1690         cairo_surface_destroy (buf.cst);
1692 #else
1694         NRPixBlock b3;
1695         nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1697         NRPixBlock b4;
1698         nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1699                                   buf.buf,
1700                                   buf.buf_rowstride,
1701                                   FALSE, FALSE);
1703         // this does the 32->24 squishing, using an assembler routine:
1704         nr_blit_pixblock_pixblock (&b3, &b4);
1706         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1707                                       canvas->pixmap_gc,
1708                                       x0 - canvas->x0, y0 - canvas->y0,
1709                                       x1 - x0, y1 - y0,
1710                                       GDK_RGB_DITHER_MAX,
1711                                       NR_PIXBLOCK_PX(&b3),
1712                                       sw * 3,
1713                                       x0 - canvas->x0, y0 - canvas->y0);
1715         nr_pixblock_release (&b3);
1716         nr_pixblock_release (&b4);
1717 #endif
1718     }
1720     cairo_surface_t *cst = cairo_get_target(buf.ct);
1721     cairo_destroy (buf.ct);
1722     cairo_surface_finish (cst);
1723     cairo_surface_destroy (cst);
1725     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1726         nr_pixelstore_256K_free (buf.buf);
1727     } else {
1728         nr_pixelstore_1M_free (buf.buf);
1729     }
1732 struct PaintRectSetup {
1733     SPCanvas* canvas;
1734     NRRectL big_rect;
1735     GTimeVal start_time;
1736     int max_pixels;
1737     NR::Point mouse_loc;
1738 };
1740 /**
1741  * Paint the given rect, recursively subdividing the region until it is the size of a single
1742  * buffer.
1743  *
1744  * @return true if the drawing completes
1745  */
1746 static int
1747 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1749     GTimeVal now;
1750     g_get_current_time (&now);
1752     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1753         + (now.tv_usec - setup->start_time.tv_usec);
1755     // Allow only very fast buffers to be run together;
1756     // as soon as the total redraw time exceeds 1ms, cancel;
1757     // this returns control to the idle loop and allows Inkscape to process user input
1758     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1759     // it will get back and finish painting what remains to paint.
1760     if (elapsed > 1000) {
1762         // Interrupting redraw isn't always good.
1763         // For example, when you drag one node of a big path, only the buffer containing
1764         // the mouse cursor will be redrawn again and again, and the rest of the path
1765         // will remain stale because Inkscape never has enough idle time to redraw all
1766         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1767         // If this limit is set, and if we have aborted redraw more times than is allowed,
1768         // interrupting is blocked and we're forced to redraw full screen once
1769         // (after which we can again interrupt forced_redraw_limit times).
1770         if (setup->canvas->forced_redraw_limit < 0 ||
1771             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1773             if (setup->canvas->forced_redraw_limit != -1) {
1774                 setup->canvas->forced_redraw_count++;
1775             }
1777             return false;
1778         }
1779     }
1781     // Find the optimal buffer dimensions
1782     int bw = this_rect.x1 - this_rect.x0;
1783     int bh = this_rect.y1 - this_rect.y0;
1784     if ((bw < 1) || (bh < 1))
1785         return 0;
1787     if (bw * bh < setup->max_pixels) {
1788         // We are small enough
1789         sp_canvas_paint_single_buffer (setup->canvas,
1790                                        this_rect.x0, this_rect.y0,
1791                                        this_rect.x1, this_rect.y1,
1792                                        setup->big_rect.x0, setup->big_rect.y0,
1793                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1794         return 1;
1795     }
1797     NRRectL lo = this_rect;
1798     NRRectL hi = this_rect;
1800 /*
1801 This test determines the redraw strategy:
1803 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1804 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1805 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1806 and seems to be faster for drawings with many smaller objects at zoom-out.
1808 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1809 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1810 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1811 faster.
1813 The default for now is the strips mode.
1814 */
1815     if (bw < bh || bh < 2 * TILE_SIZE) {
1816         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1817         int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1818         // Make sure that mid lies on a tile boundary
1819         mid = (mid / TILE_SIZE) * TILE_SIZE;
1821         lo.x1 = mid;
1822         hi.x0 = mid;
1824         if (setup->mouse_loc[NR::X] < mid) {
1825             // Always paint towards the mouse first
1826             return sp_canvas_paint_rect_internal(setup, lo)
1827                 && sp_canvas_paint_rect_internal(setup, hi);
1828         } else {
1829             return sp_canvas_paint_rect_internal(setup, hi)
1830                 && sp_canvas_paint_rect_internal(setup, lo);
1831         }
1832     } else {
1833         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1834         int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1835         // Make sure that mid lies on a tile boundary
1836         mid = (mid / TILE_SIZE) * TILE_SIZE;
1838         lo.y1 = mid;
1839         hi.y0 = mid;
1841         if (setup->mouse_loc[NR::Y] < mid) {
1842             // Always paint towards the mouse first
1843             return sp_canvas_paint_rect_internal(setup, lo)
1844                 && sp_canvas_paint_rect_internal(setup, hi);
1845         } else {
1846             return sp_canvas_paint_rect_internal(setup, hi)
1847                 && sp_canvas_paint_rect_internal(setup, lo);
1848         }
1849     }
1853 /**
1854  * Helper that draws a specific rectangular part of the canvas.
1855  *
1856  * @return true if the rectangle painting succeeds.
1857  */
1858 static bool
1859 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1861     g_return_val_if_fail (!canvas->need_update, false);
1863     NRRectL rect;
1864     rect.x0 = xx0;
1865     rect.x1 = xx1;
1866     rect.y0 = yy0;
1867     rect.y1 = yy1;
1869     // Clip rect-to-draw by the current visible area
1870     rect.x0 = MAX (rect.x0, canvas->x0);
1871     rect.y0 = MAX (rect.y0, canvas->y0);
1872     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1873     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1875 #ifdef DEBUG_REDRAW
1876     // paint the area to redraw yellow
1877     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1878     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1879                         canvas->pixmap_gc,
1880                         TRUE,
1881                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1882                         rect.x1 - rect.x0, rect.y1 - rect.y0);
1883 #endif
1885     PaintRectSetup setup;
1887     setup.canvas = canvas;
1888     setup.big_rect = rect;
1890     // Save the mouse location
1891     gint x, y;
1892     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1893     setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1895     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1896         // use 256K as a compromise to not slow down gradients
1897         // 256K is the cached buffer and we need 4 channels
1898         setup.max_pixels = 65536; // 256K/4
1899     } else {
1900         // paths only, so 1M works faster
1901         // 1M is the cached buffer and we need 4 channels
1902         setup.max_pixels = 262144; 
1903     }
1905     // Start the clock
1906     g_get_current_time(&(setup.start_time));
1908     // Go
1909     return sp_canvas_paint_rect_internal(&setup, rect);
1912 /**
1913  * Force a full redraw after a specified number of interrupted redraws
1914  */
1915 void
1916 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1917   g_return_if_fail(canvas != NULL);
1919   canvas->forced_redraw_limit = count;
1920   canvas->forced_redraw_count = 0;
1923 /**
1924  * End forced full redraw requests
1925  */
1926 void
1927 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1928   g_return_if_fail(canvas != NULL);
1930   canvas->forced_redraw_limit = -1;
1933 /**
1934  * The canvas widget's expose callback.
1935  */
1936 static gint
1937 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1939     SPCanvas *canvas = SP_CANVAS (widget);
1941     if (!GTK_WIDGET_DRAWABLE (widget) ||
1942         (event->window != SP_CANVAS_WINDOW (canvas)))
1943         return FALSE;
1945     int n_rects;
1946     GdkRectangle *rects;
1947     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1949     for (int i = 0; i < n_rects; i++) {
1950         NRRectL rect;
1952         rect.x0 = rects[i].x + canvas->x0;
1953         rect.y0 = rects[i].y + canvas->y0;
1954         rect.x1 = rect.x0 + rects[i].width;
1955         rect.y1 = rect.y0 + rects[i].height;
1957         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1958     }
1960     if (n_rects > 0)
1961         g_free (rects);
1963     return FALSE;
1966 /**
1967  * The canvas widget's keypress callback.
1968  */
1969 static gint
1970 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1972     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1975 /**
1976  * Crossing event handler for the canvas.
1977  */
1978 static gint
1979 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1981     SPCanvas *canvas = SP_CANVAS (widget);
1983     if (event->window != SP_CANVAS_WINDOW (canvas))
1984         return FALSE;
1986     canvas->state = event->state;
1987     return pick_current_item (canvas, (GdkEvent *) event);
1990 /**
1991  * Focus in handler for the canvas.
1992  */
1993 static gint
1994 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1996     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1998     SPCanvas *canvas = SP_CANVAS (widget);
2000     if (canvas->focused_item) {
2001         return emit_event (canvas, (GdkEvent *) event);
2002     } else {
2003         return FALSE;
2004     }
2007 /**
2008  * Focus out handler for the canvas.
2009  */
2010 static gint
2011 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2013     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2015     SPCanvas *canvas = SP_CANVAS (widget);
2017     if (canvas->focused_item)
2018         return emit_event (canvas, (GdkEvent *) event);
2019     else
2020         return FALSE;
2023 /**
2024  * Helper that repaints the areas in the canvas that need it.
2025  *
2026  * @return true if all the dirty parts have been redrawn
2027  */
2028 static int
2029 paint (SPCanvas *canvas)
2031     if (canvas->need_update) {
2032         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2033         canvas->need_update = FALSE;
2034     }
2036     if (!canvas->need_redraw)
2037         return TRUE;
2039     Gdk::Region to_paint;
2041     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2042         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2043             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2045             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2046                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2047                                    TILE_SIZE, TILE_SIZE));
2048             }
2050         }
2051     }
2053     if (!to_paint.empty()) {
2054         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2055         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2056         for (Iter i=rect.begin(); i != rect.end(); ++i) {
2057             int x0 = (*i).get_x();
2058             int y0 = (*i).get_y();
2059             int x1 = x0 + (*i).get_width();
2060             int y1 = y0 + (*i).get_height();
2061             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2062                 // Aborted
2063                 return FALSE;
2064             };
2065         }
2066     }
2068     canvas->need_redraw = FALSE;
2070     // we've had a full unaborted redraw, reset the full redraw counter
2071     if (canvas->forced_redraw_limit != -1) {
2072         canvas->forced_redraw_count = 0;
2073     }
2075     return TRUE;
2078 /**
2079  * Helper that invokes update, paint, and repick on canvas.
2080  */
2081 static int
2082 do_update (SPCanvas *canvas)
2084     if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2085         return TRUE;
2087     /* Cause the update if necessary */
2088     if (canvas->need_update) {
2089         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2090         canvas->need_update = FALSE;
2091     }
2093     /* Paint if able to */
2094     if (GTK_WIDGET_DRAWABLE (canvas)) {
2095             return paint (canvas);
2096     }
2098     /* Pick new current item */
2099     while (canvas->need_repick) {
2100         canvas->need_repick = FALSE;
2101         pick_current_item (canvas, &canvas->pick_event);
2102     }
2104     return TRUE;
2107 /**
2108  * Idle handler for the canvas that deals with pending updates and redraws.
2109  */
2110 static gint
2111 idle_handler (gpointer data)
2113     GDK_THREADS_ENTER ();
2115     SPCanvas *canvas = SP_CANVAS (data);
2117     int const ret = do_update (canvas);
2119     if (ret) {
2120         /* Reset idle id */
2121         canvas->idle_id = 0;
2122     }
2124     GDK_THREADS_LEAVE ();
2126     return !ret;
2129 /**
2130  * Convenience function to add an idle handler to a canvas.
2131  */
2132 static void
2133 add_idle (SPCanvas *canvas)
2135     if (canvas->idle_id != 0)
2136         return;
2138     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2141 /**
2142  * Returns the root group of the specified canvas.
2143  */
2144 SPCanvasGroup *
2145 sp_canvas_root (SPCanvas *canvas)
2147     g_return_val_if_fail (canvas != NULL, NULL);
2148     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2150     return SP_CANVAS_GROUP (canvas->root);
2153 /**
2154  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2155  */
2156 void
2157 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2159     g_return_if_fail (canvas != NULL);
2160     g_return_if_fail (SP_IS_CANVAS (canvas));
2162     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2163     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2164     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the 
2165     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2167     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2168     canvas->dy0 = cy;
2169     canvas->x0 = ix;
2170     canvas->y0 = iy;
2172     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2174     if (!clear) {
2175         // scrolling without zoom; redraw only the newly exposed areas
2176         if ((dx != 0) || (dy != 0)) {
2177             canvas->is_scrolling = is_scrolling;
2178             if (GTK_WIDGET_REALIZED (canvas)) {
2179                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2180             }
2181         }
2182     } else {
2183         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2184     }
2187 /**
2188  * Updates canvas if necessary.
2189  */
2190 void
2191 sp_canvas_update_now (SPCanvas *canvas)
2193     g_return_if_fail (canvas != NULL);
2194     g_return_if_fail (SP_IS_CANVAS (canvas));
2196     if (!(canvas->need_update ||
2197           canvas->need_redraw))
2198         return;
2200     do_update (canvas);
2203 /**
2204  * Update callback for canvas widget.
2205  */
2206 static void
2207 sp_canvas_request_update (SPCanvas *canvas)
2209     canvas->need_update = TRUE;
2210     add_idle (canvas);
2213 /**
2214  * Forces redraw of rectangular canvas area.
2215  */
2216 void
2217 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2219     NRRectL bbox;
2220     NRRectL visible;
2221     NRRectL clip;
2223     g_return_if_fail (canvas != NULL);
2224     g_return_if_fail (SP_IS_CANVAS (canvas));
2226     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2227     if ((x0 >= x1) || (y0 >= y1)) return;
2229     bbox.x0 = x0;
2230     bbox.y0 = y0;
2231     bbox.x1 = x1;
2232     bbox.y1 = y1;
2234     visible.x0 = canvas->x0;
2235     visible.y0 = canvas->y0;
2236     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2237     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2239     nr_rect_l_intersect (&clip, &bbox, &visible);
2241     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2242     add_idle (canvas);
2245 /**
2246  * Sets world coordinates from win and canvas.
2247  */
2248 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2250     g_return_if_fail (canvas != NULL);
2251     g_return_if_fail (SP_IS_CANVAS (canvas));
2253     if (worldx) *worldx = canvas->x0 + winx;
2254     if (worldy) *worldy = canvas->y0 + winy;
2257 /**
2258  * Sets win coordinates from world and canvas.
2259  */
2260 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2262     g_return_if_fail (canvas != NULL);
2263     g_return_if_fail (SP_IS_CANVAS (canvas));
2265     if (winx) *winx = worldx - canvas->x0;
2266     if (winy) *winy = worldy - canvas->y0;
2269 /**
2270  * Converts point from win to world coordinates.
2271  */
2272 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2274     g_assert (canvas != NULL);
2275     g_assert (SP_IS_CANVAS (canvas));
2277     return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2280 /**
2281  * Converts point from world to win coordinates.
2282  */
2283 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2285     g_assert (canvas != NULL);
2286     g_assert (SP_IS_CANVAS (canvas));
2288     return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2291 /**
2292  * Returns true if point given in world coordinates is inside window.
2293  */
2294 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2296     g_assert( canvas != NULL );
2297     g_assert(SP_IS_CANVAS(canvas));
2299     using NR::X;
2300     using NR::Y;
2301     GtkWidget const &w = *GTK_WIDGET(canvas);
2302     return ( ( canvas->x0 <= world[X] )  &&
2303              ( canvas->y0 <= world[Y] )  &&
2304              ( world[X] < canvas->x0 + w.allocation.width )  &&
2305              ( world[Y] < canvas->y0 + w.allocation.height ) );
2308 /**
2309  * Return canvas window coordinates as NR::Rect.
2310  */
2311 NR::Rect SPCanvas::getViewbox() const
2313     GtkWidget const *w = GTK_WIDGET(this);
2314     return NR::Rect(NR::Point(dx0, dy0),
2315                     NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2318 /**
2319  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2320  */
2321 NR::IRect SPCanvas::getViewboxIntegers() const
2323     GtkWidget const *w = GTK_WIDGET(this);
2324     return NR::IRect(NR::IPoint(x0, y0),
2325                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2328 inline int sp_canvas_tile_floor(int x)
2330     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2333 inline int sp_canvas_tile_ceil(int x)
2335     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2338 /**
2339  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2340  */
2341 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2343     if ( nl >= nr || nt >= nb ) {
2344         if ( canvas->tiles ) g_free(canvas->tiles);
2345         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2346         canvas->tileH=canvas->tileV=0;
2347         canvas->tiles=NULL;
2348         return;
2349     }
2350     int tl=sp_canvas_tile_floor(nl);
2351     int tt=sp_canvas_tile_floor(nt);
2352     int tr=sp_canvas_tile_ceil(nr);
2353     int tb=sp_canvas_tile_ceil(nb);
2355     int nh = tr-tl, nv = tb-tt;
2356     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2357     for (int i=tl; i<tr; i++) {
2358         for (int j=tt; j<tb; j++) {
2359             int ind = (i-tl) + (j-tt)*nh;
2360             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2361                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2362             } else {
2363                 ntiles[ind]=0; // newly exposed areas get 0
2364             }
2365         }
2366     }
2367     if ( canvas->tiles ) g_free(canvas->tiles);
2368     canvas->tiles=ntiles;
2369     canvas->tLeft=tl;
2370     canvas->tTop=tt;
2371     canvas->tRight=tr;
2372     canvas->tBottom=tb;
2373     canvas->tileH=nh;
2374     canvas->tileV=nv;
2377 /*
2378  * Helper that queues a canvas rectangle for redraw
2379  */
2380 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2381     canvas->need_redraw = TRUE;
2383     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2386 /**
2387  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2388  */
2389 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2391     if ( nl >= nr || nt >= nb ) {
2392         return;
2393     }
2394     int tl=sp_canvas_tile_floor(nl);
2395     int tt=sp_canvas_tile_floor(nt);
2396     int tr=sp_canvas_tile_ceil(nr);
2397     int tb=sp_canvas_tile_ceil(nb);
2398     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2399     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2400     if ( tr > canvas->tRight ) tr=canvas->tRight;
2401     if ( tt < canvas->tTop ) tt=canvas->tTop;
2402     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2404     for (int i=tl; i<tr; i++) {
2405         for (int j=tt; j<tb; j++) {
2406             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2407         }
2408     }
2412 /*
2413   Local Variables:
2414   mode:c++
2415   c-file-style:"stroustrup"
2416   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2417   indent-tabs-mode:nil
2418   fill-column:99
2419   End:
2420 */
2421 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :