Code

disable motion hints again
[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"
47 // GTK_CHECK_VERSION returns false on failure
48 #define HAS_GDK_EVENT_REQUEST_MOTIONS FALSE && GTK_CHECK_VERSION(2, 12, 0)
50 // gtk_check_version returns non-NULL on failure
51 static bool const HAS_BROKEN_MOTION_HINTS =
52   gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS;
54 // Define this to visualize the regions to be redrawn
55 //#define DEBUG_REDRAW 1;
57 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
58 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
59 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
60 #define TILE_SIZE 16
62 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
64 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
66 enum {
67     SP_CANVAS_ITEM_VISIBLE = 1 << 7,
68     SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
69     SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
70 };
72 /**
73  * A group of Items.
74  */
75 struct SPCanvasGroup {
76     SPCanvasItem item;
78     GList *items, *last;
79 };
81 /**
82  * The SPCanvasGroup vtable.
83  */
84 struct SPCanvasGroupClass {
85     SPCanvasItemClass parent_class;
86 };
88 /**
89  * The SPCanvas vtable.
90  */
91 struct SPCanvasClass {
92     GtkWidgetClass parent_class;
93 };
95 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
96 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
98 /* SPCanvasItem */
100 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
103 static void sp_canvas_request_update (SPCanvas *canvas);
105 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
106 static void sp_canvas_item_init (SPCanvasItem *item);
107 static void sp_canvas_item_dispose (GObject *object);
108 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
110 static int emit_event (SPCanvas *canvas, GdkEvent *event);
112 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
114 static GtkObjectClass *item_parent_class;
116 /**
117  * Registers the SPCanvasItem class with Glib and returns its type number.
118  */
119 GType
120 sp_canvas_item_get_type (void)
122     static GType type = 0;
123     if (!type) {
124         static GTypeInfo const info = {
125             sizeof (SPCanvasItemClass),
126             NULL, NULL,
127             (GClassInitFunc) sp_canvas_item_class_init,
128             NULL, NULL,
129             sizeof (SPCanvasItem),
130             0,
131             (GInstanceInitFunc) sp_canvas_item_init,
132             NULL
133         };
134         type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
135     }
137     return type;
140 /**
141  * Initializes the SPCanvasItem vtable and the "event" signal.
142  */
143 static void
144 sp_canvas_item_class_init (SPCanvasItemClass *klass)
146     GObjectClass *object_class = (GObjectClass *) klass;
148     /* fixme: Derive from GObject */
149     item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
151     item_signals[ITEM_EVENT] = g_signal_new ("event",
152                                              G_TYPE_FROM_CLASS (klass),
153                                              G_SIGNAL_RUN_LAST,
154                                              ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
155                                              NULL, NULL,
156                                              sp_marshal_BOOLEAN__POINTER,
157                                              G_TYPE_BOOLEAN, 1,
158                                              GDK_TYPE_EVENT);
160     object_class->dispose = sp_canvas_item_dispose;
163 /**
164  * Callback for initialization of SPCanvasItem.
165  */
166 static void
167 sp_canvas_item_init (SPCanvasItem *item)
169     item->flags |= SP_CANVAS_ITEM_VISIBLE;
170     item->xform = NR::Matrix(NR::identity());
173 /**
174  * Constructs new SPCanvasItem on SPCanvasGroup.
175  */
176 SPCanvasItem *
177 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
179     va_list args;
181     g_return_val_if_fail (parent != NULL, NULL);
182     g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
183     g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
185     SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
187     va_start (args, first_arg_name);
188     sp_canvas_item_construct (item, parent, first_arg_name, args);
189     va_end (args);
191     return item;
194 /**
195  * Sets up the newly created SPCanvasItem.
196  *
197  * We make it static for encapsulation reasons since it was nowhere used.
198  */
199 static void
200 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
202     g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
203     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
205     item->parent = SP_CANVAS_ITEM (parent);
206     item->canvas = item->parent->canvas;
208     g_object_set_valist (G_OBJECT (item), first_arg_name, args);
210     group_add (SP_CANVAS_GROUP (item->parent), item);
212     sp_canvas_item_request_update (item);
215 /**
216  * Helper function that requests redraw only if item's visible flag is set.
217  */
218 static void
219 redraw_if_visible (SPCanvasItem *item)
221     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
222         int x0 = (int)(item->x1);
223         int x1 = (int)(item->x2);
224         int y0 = (int)(item->y1);
225         int y1 = (int)(item->y2);
227         if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
228             sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
229         }
230     }
233 /**
234  * Callback that removes item from all referers and destroys it.
235  */
236 static void
237 sp_canvas_item_dispose (GObject *object)
239     SPCanvasItem *item = SP_CANVAS_ITEM (object);
241     // Hack: if this is a ctrlrect, move it to 0,0;
242     // this redraws only the stroke of the rect to be deleted,
243     // avoiding redraw of the entire area
244     if (SP_IS_CTRLRECT(item)) {
245         SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
246         SP_CTRLRECT(object)->update(item->xform, 0);
247     } else {
248         redraw_if_visible (item);
249     }
250     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
252     if (item == item->canvas->current_item) {
253         item->canvas->current_item = NULL;
254         item->canvas->need_repick = TRUE;
255     }
257     if (item == item->canvas->new_current_item) {
258         item->canvas->new_current_item = NULL;
259         item->canvas->need_repick = TRUE;
260     }
262     if (item == item->canvas->grabbed_item) {
263         item->canvas->grabbed_item = NULL;
264         gdk_pointer_ungrab (GDK_CURRENT_TIME);
265     }
267     if (item == item->canvas->focused_item)
268         item->canvas->focused_item = NULL;
270     if (item->parent) {
271         group_remove (SP_CANVAS_GROUP (item->parent), item);
272     }
274     G_OBJECT_CLASS (item_parent_class)->dispose (object);
277 /**
278  * Helper function to update item and its children.
279  *
280  * NB! affine is parent2canvas.
281  */
282 static void
283 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
285     /* Apply the child item's transform */
286     NR::Matrix child_affine = item->xform * affine;
288     /* apply object flags to child flags */
289     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
291     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
292         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
294     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
295         child_flags |= SP_CANVAS_UPDATE_AFFINE;
297     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
298         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
299             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
300     }
302     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
303     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
306 /**
307  * Helper function to invoke the point method of the item.
308  *
309  * The argument x, y should be in the parent's item-relative coordinate
310  * system.  This routine applies the inverse of the item's transform,
311  * maintaining the affine invariant.
312  */
313 static double
314 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
316     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
317         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
319     return NR_HUGE;
322 /**
323  * Makes the item's affine transformation matrix be equal to the specified
324  * matrix.
325  *
326  * @item: A canvas item.
327  * @affine: An affine transformation matrix.
328  */
329 void
330 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine)
332     item->xform = affine;
334     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
335         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
336         if (item->parent != NULL) {
337             sp_canvas_item_request_update (item->parent);
338         } else {
339             sp_canvas_request_update (item->canvas);
340         }
341     }
343     item->canvas->need_repick = TRUE;
346 /**
347  * Convenience function to reorder items in a group's child list.
348  *
349  * This puts the specified link after the "before" link.
350  */
351 static void
352 put_item_after (GList *link, GList *before)
354     if (link == before)
355         return;
357     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
359     if (before == NULL) {
360         if (link == parent->items) return;
362         link->prev->next = link->next;
364         if (link->next) {
365             link->next->prev = link->prev;
366         } else {
367             parent->last = link->prev;
368         }
370         link->prev = before;
371         link->next = parent->items;
372         link->next->prev = link;
373         parent->items = link;
374     } else {
375         if ((link == parent->last) && (before == parent->last->prev))
376             return;
378         if (link->next)
379             link->next->prev = link->prev;
381         if (link->prev)
382             link->prev->next = link->next;
383         else {
384             parent->items = link->next;
385             parent->items->prev = NULL;
386         }
388         link->prev = before;
389         link->next = before->next;
391         link->prev->next = link;
393         if (link->next)
394             link->next->prev = link;
395         else
396             parent->last = link;
397     }
401 /**
402  * Raises the item in its parent's stack by the specified number of positions.
403  *
404  * \param item A canvas item.
405  * \param positions Number of steps to raise the item.
406  *
407  * If the number of positions is greater than the distance to the top of the
408  * stack, then the item is put at the top.
409  */
410 void
411 sp_canvas_item_raise (SPCanvasItem *item, int positions)
413     g_return_if_fail (item != NULL);
414     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
415     g_return_if_fail (positions >= 0);
417     if (!item->parent || positions == 0)
418         return;
420     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
421     GList *link = g_list_find (parent->items, item);
422     g_assert (link != NULL);
424     GList *before;
425     for (before = link; positions && before; positions--)
426         before = before->next;
428     if (!before)
429         before = parent->last;
431     put_item_after (link, before);
433     redraw_if_visible (item);
434     item->canvas->need_repick = TRUE;
438 /**
439  * Lowers the item in its parent's stack by the specified number of positions.
440  *
441  * \param item A canvas item.
442  * \param positions Number of steps to lower the item.
443  *
444  * If the number of positions is greater than the distance to the bottom of the
445  * stack, then the item is put at the bottom.
446  **/
447 void
448 sp_canvas_item_lower (SPCanvasItem *item, int positions)
450     g_return_if_fail (item != NULL);
451     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
452     g_return_if_fail (positions >= 1);
454     if (!item->parent || positions == 0)
455         return;
457     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
458     GList *link = g_list_find (parent->items, item);
459     g_assert (link != NULL);
461     GList *before;
462     if (link->prev)
463         for (before = link->prev; positions && before; positions--)
464             before = before->prev;
465     else
466         before = NULL;
468     put_item_after (link, before);
470     redraw_if_visible (item);
471     item->canvas->need_repick = TRUE;
474 /**
475  * Sets visible flag on item and requests a redraw.
476  */
477 void
478 sp_canvas_item_show (SPCanvasItem *item)
480     g_return_if_fail (item != NULL);
481     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
483     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
484         return;
486     item->flags |= SP_CANVAS_ITEM_VISIBLE;
488     int x0 = (int)(item->x1);
489     int x1 = (int)(item->x2);
490     int y0 = (int)(item->y1);
491     int y1 = (int)(item->y2);
493     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
494         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
495         item->canvas->need_repick = TRUE;
496     }
499 /**
500  * Clears visible flag on item and requests a redraw.
501  */
502 void
503 sp_canvas_item_hide (SPCanvasItem *item)
505     g_return_if_fail (item != NULL);
506     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
508     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
509         return;
511     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
513     int x0 = (int)(item->x1);
514     int x1 = (int)(item->x2);
515     int y0 = (int)(item->y1);
516     int y1 = (int)(item->y2);
518     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
519         sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
520         item->canvas->need_repick = TRUE;
521     }
524 /**
525  * Grab item under cursor.
526  *
527  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
528  */
529 int
530 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
532     g_return_val_if_fail (item != NULL, -1);
533     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
534     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
536     if (item->canvas->grabbed_item)
537         return -1;
539     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
540         return -1;
542     if (HAS_BROKEN_MOTION_HINTS && ( event_mask & GDK_POINTER_MOTION_HINT_MASK )) {
543         event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
544         event_mask |= GDK_POINTER_MOTION_MASK;
545     }
547     /* fixme: Top hack (Lauris) */
548     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
549     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
550     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
551                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
552                       NULL, cursor, etime);
554     item->canvas->grabbed_item = item;
555     item->canvas->grabbed_event_mask = event_mask;
556     item->canvas->current_item = item; /* So that events go to the grabbed item */
558     return 0;
561 /**
562  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
563  * mouse.
564  *
565  * \param item A canvas item that holds a grab.
566  * \param etime The timestamp for ungrabbing the mouse.
567  */
568 void
569 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
571     g_return_if_fail (item != NULL);
572     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
574     if (item->canvas->grabbed_item != item)
575         return;
577     item->canvas->grabbed_item = NULL;
579     gdk_pointer_ungrab (etime);
582 /**
583  * Returns the product of all transformation matrices from the root item down
584  * to the item.
585  */
586 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
588     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
590     NR::Matrix affine = NR::identity();
592     while (item) {
593         affine *= item->xform;
594         item = item->parent;
595     }
596     return affine;
599 /**
600  * Helper that returns true iff item is descendant of parent.
601  */
602 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
604     while (item) {
605         if (item == parent)
606             return true;
607         item = item->parent;
608     }
610     return false;
613 /**
614  * Focus canvas, and item under cursor if it is not already focussed.
615  */
616 void
617 sp_canvas_item_grab_focus (SPCanvasItem *item)
619     g_return_if_fail (item != NULL);
620     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
621     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
623     SPCanvasItem *focused_item = item->canvas->focused_item;
625     if (focused_item) {
626         GdkEvent ev;
627         ev.focus_change.type = GDK_FOCUS_CHANGE;
628         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
629         ev.focus_change.send_event = FALSE;
630         ev.focus_change.in = FALSE;
632         emit_event (item->canvas, &ev);
633     }
635     item->canvas->focused_item = item;
636     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
638     if (focused_item) {
639         GdkEvent ev;
640         ev.focus_change.type = GDK_FOCUS_CHANGE;
641         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
642         ev.focus_change.send_event = FALSE;
643         ev.focus_change.in = TRUE;
645         emit_event (item->canvas, &ev);
646     }
649 /**
650  * Requests that the canvas queue an update for the specified item.
651  *
652  * To be used only by item implementations.
653  */
654 void
655 sp_canvas_item_request_update (SPCanvasItem *item)
657     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
658         return;
660     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
662     if (item->parent != NULL) {
663         /* Recurse up the tree */
664         sp_canvas_item_request_update (item->parent);
665     } else {
666         /* Have reached the top of the tree, make sure the update call gets scheduled. */
667         sp_canvas_request_update (item->canvas);
668     }
671 /**
672  * Returns position of item in group.
673  */
674 gint sp_canvas_item_order (SPCanvasItem * item)
676     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
679 /* SPCanvasGroup */
681 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
682 static void sp_canvas_group_init (SPCanvasGroup *group);
683 static void sp_canvas_group_destroy (GtkObject *object);
685 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
686 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
687 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
689 static SPCanvasItemClass *group_parent_class;
691 /**
692  * Registers SPCanvasGroup class with Gtk and returns its type number.
693  */
694 GType sp_canvas_group_get_type(void)
696     static GType type = 0;
697     if (!type) {
698         GTypeInfo info = {
699             sizeof(SPCanvasGroupClass),
700             0, // base_init
701             0, // base_finalize
702             (GClassInitFunc)sp_canvas_group_class_init,
703             0, // class_finalize
704             0, // class_data
705             sizeof(SPCanvasGroup),
706             0, // n_preallocs
707             (GInstanceInitFunc)sp_canvas_group_init,
708             0 // value_table
709         };
710         type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
711     }
712     return type;
715 /**
716  * Class initialization function for SPCanvasGroupClass
717  */
718 static void
719 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
721     GtkObjectClass *object_class = (GtkObjectClass *) klass;
722     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
724     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
726     object_class->destroy = sp_canvas_group_destroy;
728     item_class->update = sp_canvas_group_update;
729     item_class->render = sp_canvas_group_render;
730     item_class->point = sp_canvas_group_point;
733 /**
734  * Callback. Empty.
735  */
736 static void
737 sp_canvas_group_init (SPCanvasGroup */*group*/)
739     /* Nothing here */
742 /**
743  * Callback that destroys all items in group and calls group's virtual
744  * destroy() function.
745  */
746 static void
747 sp_canvas_group_destroy (GtkObject *object)
749     g_return_if_fail (object != NULL);
750     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
752     SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
754     GList *list = group->items;
755     while (list) {
756         SPCanvasItem *child = (SPCanvasItem *)list->data;
757         list = list->next;
759         gtk_object_destroy (GTK_OBJECT (child));
760     }
762     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
763         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
766 /**
767  * Update handler for canvas groups
768  */
769 static void
770 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
772     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
773     NR::ConvexHull corners(NR::Point(0, 0));
774     bool empty=true;
776     for (GList *list = group->items; list; list = list->next) {
777         SPCanvasItem *i = (SPCanvasItem *)list->data;
779         sp_canvas_item_invoke_update (i, affine, flags);
781         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
782             if (empty) {
783                 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
784                 empty = false;
785             } else {
786                 corners.add(NR::Point(i->x1, i->y1));
787             }
788             corners.add(NR::Point(i->x2, i->y2));
789         }
790     }
792     NR::Maybe<NR::Rect> const bounds = corners.bounds();
793     if (bounds) {
794         item->x1 = bounds->min()[NR::X];
795         item->y1 = bounds->min()[NR::Y];
796         item->x2 = bounds->max()[NR::X];
797         item->y2 = bounds->max()[NR::Y];
798     } else {
799         // FIXME ?
800         item->x1 = item->x2 = item->y1 = item->y2 = 0;
801     }
804 /**
805  * Point handler for canvas groups.
806  */
807 static double
808 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
810     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
811     double const x = p[NR::X];
812     double const y = p[NR::Y];
813     int x1 = (int)(x - item->canvas->close_enough);
814     int y1 = (int)(y - item->canvas->close_enough);
815     int x2 = (int)(x + item->canvas->close_enough);
816     int y2 = (int)(y + item->canvas->close_enough);
818     double best = 0.0;
819     *actual_item = NULL;
821     double dist = 0.0;
823     for (GList *list = group->items; list; list = list->next) {
824         SPCanvasItem *child = (SPCanvasItem *)list->data;
826         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
827             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
829             int has_point;
830             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
831                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
832                 has_point = TRUE;
833             } else
834                 has_point = FALSE;
836             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
837                 best = dist;
838                 *actual_item = point_item;
839             }
840         }
841     }
843     return best;
846 /**
847  * Renders all visible canvas group items in buf rectangle.
848  */
849 static void
850 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
852     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
854     for (GList *list = group->items; list; list = list->next) {
855         SPCanvasItem *child = (SPCanvasItem *)list->data;
856         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
857             if ((child->x1 < buf->rect.x1) &&
858                 (child->y1 < buf->rect.y1) &&
859                 (child->x2 > buf->rect.x0) &&
860                 (child->y2 > buf->rect.y0)) {
861                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
862                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
863             }
864         }
865     }
868 /**
869  * Adds an item to a canvas group.
870  */
871 static void
872 group_add (SPCanvasGroup *group, SPCanvasItem *item)
874     gtk_object_ref (GTK_OBJECT (item));
875     gtk_object_sink (GTK_OBJECT (item));
877     if (!group->items) {
878         group->items = g_list_append (group->items, item);
879         group->last = group->items;
880     } else {
881         group->last = g_list_append (group->last, item)->next;
882     }
884     sp_canvas_item_request_update (item);
887 /**
888  * Removes an item from a canvas group
889  */
890 static void
891 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
893     g_return_if_fail (group != NULL);
894     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
895     g_return_if_fail (item != NULL);
897     for (GList *children = group->items; children; children = children->next) {
898         if (children->data == item) {
900             /* Unparent the child */
902             item->parent = NULL;
903             gtk_object_unref (GTK_OBJECT (item));
905             /* Remove it from the list */
907             if (children == group->last) group->last = children->prev;
909             group->items = g_list_remove_link (group->items, children);
910             g_list_free (children);
911             break;
912         }
913     }
916 /* SPCanvas */
918 static void sp_canvas_class_init (SPCanvasClass *klass);
919 static void sp_canvas_init (SPCanvas *canvas);
920 static void sp_canvas_destroy (GtkObject *object);
922 static void sp_canvas_realize (GtkWidget *widget);
923 static void sp_canvas_unrealize (GtkWidget *widget);
925 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
926 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
928 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
929 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
930 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
931 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
932 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
933 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
934 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
935 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
937 static GtkWidgetClass *canvas_parent_class;
939 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
940 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
941 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
942 static int do_update (SPCanvas *canvas);
944 /**
945  * Registers the SPCanvas class if necessary, and returns the type ID
946  * associated to it.
947  *
948  * \return The type ID of the SPCanvas class.
949  **/
950 GType sp_canvas_get_type(void)
952     static GType type = 0;
953     if (!type) {
954         GTypeInfo info = {
955             sizeof(SPCanvasClass),
956             0, // base_init
957             0, // base_finalize
958             (GClassInitFunc)sp_canvas_class_init,
959             0, // class_finalize
960             0, // class_data
961             sizeof(SPCanvas),
962             0, // n_preallocs
963             (GInstanceInitFunc)sp_canvas_init,
964             0 // value_table
965         };
966         type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
967     }
968     return type;
971 /**
972  * Class initialization function for SPCanvasClass.
973  */
974 static void
975 sp_canvas_class_init (SPCanvasClass *klass)
977     GtkObjectClass *object_class = (GtkObjectClass *) klass;
978     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
980     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
982     object_class->destroy = sp_canvas_destroy;
984     widget_class->realize = sp_canvas_realize;
985     widget_class->unrealize = sp_canvas_unrealize;
986     widget_class->size_request = sp_canvas_size_request;
987     widget_class->size_allocate = sp_canvas_size_allocate;
988     widget_class->button_press_event = sp_canvas_button;
989     widget_class->button_release_event = sp_canvas_button;
990     widget_class->motion_notify_event = sp_canvas_motion;
991     widget_class->scroll_event = sp_canvas_scroll;
992     widget_class->expose_event = sp_canvas_expose;
993     widget_class->key_press_event = sp_canvas_key;
994     widget_class->key_release_event = sp_canvas_key;
995     widget_class->enter_notify_event = sp_canvas_crossing;
996     widget_class->leave_notify_event = sp_canvas_crossing;
997     widget_class->focus_in_event = sp_canvas_focus_in;
998     widget_class->focus_out_event = sp_canvas_focus_out;
1001 /**
1002  * Callback: object initialization for SPCanvas.
1003  */
1004 static void
1005 sp_canvas_init (SPCanvas *canvas)
1007     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1008     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1009     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1011     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1012     canvas->pick_event.crossing.x = 0;
1013     canvas->pick_event.crossing.y = 0;
1015     /* Create the root item as a special case */
1016     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1017     canvas->root->canvas = canvas;
1019     gtk_object_ref (GTK_OBJECT (canvas->root));
1020     gtk_object_sink (GTK_OBJECT (canvas->root));
1022     canvas->need_repick = TRUE;
1024     // See comment at in sp-canvas.h.
1025     canvas->gen_all_enter_events = false;
1027     canvas->tiles=NULL;
1028     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1029     canvas->tileH=canvas->tileV=0;
1031     canvas->forced_redraw_count = 0;
1032     canvas->forced_redraw_limit = -1;
1034 #if ENABLE_LCMS
1035     canvas->enable_cms_display_adj = false;
1036     canvas->cms_key = new Glib::ustring("");
1037 #endif // ENABLE_LCMS
1039     canvas->is_scrolling = false;
1043 /**
1044  * Convenience function to remove the idle handler of a canvas.
1045  */
1046 static void
1047 remove_idle (SPCanvas *canvas)
1049     if (canvas->idle_id) {
1050         gtk_idle_remove (canvas->idle_id);
1051         canvas->idle_id = 0;
1052     }
1055 /*
1056  * Removes the transient state of the canvas (idle handler, grabs).
1057  */
1058 static void
1059 shutdown_transients (SPCanvas *canvas)
1061     /* We turn off the need_redraw flag, since if the canvas is mapped again
1062      * it will request a redraw anyways.  We do not turn off the need_update
1063      * flag, though, because updates are not queued when the canvas remaps
1064      * itself.
1065      */
1066     if (canvas->need_redraw) {
1067         canvas->need_redraw = FALSE;
1068     }
1069     if ( canvas->tiles ) g_free(canvas->tiles);
1070     canvas->tiles=NULL;
1071     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1072     canvas->tileH=canvas->tileV=0;
1074     if (canvas->grabbed_item) {
1075         canvas->grabbed_item = NULL;
1076         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1077     }
1079     remove_idle (canvas);
1082 /**
1083  * Destroy handler for SPCanvas.
1084  */
1085 static void
1086 sp_canvas_destroy (GtkObject *object)
1088     SPCanvas *canvas = SP_CANVAS (object);
1090     if (canvas->root) {
1091         gtk_object_unref (GTK_OBJECT (canvas->root));
1092         canvas->root = NULL;
1093     }
1095     shutdown_transients (canvas);
1097     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1098         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1101 /**
1102  * Returns new canvas as widget.
1103  */
1104 GtkWidget *
1105 sp_canvas_new_aa (void)
1107     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1109     return (GtkWidget *) canvas;
1112 /**
1113  * The canvas widget's realize callback.
1114  */
1115 static void
1116 sp_canvas_realize (GtkWidget *widget)
1118     SPCanvas *canvas = SP_CANVAS (widget);
1120     GdkWindowAttr attributes;
1121     attributes.window_type = GDK_WINDOW_CHILD;
1122     attributes.x = widget->allocation.x;
1123     attributes.y = widget->allocation.y;
1124     attributes.width = widget->allocation.width;
1125     attributes.height = widget->allocation.height;
1126     attributes.wclass = GDK_INPUT_OUTPUT;
1127     attributes.visual = gdk_rgb_get_visual ();
1128     attributes.colormap = gdk_rgb_get_cmap ();
1129     attributes.event_mask = (gtk_widget_get_events (widget) |
1130                              GDK_EXPOSURE_MASK |
1131                              GDK_BUTTON_PRESS_MASK |
1132                              GDK_BUTTON_RELEASE_MASK |
1133                              ( HAS_BROKEN_MOTION_HINTS ? GDK_POINTER_MOTION_MASK : GDK_POINTER_MOTION_HINT_MASK ) |
1134                              GDK_PROXIMITY_IN_MASK |
1135                              GDK_PROXIMITY_OUT_MASK |
1136                              GDK_KEY_PRESS_MASK |
1137                              GDK_KEY_RELEASE_MASK |
1138                              GDK_ENTER_NOTIFY_MASK |
1139                              GDK_LEAVE_NOTIFY_MASK |
1140                              GDK_FOCUS_CHANGE_MASK);
1141     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1143     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1144     gdk_window_set_user_data (widget->window, widget);
1146     if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1147         gtk_widget_set_events(widget, attributes.event_mask);
1149     widget->style = gtk_style_attach (widget->style, widget->window);
1151     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1153     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1156 /**
1157  * The canvas widget's unrealize callback.
1158  */
1159 static void
1160 sp_canvas_unrealize (GtkWidget *widget)
1162     SPCanvas *canvas = SP_CANVAS (widget);
1164     canvas->current_item = NULL;
1165     canvas->grabbed_item = NULL;
1166     canvas->focused_item = NULL;
1168     shutdown_transients (canvas);
1170     gdk_gc_destroy (canvas->pixmap_gc);
1171     canvas->pixmap_gc = NULL;
1173     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1174         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1177 /**
1178  * The canvas widget's size_request callback.
1179  */
1180 static void
1181 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1183     static_cast<void>(SP_CANVAS (widget));
1185     req->width = 256;
1186     req->height = 256;
1189 /**
1190  * The canvas widget's size_allocate callback.
1191  */
1192 static void
1193 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1195     SPCanvas *canvas = SP_CANVAS (widget);
1197     /* Schedule redraw of new region */
1198     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1199     if (allocation->width > widget->allocation.width) {
1200         sp_canvas_request_redraw (canvas,
1201                                   canvas->x0 + widget->allocation.width,
1202                                   0,
1203                                   canvas->x0 + allocation->width,
1204                                   canvas->y0 + allocation->height);
1205     }
1206     if (allocation->height > widget->allocation.height) {
1207         sp_canvas_request_redraw (canvas,
1208                                   0,
1209                                   canvas->y0 + widget->allocation.height,
1210                                   canvas->x0 + allocation->width,
1211                                   canvas->y0 + allocation->height);
1212     }
1214     widget->allocation = *allocation;
1215     
1216     if (GTK_WIDGET_REALIZED (widget)) {
1217         gdk_window_move_resize (widget->window,
1218                                 widget->allocation.x, widget->allocation.y,
1219                                 widget->allocation.width, widget->allocation.height);
1220     }
1223 /**
1224  * Helper that emits an event for an item in the canvas, be it the current
1225  * item, grabbed item, or focused item, as appropriate.
1226  */
1227 static int
1228 emit_event (SPCanvas *canvas, GdkEvent *event)
1230     guint mask;
1232     if (canvas->grabbed_item) {
1233         switch (event->type) {
1234         case GDK_ENTER_NOTIFY:
1235             mask = GDK_ENTER_NOTIFY_MASK;
1236             break;
1237         case GDK_LEAVE_NOTIFY:
1238             mask = GDK_LEAVE_NOTIFY_MASK;
1239             break;
1240         case GDK_MOTION_NOTIFY:
1241             mask = GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
1242             break;
1243         case GDK_BUTTON_PRESS:
1244         case GDK_2BUTTON_PRESS:
1245         case GDK_3BUTTON_PRESS:
1246             mask = GDK_BUTTON_PRESS_MASK;
1247             break;
1248         case GDK_BUTTON_RELEASE:
1249             mask = GDK_BUTTON_RELEASE_MASK;
1250             break;
1251         case GDK_KEY_PRESS:
1252             mask = GDK_KEY_PRESS_MASK;
1253             break;
1254         case GDK_KEY_RELEASE:
1255             mask = GDK_KEY_RELEASE_MASK;
1256             break;
1257         case GDK_SCROLL:
1258             mask = GDK_SCROLL;
1259             break;
1260         default:
1261             mask = 0;
1262             break;
1263         }
1265         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1266     }
1268     /* Convert to world coordinates -- we have two cases because of diferent
1269      * offsets of the fields in the event structures.
1270      */
1272     GdkEvent ev = *event;
1274     switch (ev.type) {
1275     case GDK_ENTER_NOTIFY:
1276     case GDK_LEAVE_NOTIFY:
1277         ev.crossing.x += canvas->x0;
1278         ev.crossing.y += canvas->y0;
1279         break;
1280     case GDK_MOTION_NOTIFY:
1281     case GDK_BUTTON_PRESS:
1282     case GDK_2BUTTON_PRESS:
1283     case GDK_3BUTTON_PRESS:
1284     case GDK_BUTTON_RELEASE:
1285         ev.motion.x += canvas->x0;
1286         ev.motion.y += canvas->y0;
1287         break;
1288     default:
1289         break;
1290     }
1292     /* Choose where we send the event */
1294     /* canvas->current_item becomes NULL in some cases under Win32
1295     ** (e.g. if the pointer leaves the window).  So this is a hack that
1296     ** Lauris applied to SP to get around the problem.
1297     */
1298     SPCanvasItem* item = NULL;
1299     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1300         item = canvas->grabbed_item;
1301     } else {
1302         item = canvas->current_item;
1303     }
1305     if (canvas->focused_item &&
1306         ((event->type == GDK_KEY_PRESS) ||
1307          (event->type == GDK_KEY_RELEASE) ||
1308          (event->type == GDK_FOCUS_CHANGE))) {
1309         item = canvas->focused_item;
1310     }
1312     /* The event is propagated up the hierarchy (for if someone connected to
1313      * a group instead of a leaf event), and emission is stopped if a
1314      * handler returns TRUE, just like for GtkWidget events.
1315      */
1317     gint finished = FALSE;
1319     while (item && !finished) {
1320         gtk_object_ref (GTK_OBJECT (item));
1321         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1322         SPCanvasItem *parent = item->parent;
1323         gtk_object_unref (GTK_OBJECT (item));
1324         item = parent;
1325     }
1327     return finished;
1330 /**
1331  * Helper that re-picks the current item in the canvas, based on the event's
1332  * coordinates and emits enter/leave events for items as appropriate.
1333  */
1334 static int
1335 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1337     int button_down = 0;
1338     double x, y;
1340     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1341         return FALSE;
1343     int retval = FALSE;
1345     if (canvas->gen_all_enter_events == false) {
1346         // If a button is down, we'll perform enter and leave events on the
1347         // current item, but not enter on any other item.  This is more or
1348         // less like X pointer grabbing for canvas items.
1349         //
1350         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1351                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1353         if (!button_down) canvas->left_grabbed_item = FALSE;
1354     }
1356     /* Save the event in the canvas.  This is used to synthesize enter and
1357      * leave events in case the current item changes.  It is also used to
1358      * re-pick the current item if the current one gets deleted.  Also,
1359      * synthesize an enter event.
1360      */
1361     if (event != &canvas->pick_event) {
1362         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1363             /* these fields have the same offsets in both types of events */
1365             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1366             canvas->pick_event.crossing.window     = event->motion.window;
1367             canvas->pick_event.crossing.send_event = event->motion.send_event;
1368             canvas->pick_event.crossing.subwindow  = NULL;
1369             canvas->pick_event.crossing.x          = event->motion.x;
1370             canvas->pick_event.crossing.y          = event->motion.y;
1371             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1372             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1373             canvas->pick_event.crossing.focus      = FALSE;
1374             canvas->pick_event.crossing.state      = event->motion.state;
1376             /* these fields don't have the same offsets in both types of events */
1378             if (event->type == GDK_MOTION_NOTIFY) {
1379                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1380                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1381             } else {
1382                 canvas->pick_event.crossing.x_root = event->button.x_root;
1383                 canvas->pick_event.crossing.y_root = event->button.y_root;
1384             }
1385         } else {
1386             canvas->pick_event = *event;
1387         }
1388     }
1390     /* Don't do anything else if this is a recursive call */
1391     if (canvas->in_repick) return retval;
1393     /* LeaveNotify means that there is no current item, so we don't look for one */
1394     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1395         /* these fields don't have the same offsets in both types of events */
1397         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1398             x = canvas->pick_event.crossing.x;
1399             y = canvas->pick_event.crossing.y;
1400         } else {
1401             x = canvas->pick_event.motion.x;
1402             y = canvas->pick_event.motion.y;
1403         }
1405         /* world coords */
1406         x += canvas->x0;
1407         y += canvas->y0;
1409         /* find the closest item */
1410         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1411             sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1412         } else {
1413             canvas->new_current_item = NULL;
1414         }
1415     } else {
1416         canvas->new_current_item = NULL;
1417     }
1419     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1420         return retval; /* current item did not change */
1421     }
1423     /* Synthesize events for old and new current items */
1425     if ((canvas->new_current_item != canvas->current_item)
1426         && (canvas->current_item != NULL)
1427         && !canvas->left_grabbed_item) {
1428         GdkEvent new_event;
1429         SPCanvasItem *item;
1431         item = canvas->current_item;
1433         new_event = canvas->pick_event;
1434         new_event.type = GDK_LEAVE_NOTIFY;
1436         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1437         new_event.crossing.subwindow = NULL;
1438         canvas->in_repick = TRUE;
1439         retval = emit_event (canvas, &new_event);
1440         canvas->in_repick = FALSE;
1441     }
1443     if (canvas->gen_all_enter_events == false) {
1444         // new_current_item may have been set to NULL during the call to
1445         // emit_event() above
1446         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1447             canvas->left_grabbed_item = TRUE;
1448             return retval;
1449         }
1450     }
1452     /* Handle the rest of cases */
1454     canvas->left_grabbed_item = FALSE;
1455     canvas->current_item = canvas->new_current_item;
1457     if (canvas->current_item != NULL) {
1458         GdkEvent new_event;
1460         new_event = canvas->pick_event;
1461         new_event.type = GDK_ENTER_NOTIFY;
1462         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1463         new_event.crossing.subwindow = NULL;
1464         retval = emit_event (canvas, &new_event);
1465     }
1467     return retval;
1470 /**
1471  * Button event handler for the canvas.
1472  */
1473 static gint
1474 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1476     SPCanvas *canvas = SP_CANVAS (widget);
1478     int retval = FALSE;
1480     /* dispatch normally regardless of the event's window if an item has
1481        has a pointer grab in effect */
1482     if (!canvas->grabbed_item &&
1483         event->window != SP_CANVAS_WINDOW (canvas))
1484         return retval;
1486     int mask;
1487     switch (event->button) {
1488     case 1:
1489         mask = GDK_BUTTON1_MASK;
1490         break;
1491     case 2:
1492         mask = GDK_BUTTON2_MASK;
1493         break;
1494     case 3:
1495         mask = GDK_BUTTON3_MASK;
1496         break;
1497     case 4:
1498         mask = GDK_BUTTON4_MASK;
1499         break;
1500     case 5:
1501         mask = GDK_BUTTON5_MASK;
1502         break;
1503     default:
1504         mask = 0;
1505     }
1507     switch (event->type) {
1508     case GDK_BUTTON_PRESS:
1509     case GDK_2BUTTON_PRESS:
1510     case GDK_3BUTTON_PRESS:
1511         /* Pick the current item as if the button were not pressed, and
1512          * then process the event.
1513          */
1514         canvas->state = event->state;
1515         pick_current_item (canvas, (GdkEvent *) event);
1516         canvas->state ^= mask;
1517         retval = emit_event (canvas, (GdkEvent *) event);
1518         break;
1520     case GDK_BUTTON_RELEASE:
1521         /* Process the event as if the button were pressed, then repick
1522          * after the button has been released
1523          */
1524         canvas->state = event->state;
1525         retval = emit_event (canvas, (GdkEvent *) event);
1526         event->state ^= mask;
1527         canvas->state = event->state;
1528         pick_current_item (canvas, (GdkEvent *) event);
1529         event->state ^= mask;
1530         break;
1532     default:
1533         g_assert_not_reached ();
1534     }
1536     return retval;
1539 /**
1540  * Scroll event handler for the canvas.
1541  *
1542  * \todo FIXME: generate motion events to re-select items.
1543  */
1544 static gint
1545 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1547     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1550 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1551 static inline void request_motions(GdkWindow *, GdkEventMotion *event) {
1552     gdk_event_request_motions(event);
1554 #else
1555 static inline void request_motions(GdkWindow *w, GdkEventMotion *) {
1556     gdk_window_get_pointer(w, NULL, NULL, NULL);
1558 #endif
1560 /**
1561  * Motion event handler for the canvas.
1562  */
1563 static int
1564 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1566     int status;
1567     SPCanvas *canvas = SP_CANVAS (widget);
1569     if (event->window != SP_CANVAS_WINDOW (canvas))
1570         return FALSE;
1572     if (canvas->pixmap_gc == NULL) // canvas being deleted
1573         return FALSE;
1575     canvas->state = event->state;
1576     pick_current_item (canvas, (GdkEvent *) event);
1578     status = emit_event (canvas, (GdkEvent *) event);
1580     if (event->is_hint) {
1581         request_motions(widget->window, event);
1582     }
1584     return status;
1587 static void
1588 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)
1590     GtkWidget *widget = GTK_WIDGET (canvas);
1592     SPCanvasBuf buf;
1593     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1594         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1595     } else {
1596         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1597     }
1599     // Mark the region clean
1600     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1602     buf.buf_rowstride = sw * 4; 
1603     buf.rect.x0 = x0;
1604     buf.rect.y0 = y0;
1605     buf.rect.x1 = x1;
1606     buf.rect.y1 = y1;
1607     buf.visible_rect.x0 = draw_x1;
1608     buf.visible_rect.y0 = draw_y1;
1609     buf.visible_rect.x1 = draw_x2;
1610     buf.visible_rect.y1 = draw_y2;
1611     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1612     buf.bg_color = (((color->red & 0xff00) << 8)
1613                     | (color->green & 0xff00)
1614                     | (color->blue >> 8));
1615     buf.is_empty = true;
1617     buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1619     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1620         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1621     }
1623 #if ENABLE_LCMS
1624     cmsHTRANSFORM transf = 0;
1625     long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1626     if ( fromDisplay ) {
1627         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1628     } else {
1629         transf = Inkscape::colorprofile_get_display_transform();
1630     }
1631 #endif // ENABLE_LCMS
1633     if (buf.is_empty) {
1634 #if ENABLE_LCMS
1635         if ( transf && canvas->enable_cms_display_adj ) {
1636             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1637         }
1638 #endif // ENABLE_LCMS
1639         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1640         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1641                             canvas->pixmap_gc,
1642                             TRUE,
1643                             x0 - canvas->x0, y0 - canvas->y0,
1644                             x1 - x0, y1 - y0);
1645     } else {
1647 #if ENABLE_LCMS
1648         if ( transf && canvas->enable_cms_display_adj ) {
1649             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1650                 guchar* p = buf.buf + (sw * 3) * yy;
1651                 cmsDoTransform( transf, p, p, (x1 - x0) );
1652             }
1653         }
1654 #endif // ENABLE_LCMS
1656 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1657 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1658 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1659 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1660 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1661 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1663 ///#define CANVAS_OUTPUT_VIA_CAIRO
1665 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1667         buf.cst = cairo_image_surface_create_for_data (
1668             buf.buf,
1669             CAIRO_FORMAT_ARGB32,  // unpacked, i.e. 32 bits! one byte is unused
1670             x1 - x0, y1 - y0,
1671             buf.buf_rowstride
1672             );
1673         cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1674         cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1675         cairo_paint (window_ct);
1676         cairo_destroy (window_ct);
1677         cairo_surface_finish (buf.cst);
1678         cairo_surface_destroy (buf.cst);
1680 #else
1682         NRPixBlock b3;
1683         nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1685         NRPixBlock b4;
1686         nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1687                                   buf.buf,
1688                                   buf.buf_rowstride,
1689                                   FALSE, FALSE);
1691         // this does the 32->24 squishing, using an assembler routine:
1692         nr_blit_pixblock_pixblock (&b3, &b4);
1694         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1695                                       canvas->pixmap_gc,
1696                                       x0 - canvas->x0, y0 - canvas->y0,
1697                                       x1 - x0, y1 - y0,
1698                                       GDK_RGB_DITHER_MAX,
1699                                       b3.data.px,
1700                                       sw * 3,
1701                                       x0 - canvas->x0, y0 - canvas->y0);
1703         nr_pixblock_release (&b3);
1704         nr_pixblock_release (&b4);
1705 #endif
1706     }
1708     cairo_surface_t *cst = cairo_get_target(buf.ct);
1709     cairo_destroy (buf.ct);
1710     cairo_surface_finish (cst);
1711     cairo_surface_destroy (cst);
1713     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1714         nr_pixelstore_256K_free (buf.buf);
1715     } else {
1716         nr_pixelstore_1M_free (buf.buf);
1717     }
1720 struct PaintRectSetup {
1721     SPCanvas* canvas;
1722     NRRectL big_rect;
1723     GTimeVal start_time;
1724     int max_pixels;
1725     NR::Point mouse_loc;
1726 };
1728 /**
1729  * Paint the given rect, recursively subdividing the region until it is the size of a single
1730  * buffer.
1731  *
1732  * @return true if the drawing completes
1733  */
1734 static int
1735 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1737     GTimeVal now;
1738     g_get_current_time (&now);
1740     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1741         + (now.tv_usec - setup->start_time.tv_usec);
1743     // Allow only very fast buffers to be run together;
1744     // as soon as the total redraw time exceeds 1ms, cancel;
1745     // this returns control to the idle loop and allows Inkscape to process user input
1746     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1747     // it will get back and finish painting what remains to paint.
1748     if (elapsed > 1000) {
1750         // Interrupting redraw isn't always good.
1751         // For example, when you drag one node of a big path, only the buffer containing
1752         // the mouse cursor will be redrawn again and again, and the rest of the path
1753         // will remain stale because Inkscape never has enough idle time to redraw all
1754         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1755         // If this limit is set, and if we have aborted redraw more times than is allowed,
1756         // interrupting is blocked and we're forced to redraw full screen once
1757         // (after which we can again interrupt forced_redraw_limit times).
1758         if (setup->canvas->forced_redraw_limit < 0 ||
1759             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1761             if (setup->canvas->forced_redraw_limit != -1) {
1762                 setup->canvas->forced_redraw_count++;
1763             }
1765             return false;
1766         }
1767     }
1769     // Find the optimal buffer dimensions
1770     int bw = this_rect.x1 - this_rect.x0;
1771     int bh = this_rect.y1 - this_rect.y0;
1772     if ((bw < 1) || (bh < 1))
1773         return 0;
1775     if (bw * bh < setup->max_pixels) {
1776         // We are small enough
1777         sp_canvas_paint_single_buffer (setup->canvas,
1778                                        this_rect.x0, this_rect.y0,
1779                                        this_rect.x1, this_rect.y1,
1780                                        setup->big_rect.x0, setup->big_rect.y0,
1781                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1782         return 1;
1783     }
1785     NRRectL lo = this_rect;
1786     NRRectL hi = this_rect;
1788 /*
1789 This test determines the redraw strategy:
1791 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1792 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1793 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1794 and seems to be faster for drawings with many smaller objects at zoom-out.
1796 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1797 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1798 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1799 faster.
1801 The default for now is the strips mode.
1802 */
1803     if (bw < bh || bh < 2 * TILE_SIZE) {
1804         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1805         int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1806         // Make sure that mid lies on a tile boundary
1807         mid = (mid / TILE_SIZE) * TILE_SIZE;
1809         lo.x1 = mid;
1810         hi.x0 = mid;
1812         if (setup->mouse_loc[NR::X] < mid) {
1813             // Always paint towards the mouse first
1814             return sp_canvas_paint_rect_internal(setup, lo)
1815                 && sp_canvas_paint_rect_internal(setup, hi);
1816         } else {
1817             return sp_canvas_paint_rect_internal(setup, hi)
1818                 && sp_canvas_paint_rect_internal(setup, lo);
1819         }
1820     } else {
1821         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1822         int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1823         // Make sure that mid lies on a tile boundary
1824         mid = (mid / TILE_SIZE) * TILE_SIZE;
1826         lo.y1 = mid;
1827         hi.y0 = mid;
1829         if (setup->mouse_loc[NR::Y] < mid) {
1830             // Always paint towards the mouse first
1831             return sp_canvas_paint_rect_internal(setup, lo)
1832                 && sp_canvas_paint_rect_internal(setup, hi);
1833         } else {
1834             return sp_canvas_paint_rect_internal(setup, hi)
1835                 && sp_canvas_paint_rect_internal(setup, lo);
1836         }
1837     }
1841 /**
1842  * Helper that draws a specific rectangular part of the canvas.
1843  *
1844  * @return true if the rectangle painting succeeds.
1845  */
1846 static bool
1847 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1849     g_return_val_if_fail (!canvas->need_update, false);
1851     NRRectL rect;
1852     rect.x0 = xx0;
1853     rect.x1 = xx1;
1854     rect.y0 = yy0;
1855     rect.y1 = yy1;
1857     // Clip rect-to-draw by the current visible area
1858     rect.x0 = MAX (rect.x0, canvas->x0);
1859     rect.y0 = MAX (rect.y0, canvas->y0);
1860     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1861     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1863 #ifdef DEBUG_REDRAW
1864     // paint the area to redraw yellow
1865     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1866     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1867                         canvas->pixmap_gc,
1868                         TRUE,
1869                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1870                         rect.x1 - rect.x0, rect.y1 - rect.y0);
1871 #endif
1873     PaintRectSetup setup;
1875     setup.canvas = canvas;
1876     setup.big_rect = rect;
1878     // Save the mouse location
1879     gint x, y;
1880     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1881     setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1883     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1884         // use 256K as a compromise to not slow down gradients
1885         // 256K is the cached buffer and we need 4 channels
1886         setup.max_pixels = 65536; // 256K/4
1887     } else {
1888         // paths only, so 1M works faster
1889         // 1M is the cached buffer and we need 4 channels
1890         setup.max_pixels = 262144; 
1891     }
1893     // Start the clock
1894     g_get_current_time(&(setup.start_time));
1896     // Go
1897     return sp_canvas_paint_rect_internal(&setup, rect);
1900 /**
1901  * Force a full redraw after a specified number of interrupted redraws
1902  */
1903 void
1904 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1905   g_return_if_fail(canvas != NULL);
1907   canvas->forced_redraw_limit = count;
1908   canvas->forced_redraw_count = 0;
1911 /**
1912  * End forced full redraw requests
1913  */
1914 void
1915 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1916   g_return_if_fail(canvas != NULL);
1918   canvas->forced_redraw_limit = -1;
1921 /**
1922  * The canvas widget's expose callback.
1923  */
1924 static gint
1925 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1927     SPCanvas *canvas = SP_CANVAS (widget);
1929     if (!GTK_WIDGET_DRAWABLE (widget) ||
1930         (event->window != SP_CANVAS_WINDOW (canvas)))
1931         return FALSE;
1933     int n_rects;
1934     GdkRectangle *rects;
1935     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1937     for (int i = 0; i < n_rects; i++) {
1938         NRRectL rect;
1940         rect.x0 = rects[i].x + canvas->x0;
1941         rect.y0 = rects[i].y + canvas->y0;
1942         rect.x1 = rect.x0 + rects[i].width;
1943         rect.y1 = rect.y0 + rects[i].height;
1945         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1946     }
1948     if (n_rects > 0)
1949         g_free (rects);
1951     return FALSE;
1954 /**
1955  * The canvas widget's keypress callback.
1956  */
1957 static gint
1958 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1960     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1963 /**
1964  * Crossing event handler for the canvas.
1965  */
1966 static gint
1967 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1969     SPCanvas *canvas = SP_CANVAS (widget);
1971     if (event->window != SP_CANVAS_WINDOW (canvas))
1972         return FALSE;
1974     canvas->state = event->state;
1975     return pick_current_item (canvas, (GdkEvent *) event);
1978 /**
1979  * Focus in handler for the canvas.
1980  */
1981 static gint
1982 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1984     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1986     SPCanvas *canvas = SP_CANVAS (widget);
1988     if (canvas->focused_item) {
1989         return emit_event (canvas, (GdkEvent *) event);
1990     } else {
1991         return FALSE;
1992     }
1995 /**
1996  * Focus out handler for the canvas.
1997  */
1998 static gint
1999 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2001     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2003     SPCanvas *canvas = SP_CANVAS (widget);
2005     if (canvas->focused_item)
2006         return emit_event (canvas, (GdkEvent *) event);
2007     else
2008         return FALSE;
2011 /**
2012  * Helper that repaints the areas in the canvas that need it.
2013  *
2014  * @return true if all the dirty parts have been redrawn
2015  */
2016 static int
2017 paint (SPCanvas *canvas)
2019     if (canvas->need_update) {
2020         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2021         canvas->need_update = FALSE;
2022     }
2024     if (!canvas->need_redraw)
2025         return TRUE;
2027     Gdk::Region to_paint;
2029     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2030         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2031             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2033             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2034                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2035                                    TILE_SIZE, TILE_SIZE));
2036             }
2038         }
2039     }
2041     if (!to_paint.empty()) {
2042         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2043         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2044         for (Iter i=rect.begin(); i != rect.end(); ++i) {
2045             int x0 = (*i).get_x();
2046             int y0 = (*i).get_y();
2047             int x1 = x0 + (*i).get_width();
2048             int y1 = y0 + (*i).get_height();
2049             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2050                 // Aborted
2051                 return FALSE;
2052             };
2053         }
2054     }
2056     canvas->need_redraw = FALSE;
2058     // we've had a full unaborted redraw, reset the full redraw counter
2059     if (canvas->forced_redraw_limit != -1) {
2060         canvas->forced_redraw_count = 0;
2061     }
2063     return TRUE;
2066 /**
2067  * Helper that invokes update, paint, and repick on canvas.
2068  */
2069 static int
2070 do_update (SPCanvas *canvas)
2072     if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2073         return TRUE;
2075     /* Cause the update if necessary */
2076     if (canvas->need_update) {
2077         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2078         canvas->need_update = FALSE;
2079     }
2081     /* Paint if able to */
2082     if (GTK_WIDGET_DRAWABLE (canvas)) {
2083             return paint (canvas);
2084     }
2086     /* Pick new current item */
2087     while (canvas->need_repick) {
2088         canvas->need_repick = FALSE;
2089         pick_current_item (canvas, &canvas->pick_event);
2090     }
2092     return TRUE;
2095 /**
2096  * Idle handler for the canvas that deals with pending updates and redraws.
2097  */
2098 static gint
2099 idle_handler (gpointer data)
2101     GDK_THREADS_ENTER ();
2103     SPCanvas *canvas = SP_CANVAS (data);
2105     int const ret = do_update (canvas);
2107     if (ret) {
2108         /* Reset idle id */
2109         canvas->idle_id = 0;
2110     }
2112     GDK_THREADS_LEAVE ();
2114     return !ret;
2117 /**
2118  * Convenience function to add an idle handler to a canvas.
2119  */
2120 static void
2121 add_idle (SPCanvas *canvas)
2123     if (canvas->idle_id != 0)
2124         return;
2126     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2129 /**
2130  * Returns the root group of the specified canvas.
2131  */
2132 SPCanvasGroup *
2133 sp_canvas_root (SPCanvas *canvas)
2135     g_return_val_if_fail (canvas != NULL, NULL);
2136     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2138     return SP_CANVAS_GROUP (canvas->root);
2141 /**
2142  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2143  */
2144 void
2145 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2147     g_return_if_fail (canvas != NULL);
2148     g_return_if_fail (SP_IS_CANVAS (canvas));
2150     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2151     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2152     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the 
2153     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2155     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2156     canvas->dy0 = cy;
2157     canvas->x0 = ix;
2158     canvas->y0 = iy;
2160     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2162     if (!clear) {
2163         // scrolling without zoom; redraw only the newly exposed areas
2164         if ((dx != 0) || (dy != 0)) {
2165             canvas->is_scrolling = is_scrolling;
2166             if (GTK_WIDGET_REALIZED (canvas)) {
2167                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2168             }
2169         }
2170     } else {
2171         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2172     }
2175 /**
2176  * Updates canvas if necessary.
2177  */
2178 void
2179 sp_canvas_update_now (SPCanvas *canvas)
2181     g_return_if_fail (canvas != NULL);
2182     g_return_if_fail (SP_IS_CANVAS (canvas));
2184     if (!(canvas->need_update ||
2185           canvas->need_redraw))
2186         return;
2188     do_update (canvas);
2191 /**
2192  * Update callback for canvas widget.
2193  */
2194 static void
2195 sp_canvas_request_update (SPCanvas *canvas)
2197     canvas->need_update = TRUE;
2198     add_idle (canvas);
2201 /**
2202  * Forces redraw of rectangular canvas area.
2203  */
2204 void
2205 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2207     NRRectL bbox;
2208     NRRectL visible;
2209     NRRectL clip;
2211     g_return_if_fail (canvas != NULL);
2212     g_return_if_fail (SP_IS_CANVAS (canvas));
2214     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2215     if ((x0 >= x1) || (y0 >= y1)) return;
2217     bbox.x0 = x0;
2218     bbox.y0 = y0;
2219     bbox.x1 = x1;
2220     bbox.y1 = y1;
2222     visible.x0 = canvas->x0;
2223     visible.y0 = canvas->y0;
2224     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2225     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2227     nr_rect_l_intersect (&clip, &bbox, &visible);
2229     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2230     add_idle (canvas);
2233 /**
2234  * Sets world coordinates from win and canvas.
2235  */
2236 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2238     g_return_if_fail (canvas != NULL);
2239     g_return_if_fail (SP_IS_CANVAS (canvas));
2241     if (worldx) *worldx = canvas->x0 + winx;
2242     if (worldy) *worldy = canvas->y0 + winy;
2245 /**
2246  * Sets win coordinates from world and canvas.
2247  */
2248 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2250     g_return_if_fail (canvas != NULL);
2251     g_return_if_fail (SP_IS_CANVAS (canvas));
2253     if (winx) *winx = worldx - canvas->x0;
2254     if (winy) *winy = worldy - canvas->y0;
2257 /**
2258  * Converts point from win to world coordinates.
2259  */
2260 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2262     g_assert (canvas != NULL);
2263     g_assert (SP_IS_CANVAS (canvas));
2265     return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2268 /**
2269  * Converts point from world to win coordinates.
2270  */
2271 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2273     g_assert (canvas != NULL);
2274     g_assert (SP_IS_CANVAS (canvas));
2276     return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2279 /**
2280  * Returns true if point given in world coordinates is inside window.
2281  */
2282 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2284     g_assert( canvas != NULL );
2285     g_assert(SP_IS_CANVAS(canvas));
2287     using NR::X;
2288     using NR::Y;
2289     GtkWidget const &w = *GTK_WIDGET(canvas);
2290     return ( ( canvas->x0 <= world[X] )  &&
2291              ( canvas->y0 <= world[Y] )  &&
2292              ( world[X] < canvas->x0 + w.allocation.width )  &&
2293              ( world[Y] < canvas->y0 + w.allocation.height ) );
2296 /**
2297  * Return canvas window coordinates as NR::Rect.
2298  */
2299 NR::Rect SPCanvas::getViewbox() const
2301     GtkWidget const *w = GTK_WIDGET(this);
2302     return NR::Rect(NR::Point(dx0, dy0),
2303                     NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2306 /**
2307  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2308  */
2309 NR::IRect SPCanvas::getViewboxIntegers() const
2311     GtkWidget const *w = GTK_WIDGET(this);
2312     return NR::IRect(NR::IPoint(x0, y0),
2313                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2316 inline int sp_canvas_tile_floor(int x)
2318     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2321 inline int sp_canvas_tile_ceil(int x)
2323     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2326 /**
2327  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2328  */
2329 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2331     if ( nl >= nr || nt >= nb ) {
2332         if ( canvas->tiles ) g_free(canvas->tiles);
2333         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2334         canvas->tileH=canvas->tileV=0;
2335         canvas->tiles=NULL;
2336         return;
2337     }
2338     int tl=sp_canvas_tile_floor(nl);
2339     int tt=sp_canvas_tile_floor(nt);
2340     int tr=sp_canvas_tile_ceil(nr);
2341     int tb=sp_canvas_tile_ceil(nb);
2343     int nh = tr-tl, nv = tb-tt;
2344     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2345     for (int i=tl; i<tr; i++) {
2346         for (int j=tt; j<tb; j++) {
2347             int ind = (i-tl) + (j-tt)*nh;
2348             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2349                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2350             } else {
2351                 ntiles[ind]=0; // newly exposed areas get 0
2352             }
2353         }
2354     }
2355     if ( canvas->tiles ) g_free(canvas->tiles);
2356     canvas->tiles=ntiles;
2357     canvas->tLeft=tl;
2358     canvas->tTop=tt;
2359     canvas->tRight=tr;
2360     canvas->tBottom=tb;
2361     canvas->tileH=nh;
2362     canvas->tileV=nv;
2365 /*
2366  * Helper that queues a canvas rectangle for redraw
2367  */
2368 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2369     canvas->need_redraw = TRUE;
2371     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2374 /**
2375  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2376  */
2377 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2379     if ( nl >= nr || nt >= nb ) {
2380         return;
2381     }
2382     int tl=sp_canvas_tile_floor(nl);
2383     int tt=sp_canvas_tile_floor(nt);
2384     int tr=sp_canvas_tile_ceil(nr);
2385     int tb=sp_canvas_tile_ceil(nb);
2386     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2387     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2388     if ( tr > canvas->tRight ) tr=canvas->tRight;
2389     if ( tt < canvas->tTop ) tt=canvas->tTop;
2390     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2392     for (int i=tl; i<tr; i++) {
2393         for (int j=tt; j<tb; j++) {
2394             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2395         }
2396     }
2400 /*
2401   Local Variables:
2402   mode:c++
2403   c-file-style:"stroustrup"
2404   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2405   indent-tabs-mode:nil
2406   fill-column:99
2407   End:
2408 */
2409 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :