Code

disable motion hints again; still some subtle issues to work out
[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 GTK_CHECK_VERSION(2, 12, 0)
50 // gtk_check_version returns non-NULL on failure
51 static bool const HAS_BROKEN_MOTION_HINTS =
52   true || 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) {
543         event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
544     }
546     /* fixme: Top hack (Lauris) */
547     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
548     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
549     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
550                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
551                       NULL, cursor, etime);
553     item->canvas->grabbed_item = item;
554     item->canvas->grabbed_event_mask = event_mask;
555     item->canvas->current_item = item; /* So that events go to the grabbed item */
557     return 0;
560 /**
561  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
562  * mouse.
563  *
564  * \param item A canvas item that holds a grab.
565  * \param etime The timestamp for ungrabbing the mouse.
566  */
567 void
568 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
570     g_return_if_fail (item != NULL);
571     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
573     if (item->canvas->grabbed_item != item)
574         return;
576     item->canvas->grabbed_item = NULL;
578     gdk_pointer_ungrab (etime);
581 /**
582  * Returns the product of all transformation matrices from the root item down
583  * to the item.
584  */
585 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
587     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
589     NR::Matrix affine = NR::identity();
591     while (item) {
592         affine *= item->xform;
593         item = item->parent;
594     }
595     return affine;
598 /**
599  * Helper that returns true iff item is descendant of parent.
600  */
601 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
603     while (item) {
604         if (item == parent)
605             return true;
606         item = item->parent;
607     }
609     return false;
612 /**
613  * Focus canvas, and item under cursor if it is not already focussed.
614  */
615 void
616 sp_canvas_item_grab_focus (SPCanvasItem *item)
618     g_return_if_fail (item != NULL);
619     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
620     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
622     SPCanvasItem *focused_item = item->canvas->focused_item;
624     if (focused_item) {
625         GdkEvent ev;
626         ev.focus_change.type = GDK_FOCUS_CHANGE;
627         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
628         ev.focus_change.send_event = FALSE;
629         ev.focus_change.in = FALSE;
631         emit_event (item->canvas, &ev);
632     }
634     item->canvas->focused_item = item;
635     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
637     if (focused_item) {
638         GdkEvent ev;
639         ev.focus_change.type = GDK_FOCUS_CHANGE;
640         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
641         ev.focus_change.send_event = FALSE;
642         ev.focus_change.in = TRUE;
644         emit_event (item->canvas, &ev);
645     }
648 /**
649  * Requests that the canvas queue an update for the specified item.
650  *
651  * To be used only by item implementations.
652  */
653 void
654 sp_canvas_item_request_update (SPCanvasItem *item)
656     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
657         return;
659     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
661     if (item->parent != NULL) {
662         /* Recurse up the tree */
663         sp_canvas_item_request_update (item->parent);
664     } else {
665         /* Have reached the top of the tree, make sure the update call gets scheduled. */
666         sp_canvas_request_update (item->canvas);
667     }
670 /**
671  * Returns position of item in group.
672  */
673 gint sp_canvas_item_order (SPCanvasItem * item)
675     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
678 /* SPCanvasGroup */
680 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
681 static void sp_canvas_group_init (SPCanvasGroup *group);
682 static void sp_canvas_group_destroy (GtkObject *object);
684 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
685 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
686 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
688 static SPCanvasItemClass *group_parent_class;
690 /**
691  * Registers SPCanvasGroup class with Gtk and returns its type number.
692  */
693 GType sp_canvas_group_get_type(void)
695     static GType type = 0;
696     if (!type) {
697         GTypeInfo info = {
698             sizeof(SPCanvasGroupClass),
699             0, // base_init
700             0, // base_finalize
701             (GClassInitFunc)sp_canvas_group_class_init,
702             0, // class_finalize
703             0, // class_data
704             sizeof(SPCanvasGroup),
705             0, // n_preallocs
706             (GInstanceInitFunc)sp_canvas_group_init,
707             0 // value_table
708         };
709         type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
710     }
711     return type;
714 /**
715  * Class initialization function for SPCanvasGroupClass
716  */
717 static void
718 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
720     GtkObjectClass *object_class = (GtkObjectClass *) klass;
721     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
723     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
725     object_class->destroy = sp_canvas_group_destroy;
727     item_class->update = sp_canvas_group_update;
728     item_class->render = sp_canvas_group_render;
729     item_class->point = sp_canvas_group_point;
732 /**
733  * Callback. Empty.
734  */
735 static void
736 sp_canvas_group_init (SPCanvasGroup */*group*/)
738     /* Nothing here */
741 /**
742  * Callback that destroys all items in group and calls group's virtual
743  * destroy() function.
744  */
745 static void
746 sp_canvas_group_destroy (GtkObject *object)
748     g_return_if_fail (object != NULL);
749     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
751     SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
753     GList *list = group->items;
754     while (list) {
755         SPCanvasItem *child = (SPCanvasItem *)list->data;
756         list = list->next;
758         gtk_object_destroy (GTK_OBJECT (child));
759     }
761     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
762         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
765 /**
766  * Update handler for canvas groups
767  */
768 static void
769 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
771     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
772     NR::ConvexHull corners(NR::Point(0, 0));
773     bool empty=true;
775     for (GList *list = group->items; list; list = list->next) {
776         SPCanvasItem *i = (SPCanvasItem *)list->data;
778         sp_canvas_item_invoke_update (i, affine, flags);
780         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
781             if (empty) {
782                 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
783                 empty = false;
784             } else {
785                 corners.add(NR::Point(i->x1, i->y1));
786             }
787             corners.add(NR::Point(i->x2, i->y2));
788         }
789     }
791     NR::Maybe<NR::Rect> const bounds = corners.bounds();
792     if (bounds) {
793         item->x1 = bounds->min()[NR::X];
794         item->y1 = bounds->min()[NR::Y];
795         item->x2 = bounds->max()[NR::X];
796         item->y2 = bounds->max()[NR::Y];
797     } else {
798         // FIXME ?
799         item->x1 = item->x2 = item->y1 = item->y2 = 0;
800     }
803 /**
804  * Point handler for canvas groups.
805  */
806 static double
807 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
809     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
810     double const x = p[NR::X];
811     double const y = p[NR::Y];
812     int x1 = (int)(x - item->canvas->close_enough);
813     int y1 = (int)(y - item->canvas->close_enough);
814     int x2 = (int)(x + item->canvas->close_enough);
815     int y2 = (int)(y + item->canvas->close_enough);
817     double best = 0.0;
818     *actual_item = NULL;
820     double dist = 0.0;
822     for (GList *list = group->items; list; list = list->next) {
823         SPCanvasItem *child = (SPCanvasItem *)list->data;
825         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
826             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
828             int has_point;
829             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
830                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
831                 has_point = TRUE;
832             } else
833                 has_point = FALSE;
835             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
836                 best = dist;
837                 *actual_item = point_item;
838             }
839         }
840     }
842     return best;
845 /**
846  * Renders all visible canvas group items in buf rectangle.
847  */
848 static void
849 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
851     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
853     for (GList *list = group->items; list; list = list->next) {
854         SPCanvasItem *child = (SPCanvasItem *)list->data;
855         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
856             if ((child->x1 < buf->rect.x1) &&
857                 (child->y1 < buf->rect.y1) &&
858                 (child->x2 > buf->rect.x0) &&
859                 (child->y2 > buf->rect.y0)) {
860                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
861                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
862             }
863         }
864     }
867 /**
868  * Adds an item to a canvas group.
869  */
870 static void
871 group_add (SPCanvasGroup *group, SPCanvasItem *item)
873     gtk_object_ref (GTK_OBJECT (item));
874     gtk_object_sink (GTK_OBJECT (item));
876     if (!group->items) {
877         group->items = g_list_append (group->items, item);
878         group->last = group->items;
879     } else {
880         group->last = g_list_append (group->last, item)->next;
881     }
883     sp_canvas_item_request_update (item);
886 /**
887  * Removes an item from a canvas group
888  */
889 static void
890 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
892     g_return_if_fail (group != NULL);
893     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
894     g_return_if_fail (item != NULL);
896     for (GList *children = group->items; children; children = children->next) {
897         if (children->data == item) {
899             /* Unparent the child */
901             item->parent = NULL;
902             gtk_object_unref (GTK_OBJECT (item));
904             /* Remove it from the list */
906             if (children == group->last) group->last = children->prev;
908             group->items = g_list_remove_link (group->items, children);
909             g_list_free (children);
910             break;
911         }
912     }
915 /* SPCanvas */
917 static void sp_canvas_class_init (SPCanvasClass *klass);
918 static void sp_canvas_init (SPCanvas *canvas);
919 static void sp_canvas_destroy (GtkObject *object);
921 static void sp_canvas_realize (GtkWidget *widget);
922 static void sp_canvas_unrealize (GtkWidget *widget);
924 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
925 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
927 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
928 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
929 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
930 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
931 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
932 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
933 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
934 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
936 static GtkWidgetClass *canvas_parent_class;
938 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
939 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
940 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
941 static int do_update (SPCanvas *canvas);
943 /**
944  * Registers the SPCanvas class if necessary, and returns the type ID
945  * associated to it.
946  *
947  * \return The type ID of the SPCanvas class.
948  **/
949 GType sp_canvas_get_type(void)
951     static GType type = 0;
952     if (!type) {
953         GTypeInfo info = {
954             sizeof(SPCanvasClass),
955             0, // base_init
956             0, // base_finalize
957             (GClassInitFunc)sp_canvas_class_init,
958             0, // class_finalize
959             0, // class_data
960             sizeof(SPCanvas),
961             0, // n_preallocs
962             (GInstanceInitFunc)sp_canvas_init,
963             0 // value_table
964         };
965         type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
966     }
967     return type;
970 /**
971  * Class initialization function for SPCanvasClass.
972  */
973 static void
974 sp_canvas_class_init (SPCanvasClass *klass)
976     GtkObjectClass *object_class = (GtkObjectClass *) klass;
977     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
979     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
981     object_class->destroy = sp_canvas_destroy;
983     widget_class->realize = sp_canvas_realize;
984     widget_class->unrealize = sp_canvas_unrealize;
985     widget_class->size_request = sp_canvas_size_request;
986     widget_class->size_allocate = sp_canvas_size_allocate;
987     widget_class->button_press_event = sp_canvas_button;
988     widget_class->button_release_event = sp_canvas_button;
989     widget_class->motion_notify_event = sp_canvas_motion;
990     widget_class->scroll_event = sp_canvas_scroll;
991     widget_class->expose_event = sp_canvas_expose;
992     widget_class->key_press_event = sp_canvas_key;
993     widget_class->key_release_event = sp_canvas_key;
994     widget_class->enter_notify_event = sp_canvas_crossing;
995     widget_class->leave_notify_event = sp_canvas_crossing;
996     widget_class->focus_in_event = sp_canvas_focus_in;
997     widget_class->focus_out_event = sp_canvas_focus_out;
1000 /**
1001  * Callback: object initialization for SPCanvas.
1002  */
1003 static void
1004 sp_canvas_init (SPCanvas *canvas)
1006     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1007     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1008     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1010     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1011     canvas->pick_event.crossing.x = 0;
1012     canvas->pick_event.crossing.y = 0;
1014     /* Create the root item as a special case */
1015     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1016     canvas->root->canvas = canvas;
1018     gtk_object_ref (GTK_OBJECT (canvas->root));
1019     gtk_object_sink (GTK_OBJECT (canvas->root));
1021     canvas->need_repick = TRUE;
1023     // See comment at in sp-canvas.h.
1024     canvas->gen_all_enter_events = false;
1026     canvas->tiles=NULL;
1027     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1028     canvas->tileH=canvas->tileV=0;
1030     canvas->forced_redraw_count = 0;
1031     canvas->forced_redraw_limit = -1;
1033 #if ENABLE_LCMS
1034     canvas->enable_cms_display_adj = false;
1035     canvas->cms_key = new Glib::ustring("");
1036 #endif // ENABLE_LCMS
1038     canvas->is_scrolling = false;
1042 /**
1043  * Convenience function to remove the idle handler of a canvas.
1044  */
1045 static void
1046 remove_idle (SPCanvas *canvas)
1048     if (canvas->idle_id) {
1049         gtk_idle_remove (canvas->idle_id);
1050         canvas->idle_id = 0;
1051     }
1054 /*
1055  * Removes the transient state of the canvas (idle handler, grabs).
1056  */
1057 static void
1058 shutdown_transients (SPCanvas *canvas)
1060     /* We turn off the need_redraw flag, since if the canvas is mapped again
1061      * it will request a redraw anyways.  We do not turn off the need_update
1062      * flag, though, because updates are not queued when the canvas remaps
1063      * itself.
1064      */
1065     if (canvas->need_redraw) {
1066         canvas->need_redraw = FALSE;
1067     }
1068     if ( canvas->tiles ) g_free(canvas->tiles);
1069     canvas->tiles=NULL;
1070     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1071     canvas->tileH=canvas->tileV=0;
1073     if (canvas->grabbed_item) {
1074         canvas->grabbed_item = NULL;
1075         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1076     }
1078     remove_idle (canvas);
1081 /**
1082  * Destroy handler for SPCanvas.
1083  */
1084 static void
1085 sp_canvas_destroy (GtkObject *object)
1087     SPCanvas *canvas = SP_CANVAS (object);
1089     if (canvas->root) {
1090         gtk_object_unref (GTK_OBJECT (canvas->root));
1091         canvas->root = NULL;
1092     }
1094     shutdown_transients (canvas);
1096     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1097         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1100 /**
1101  * Returns new canvas as widget.
1102  */
1103 GtkWidget *
1104 sp_canvas_new_aa (void)
1106     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1108     return (GtkWidget *) canvas;
1111 /**
1112  * The canvas widget's realize callback.
1113  */
1114 static void
1115 sp_canvas_realize (GtkWidget *widget)
1117     SPCanvas *canvas = SP_CANVAS (widget);
1119     GdkWindowAttr attributes;
1120     attributes.window_type = GDK_WINDOW_CHILD;
1121     attributes.x = widget->allocation.x;
1122     attributes.y = widget->allocation.y;
1123     attributes.width = widget->allocation.width;
1124     attributes.height = widget->allocation.height;
1125     attributes.wclass = GDK_INPUT_OUTPUT;
1126     attributes.visual = gdk_rgb_get_visual ();
1127     attributes.colormap = gdk_rgb_get_cmap ();
1128     attributes.event_mask = (gtk_widget_get_events (widget) |
1129                              GDK_EXPOSURE_MASK |
1130                              GDK_BUTTON_PRESS_MASK |
1131                              GDK_BUTTON_RELEASE_MASK |
1132                              GDK_POINTER_MOTION_MASK |
1133                              ( HAS_BROKEN_MOTION_HINTS ?
1134                                0 : GDK_POINTER_MOTION_HINT_MASK ) |
1135                              GDK_PROXIMITY_IN_MASK |
1136                              GDK_PROXIMITY_OUT_MASK |
1137                              GDK_KEY_PRESS_MASK |
1138                              GDK_KEY_RELEASE_MASK |
1139                              GDK_ENTER_NOTIFY_MASK |
1140                              GDK_LEAVE_NOTIFY_MASK |
1141                              GDK_FOCUS_CHANGE_MASK);
1142     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1144     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1145     gdk_window_set_user_data (widget->window, widget);
1147     if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1148         gtk_widget_set_events(widget, attributes.event_mask);
1150     widget->style = gtk_style_attach (widget->style, widget->window);
1152     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1154     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1157 /**
1158  * The canvas widget's unrealize callback.
1159  */
1160 static void
1161 sp_canvas_unrealize (GtkWidget *widget)
1163     SPCanvas *canvas = SP_CANVAS (widget);
1165     canvas->current_item = NULL;
1166     canvas->grabbed_item = NULL;
1167     canvas->focused_item = NULL;
1169     shutdown_transients (canvas);
1171     gdk_gc_destroy (canvas->pixmap_gc);
1172     canvas->pixmap_gc = NULL;
1174     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1175         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1178 /**
1179  * The canvas widget's size_request callback.
1180  */
1181 static void
1182 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1184     static_cast<void>(SP_CANVAS (widget));
1186     req->width = 256;
1187     req->height = 256;
1190 /**
1191  * The canvas widget's size_allocate callback.
1192  */
1193 static void
1194 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1196     SPCanvas *canvas = SP_CANVAS (widget);
1198     /* Schedule redraw of new region */
1199     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1200     if (allocation->width > widget->allocation.width) {
1201         sp_canvas_request_redraw (canvas,
1202                                   canvas->x0 + widget->allocation.width,
1203                                   0,
1204                                   canvas->x0 + allocation->width,
1205                                   canvas->y0 + allocation->height);
1206     }
1207     if (allocation->height > widget->allocation.height) {
1208         sp_canvas_request_redraw (canvas,
1209                                   0,
1210                                   canvas->y0 + widget->allocation.height,
1211                                   canvas->x0 + allocation->width,
1212                                   canvas->y0 + allocation->height);
1213     }
1215     widget->allocation = *allocation;
1216     
1217     if (GTK_WIDGET_REALIZED (widget)) {
1218         gdk_window_move_resize (widget->window,
1219                                 widget->allocation.x, widget->allocation.y,
1220                                 widget->allocation.width, widget->allocation.height);
1221     }
1224 /**
1225  * Helper that emits an event for an item in the canvas, be it the current
1226  * item, grabbed item, or focused item, as appropriate.
1227  */
1228 static int
1229 emit_event (SPCanvas *canvas, GdkEvent *event)
1231     guint mask;
1233     if (canvas->grabbed_item) {
1234         switch (event->type) {
1235         case GDK_ENTER_NOTIFY:
1236             mask = GDK_ENTER_NOTIFY_MASK;
1237             break;
1238         case GDK_LEAVE_NOTIFY:
1239             mask = GDK_LEAVE_NOTIFY_MASK;
1240             break;
1241         case GDK_MOTION_NOTIFY:
1242             mask = GDK_POINTER_MOTION_MASK;
1243             break;
1244         case GDK_BUTTON_PRESS:
1245         case GDK_2BUTTON_PRESS:
1246         case GDK_3BUTTON_PRESS:
1247             mask = GDK_BUTTON_PRESS_MASK;
1248             break;
1249         case GDK_BUTTON_RELEASE:
1250             mask = GDK_BUTTON_RELEASE_MASK;
1251             break;
1252         case GDK_KEY_PRESS:
1253             mask = GDK_KEY_PRESS_MASK;
1254             break;
1255         case GDK_KEY_RELEASE:
1256             mask = GDK_KEY_RELEASE_MASK;
1257             break;
1258         case GDK_SCROLL:
1259             mask = GDK_SCROLL;
1260             break;
1261         default:
1262             mask = 0;
1263             break;
1264         }
1266         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1267     }
1269     /* Convert to world coordinates -- we have two cases because of diferent
1270      * offsets of the fields in the event structures.
1271      */
1273     GdkEvent ev = *event;
1275     switch (ev.type) {
1276     case GDK_ENTER_NOTIFY:
1277     case GDK_LEAVE_NOTIFY:
1278         ev.crossing.x += canvas->x0;
1279         ev.crossing.y += canvas->y0;
1280         break;
1281     case GDK_MOTION_NOTIFY:
1282     case GDK_BUTTON_PRESS:
1283     case GDK_2BUTTON_PRESS:
1284     case GDK_3BUTTON_PRESS:
1285     case GDK_BUTTON_RELEASE:
1286         ev.motion.x += canvas->x0;
1287         ev.motion.y += canvas->y0;
1288         break;
1289     default:
1290         break;
1291     }
1293     /* Choose where we send the event */
1295     /* canvas->current_item becomes NULL in some cases under Win32
1296     ** (e.g. if the pointer leaves the window).  So this is a hack that
1297     ** Lauris applied to SP to get around the problem.
1298     */
1299     SPCanvasItem* item = NULL;
1300     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1301         item = canvas->grabbed_item;
1302     } else {
1303         item = canvas->current_item;
1304     }
1306     if (canvas->focused_item &&
1307         ((event->type == GDK_KEY_PRESS) ||
1308          (event->type == GDK_KEY_RELEASE) ||
1309          (event->type == GDK_FOCUS_CHANGE))) {
1310         item = canvas->focused_item;
1311     }
1313     /* The event is propagated up the hierarchy (for if someone connected to
1314      * a group instead of a leaf event), and emission is stopped if a
1315      * handler returns TRUE, just like for GtkWidget events.
1316      */
1318     gint finished = FALSE;
1320     while (item && !finished) {
1321         gtk_object_ref (GTK_OBJECT (item));
1322         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1323         SPCanvasItem *parent = item->parent;
1324         gtk_object_unref (GTK_OBJECT (item));
1325         item = parent;
1326     }
1328     return finished;
1331 /**
1332  * Helper that re-picks the current item in the canvas, based on the event's
1333  * coordinates and emits enter/leave events for items as appropriate.
1334  */
1335 static int
1336 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1338     int button_down = 0;
1339     double x, y;
1341     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1342         return FALSE;
1344     int retval = FALSE;
1346     if (canvas->gen_all_enter_events == false) {
1347         // If a button is down, we'll perform enter and leave events on the
1348         // current item, but not enter on any other item.  This is more or
1349         // less like X pointer grabbing for canvas items.
1350         //
1351         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1352                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1354         if (!button_down) canvas->left_grabbed_item = FALSE;
1355     }
1357     /* Save the event in the canvas.  This is used to synthesize enter and
1358      * leave events in case the current item changes.  It is also used to
1359      * re-pick the current item if the current one gets deleted.  Also,
1360      * synthesize an enter event.
1361      */
1362     if (event != &canvas->pick_event) {
1363         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1364             /* these fields have the same offsets in both types of events */
1366             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1367             canvas->pick_event.crossing.window     = event->motion.window;
1368             canvas->pick_event.crossing.send_event = event->motion.send_event;
1369             canvas->pick_event.crossing.subwindow  = NULL;
1370             canvas->pick_event.crossing.x          = event->motion.x;
1371             canvas->pick_event.crossing.y          = event->motion.y;
1372             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1373             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1374             canvas->pick_event.crossing.focus      = FALSE;
1375             canvas->pick_event.crossing.state      = event->motion.state;
1377             /* these fields don't have the same offsets in both types of events */
1379             if (event->type == GDK_MOTION_NOTIFY) {
1380                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1381                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1382             } else {
1383                 canvas->pick_event.crossing.x_root = event->button.x_root;
1384                 canvas->pick_event.crossing.y_root = event->button.y_root;
1385             }
1386         } else {
1387             canvas->pick_event = *event;
1388         }
1389     }
1391     /* Don't do anything else if this is a recursive call */
1392     if (canvas->in_repick) return retval;
1394     /* LeaveNotify means that there is no current item, so we don't look for one */
1395     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1396         /* these fields don't have the same offsets in both types of events */
1398         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1399             x = canvas->pick_event.crossing.x;
1400             y = canvas->pick_event.crossing.y;
1401         } else {
1402             x = canvas->pick_event.motion.x;
1403             y = canvas->pick_event.motion.y;
1404         }
1406         /* world coords */
1407         x += canvas->x0;
1408         y += canvas->y0;
1410         /* find the closest item */
1411         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1412             sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1413         } else {
1414             canvas->new_current_item = NULL;
1415         }
1416     } else {
1417         canvas->new_current_item = NULL;
1418     }
1420     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1421         return retval; /* current item did not change */
1422     }
1424     /* Synthesize events for old and new current items */
1426     if ((canvas->new_current_item != canvas->current_item)
1427         && (canvas->current_item != NULL)
1428         && !canvas->left_grabbed_item) {
1429         GdkEvent new_event;
1430         SPCanvasItem *item;
1432         item = canvas->current_item;
1434         new_event = canvas->pick_event;
1435         new_event.type = GDK_LEAVE_NOTIFY;
1437         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1438         new_event.crossing.subwindow = NULL;
1439         canvas->in_repick = TRUE;
1440         retval = emit_event (canvas, &new_event);
1441         canvas->in_repick = FALSE;
1442     }
1444     if (canvas->gen_all_enter_events == false) {
1445         // new_current_item may have been set to NULL during the call to
1446         // emit_event() above
1447         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1448             canvas->left_grabbed_item = TRUE;
1449             return retval;
1450         }
1451     }
1453     /* Handle the rest of cases */
1455     canvas->left_grabbed_item = FALSE;
1456     canvas->current_item = canvas->new_current_item;
1458     if (canvas->current_item != NULL) {
1459         GdkEvent new_event;
1461         new_event = canvas->pick_event;
1462         new_event.type = GDK_ENTER_NOTIFY;
1463         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1464         new_event.crossing.subwindow = NULL;
1465         retval = emit_event (canvas, &new_event);
1466     }
1468     return retval;
1471 /**
1472  * Button event handler for the canvas.
1473  */
1474 static gint
1475 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1477     SPCanvas *canvas = SP_CANVAS (widget);
1479     int retval = FALSE;
1481     /* dispatch normally regardless of the event's window if an item has
1482        has a pointer grab in effect */
1483     if (!canvas->grabbed_item &&
1484         event->window != SP_CANVAS_WINDOW (canvas))
1485         return retval;
1487     int mask;
1488     switch (event->button) {
1489     case 1:
1490         mask = GDK_BUTTON1_MASK;
1491         break;
1492     case 2:
1493         mask = GDK_BUTTON2_MASK;
1494         break;
1495     case 3:
1496         mask = GDK_BUTTON3_MASK;
1497         break;
1498     case 4:
1499         mask = GDK_BUTTON4_MASK;
1500         break;
1501     case 5:
1502         mask = GDK_BUTTON5_MASK;
1503         break;
1504     default:
1505         mask = 0;
1506     }
1508     switch (event->type) {
1509     case GDK_BUTTON_PRESS:
1510     case GDK_2BUTTON_PRESS:
1511     case GDK_3BUTTON_PRESS:
1512         /* Pick the current item as if the button were not pressed, and
1513          * then process the event.
1514          */
1515         canvas->state = event->state;
1516         pick_current_item (canvas, (GdkEvent *) event);
1517         canvas->state ^= mask;
1518         retval = emit_event (canvas, (GdkEvent *) event);
1519         break;
1521     case GDK_BUTTON_RELEASE:
1522         /* Process the event as if the button were pressed, then repick
1523          * after the button has been released
1524          */
1525         canvas->state = event->state;
1526         retval = emit_event (canvas, (GdkEvent *) event);
1527         event->state ^= mask;
1528         canvas->state = event->state;
1529         pick_current_item (canvas, (GdkEvent *) event);
1530         event->state ^= mask;
1531         break;
1533     default:
1534         g_assert_not_reached ();
1535     }
1537     return retval;
1540 /**
1541  * Scroll event handler for the canvas.
1542  *
1543  * \todo FIXME: generate motion events to re-select items.
1544  */
1545 static gint
1546 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1548     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1551 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1552     gdk_window_get_pointer(w, NULL, NULL, NULL);
1553 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1554     gdk_event_request_motions(event);
1555 #endif
1558 /**
1559  * Motion event handler for the canvas.
1560  */
1561 static int
1562 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1564     int status;
1565     SPCanvas *canvas = SP_CANVAS (widget);
1567     if (event->window != SP_CANVAS_WINDOW (canvas))
1568         return FALSE;
1570     if (canvas->pixmap_gc == NULL) // canvas being deleted
1571         return FALSE;
1573     canvas->state = event->state;
1574     pick_current_item (canvas, (GdkEvent *) event);
1576     status = emit_event (canvas, (GdkEvent *) event);
1578     if (event->is_hint) {
1579         request_motions(widget->window, event);
1580     }
1582     return status;
1585 static void
1586 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)
1588     GtkWidget *widget = GTK_WIDGET (canvas);
1590     SPCanvasBuf buf;
1591     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1592         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1593     } else {
1594         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1595     }
1597     // Mark the region clean
1598     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1600     buf.buf_rowstride = sw * 4; 
1601     buf.rect.x0 = x0;
1602     buf.rect.y0 = y0;
1603     buf.rect.x1 = x1;
1604     buf.rect.y1 = y1;
1605     buf.visible_rect.x0 = draw_x1;
1606     buf.visible_rect.y0 = draw_y1;
1607     buf.visible_rect.x1 = draw_x2;
1608     buf.visible_rect.y1 = draw_y2;
1609     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1610     buf.bg_color = (((color->red & 0xff00) << 8)
1611                     | (color->green & 0xff00)
1612                     | (color->blue >> 8));
1613     buf.is_empty = true;
1615     buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1617     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1618         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1619     }
1621 #if ENABLE_LCMS
1622     cmsHTRANSFORM transf = 0;
1623     long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1624     if ( fromDisplay ) {
1625         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1626     } else {
1627         transf = Inkscape::colorprofile_get_display_transform();
1628     }
1629 #endif // ENABLE_LCMS
1631     if (buf.is_empty) {
1632 #if ENABLE_LCMS
1633         if ( transf && canvas->enable_cms_display_adj ) {
1634             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1635         }
1636 #endif // ENABLE_LCMS
1637         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1638         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1639                             canvas->pixmap_gc,
1640                             TRUE,
1641                             x0 - canvas->x0, y0 - canvas->y0,
1642                             x1 - x0, y1 - y0);
1643     } else {
1645 #if ENABLE_LCMS
1646         if ( transf && canvas->enable_cms_display_adj ) {
1647             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1648                 guchar* p = buf.buf + (sw * 3) * yy;
1649                 cmsDoTransform( transf, p, p, (x1 - x0) );
1650             }
1651         }
1652 #endif // ENABLE_LCMS
1654 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1655 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1656 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1657 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1658 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1659 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1661 ///#define CANVAS_OUTPUT_VIA_CAIRO
1663 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1665         buf.cst = cairo_image_surface_create_for_data (
1666             buf.buf,
1667             CAIRO_FORMAT_ARGB32,  // unpacked, i.e. 32 bits! one byte is unused
1668             x1 - x0, y1 - y0,
1669             buf.buf_rowstride
1670             );
1671         cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1672         cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1673         cairo_paint (window_ct);
1674         cairo_destroy (window_ct);
1675         cairo_surface_finish (buf.cst);
1676         cairo_surface_destroy (buf.cst);
1678 #else
1680         NRPixBlock b3;
1681         nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1683         NRPixBlock b4;
1684         nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1685                                   buf.buf,
1686                                   buf.buf_rowstride,
1687                                   FALSE, FALSE);
1689         // this does the 32->24 squishing, using an assembler routine:
1690         nr_blit_pixblock_pixblock (&b3, &b4);
1692         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1693                                       canvas->pixmap_gc,
1694                                       x0 - canvas->x0, y0 - canvas->y0,
1695                                       x1 - x0, y1 - y0,
1696                                       GDK_RGB_DITHER_MAX,
1697                                       b3.data.px,
1698                                       sw * 3,
1699                                       x0 - canvas->x0, y0 - canvas->y0);
1701         nr_pixblock_release (&b3);
1702         nr_pixblock_release (&b4);
1703 #endif
1704     }
1706     cairo_surface_t *cst = cairo_get_target(buf.ct);
1707     cairo_destroy (buf.ct);
1708     cairo_surface_finish (cst);
1709     cairo_surface_destroy (cst);
1711     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1712         nr_pixelstore_256K_free (buf.buf);
1713     } else {
1714         nr_pixelstore_1M_free (buf.buf);
1715     }
1718 struct PaintRectSetup {
1719     SPCanvas* canvas;
1720     NRRectL big_rect;
1721     GTimeVal start_time;
1722     int max_pixels;
1723     NR::Point mouse_loc;
1724 };
1726 /**
1727  * Paint the given rect, recursively subdividing the region until it is the size of a single
1728  * buffer.
1729  *
1730  * @return true if the drawing completes
1731  */
1732 static int
1733 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1735     GTimeVal now;
1736     g_get_current_time (&now);
1738     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1739         + (now.tv_usec - setup->start_time.tv_usec);
1741     // Allow only very fast buffers to be run together;
1742     // as soon as the total redraw time exceeds 1ms, cancel;
1743     // this returns control to the idle loop and allows Inkscape to process user input
1744     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1745     // it will get back and finish painting what remains to paint.
1746     if (elapsed > 1000) {
1748         // Interrupting redraw isn't always good.
1749         // For example, when you drag one node of a big path, only the buffer containing
1750         // the mouse cursor will be redrawn again and again, and the rest of the path
1751         // will remain stale because Inkscape never has enough idle time to redraw all
1752         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1753         // If this limit is set, and if we have aborted redraw more times than is allowed,
1754         // interrupting is blocked and we're forced to redraw full screen once
1755         // (after which we can again interrupt forced_redraw_limit times).
1756         if (setup->canvas->forced_redraw_limit < 0 ||
1757             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1759             if (setup->canvas->forced_redraw_limit != -1) {
1760                 setup->canvas->forced_redraw_count++;
1761             }
1763             return false;
1764         }
1765     }
1767     // Find the optimal buffer dimensions
1768     int bw = this_rect.x1 - this_rect.x0;
1769     int bh = this_rect.y1 - this_rect.y0;
1770     if ((bw < 1) || (bh < 1))
1771         return 0;
1773     if (bw * bh < setup->max_pixels) {
1774         // We are small enough
1775         sp_canvas_paint_single_buffer (setup->canvas,
1776                                        this_rect.x0, this_rect.y0,
1777                                        this_rect.x1, this_rect.y1,
1778                                        setup->big_rect.x0, setup->big_rect.y0,
1779                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1780         return 1;
1781     }
1783     NRRectL lo = this_rect;
1784     NRRectL hi = this_rect;
1786 /*
1787 This test determines the redraw strategy:
1789 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1790 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1791 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1792 and seems to be faster for drawings with many smaller objects at zoom-out.
1794 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1795 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1796 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1797 faster.
1799 The default for now is the strips mode.
1800 */
1801     if (bw < bh || bh < 2 * TILE_SIZE) {
1802         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1803         int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1804         // Make sure that mid lies on a tile boundary
1805         mid = (mid / TILE_SIZE) * TILE_SIZE;
1807         lo.x1 = mid;
1808         hi.x0 = mid;
1810         if (setup->mouse_loc[NR::X] < mid) {
1811             // Always paint towards the mouse first
1812             return sp_canvas_paint_rect_internal(setup, lo)
1813                 && sp_canvas_paint_rect_internal(setup, hi);
1814         } else {
1815             return sp_canvas_paint_rect_internal(setup, hi)
1816                 && sp_canvas_paint_rect_internal(setup, lo);
1817         }
1818     } else {
1819         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1820         int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1821         // Make sure that mid lies on a tile boundary
1822         mid = (mid / TILE_SIZE) * TILE_SIZE;
1824         lo.y1 = mid;
1825         hi.y0 = mid;
1827         if (setup->mouse_loc[NR::Y] < mid) {
1828             // Always paint towards the mouse first
1829             return sp_canvas_paint_rect_internal(setup, lo)
1830                 && sp_canvas_paint_rect_internal(setup, hi);
1831         } else {
1832             return sp_canvas_paint_rect_internal(setup, hi)
1833                 && sp_canvas_paint_rect_internal(setup, lo);
1834         }
1835     }
1839 /**
1840  * Helper that draws a specific rectangular part of the canvas.
1841  *
1842  * @return true if the rectangle painting succeeds.
1843  */
1844 static bool
1845 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1847     g_return_val_if_fail (!canvas->need_update, false);
1849     NRRectL rect;
1850     rect.x0 = xx0;
1851     rect.x1 = xx1;
1852     rect.y0 = yy0;
1853     rect.y1 = yy1;
1855     // Clip rect-to-draw by the current visible area
1856     rect.x0 = MAX (rect.x0, canvas->x0);
1857     rect.y0 = MAX (rect.y0, canvas->y0);
1858     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1859     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1861 #ifdef DEBUG_REDRAW
1862     // paint the area to redraw yellow
1863     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1864     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1865                         canvas->pixmap_gc,
1866                         TRUE,
1867                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1868                         rect.x1 - rect.x0, rect.y1 - rect.y0);
1869 #endif
1871     PaintRectSetup setup;
1873     setup.canvas = canvas;
1874     setup.big_rect = rect;
1876     // Save the mouse location
1877     gint x, y;
1878     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1879     setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1881     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1882         // use 256K as a compromise to not slow down gradients
1883         // 256K is the cached buffer and we need 4 channels
1884         setup.max_pixels = 65536; // 256K/4
1885     } else {
1886         // paths only, so 1M works faster
1887         // 1M is the cached buffer and we need 4 channels
1888         setup.max_pixels = 262144; 
1889     }
1891     // Start the clock
1892     g_get_current_time(&(setup.start_time));
1894     // Go
1895     return sp_canvas_paint_rect_internal(&setup, rect);
1898 /**
1899  * Force a full redraw after a specified number of interrupted redraws
1900  */
1901 void
1902 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1903   g_return_if_fail(canvas != NULL);
1905   canvas->forced_redraw_limit = count;
1906   canvas->forced_redraw_count = 0;
1909 /**
1910  * End forced full redraw requests
1911  */
1912 void
1913 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1914   g_return_if_fail(canvas != NULL);
1916   canvas->forced_redraw_limit = -1;
1919 /**
1920  * The canvas widget's expose callback.
1921  */
1922 static gint
1923 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1925     SPCanvas *canvas = SP_CANVAS (widget);
1927     if (!GTK_WIDGET_DRAWABLE (widget) ||
1928         (event->window != SP_CANVAS_WINDOW (canvas)))
1929         return FALSE;
1931     int n_rects;
1932     GdkRectangle *rects;
1933     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1935     for (int i = 0; i < n_rects; i++) {
1936         NRRectL rect;
1938         rect.x0 = rects[i].x + canvas->x0;
1939         rect.y0 = rects[i].y + canvas->y0;
1940         rect.x1 = rect.x0 + rects[i].width;
1941         rect.y1 = rect.y0 + rects[i].height;
1943         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1944     }
1946     if (n_rects > 0)
1947         g_free (rects);
1949     return FALSE;
1952 /**
1953  * The canvas widget's keypress callback.
1954  */
1955 static gint
1956 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1958     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1961 /**
1962  * Crossing event handler for the canvas.
1963  */
1964 static gint
1965 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1967     SPCanvas *canvas = SP_CANVAS (widget);
1969     if (event->window != SP_CANVAS_WINDOW (canvas))
1970         return FALSE;
1972     canvas->state = event->state;
1973     return pick_current_item (canvas, (GdkEvent *) event);
1976 /**
1977  * Focus in handler for the canvas.
1978  */
1979 static gint
1980 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1982     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1984     SPCanvas *canvas = SP_CANVAS (widget);
1986     if (canvas->focused_item) {
1987         return emit_event (canvas, (GdkEvent *) event);
1988     } else {
1989         return FALSE;
1990     }
1993 /**
1994  * Focus out handler for the canvas.
1995  */
1996 static gint
1997 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1999     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2001     SPCanvas *canvas = SP_CANVAS (widget);
2003     if (canvas->focused_item)
2004         return emit_event (canvas, (GdkEvent *) event);
2005     else
2006         return FALSE;
2009 /**
2010  * Helper that repaints the areas in the canvas that need it.
2011  *
2012  * @return true if all the dirty parts have been redrawn
2013  */
2014 static int
2015 paint (SPCanvas *canvas)
2017     if (canvas->need_update) {
2018         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2019         canvas->need_update = FALSE;
2020     }
2022     if (!canvas->need_redraw)
2023         return TRUE;
2025     Gdk::Region to_paint;
2027     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2028         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2029             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2031             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2032                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2033                                    TILE_SIZE, TILE_SIZE));
2034             }
2036         }
2037     }
2039     if (!to_paint.empty()) {
2040         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2041         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2042         for (Iter i=rect.begin(); i != rect.end(); ++i) {
2043             int x0 = (*i).get_x();
2044             int y0 = (*i).get_y();
2045             int x1 = x0 + (*i).get_width();
2046             int y1 = y0 + (*i).get_height();
2047             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2048                 // Aborted
2049                 return FALSE;
2050             };
2051         }
2052     }
2054     canvas->need_redraw = FALSE;
2056     // we've had a full unaborted redraw, reset the full redraw counter
2057     if (canvas->forced_redraw_limit != -1) {
2058         canvas->forced_redraw_count = 0;
2059     }
2061     return TRUE;
2064 /**
2065  * Helper that invokes update, paint, and repick on canvas.
2066  */
2067 static int
2068 do_update (SPCanvas *canvas)
2070     if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2071         return TRUE;
2073     /* Cause the update if necessary */
2074     if (canvas->need_update) {
2075         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2076         canvas->need_update = FALSE;
2077     }
2079     /* Paint if able to */
2080     if (GTK_WIDGET_DRAWABLE (canvas)) {
2081             return paint (canvas);
2082     }
2084     /* Pick new current item */
2085     while (canvas->need_repick) {
2086         canvas->need_repick = FALSE;
2087         pick_current_item (canvas, &canvas->pick_event);
2088     }
2090     return TRUE;
2093 /**
2094  * Idle handler for the canvas that deals with pending updates and redraws.
2095  */
2096 static gint
2097 idle_handler (gpointer data)
2099     GDK_THREADS_ENTER ();
2101     SPCanvas *canvas = SP_CANVAS (data);
2103     int const ret = do_update (canvas);
2105     if (ret) {
2106         /* Reset idle id */
2107         canvas->idle_id = 0;
2108     }
2110     GDK_THREADS_LEAVE ();
2112     return !ret;
2115 /**
2116  * Convenience function to add an idle handler to a canvas.
2117  */
2118 static void
2119 add_idle (SPCanvas *canvas)
2121     if (canvas->idle_id != 0)
2122         return;
2124     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2127 /**
2128  * Returns the root group of the specified canvas.
2129  */
2130 SPCanvasGroup *
2131 sp_canvas_root (SPCanvas *canvas)
2133     g_return_val_if_fail (canvas != NULL, NULL);
2134     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2136     return SP_CANVAS_GROUP (canvas->root);
2139 /**
2140  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2141  */
2142 void
2143 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2145     g_return_if_fail (canvas != NULL);
2146     g_return_if_fail (SP_IS_CANVAS (canvas));
2148     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2149     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2150     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the 
2151     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2153     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2154     canvas->dy0 = cy;
2155     canvas->x0 = ix;
2156     canvas->y0 = iy;
2158     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2160     if (!clear) {
2161         // scrolling without zoom; redraw only the newly exposed areas
2162         if ((dx != 0) || (dy != 0)) {
2163             canvas->is_scrolling = is_scrolling;
2164             if (GTK_WIDGET_REALIZED (canvas)) {
2165                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2166             }
2167         }
2168     } else {
2169         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2170     }
2173 /**
2174  * Updates canvas if necessary.
2175  */
2176 void
2177 sp_canvas_update_now (SPCanvas *canvas)
2179     g_return_if_fail (canvas != NULL);
2180     g_return_if_fail (SP_IS_CANVAS (canvas));
2182     if (!(canvas->need_update ||
2183           canvas->need_redraw))
2184         return;
2186     do_update (canvas);
2189 /**
2190  * Update callback for canvas widget.
2191  */
2192 static void
2193 sp_canvas_request_update (SPCanvas *canvas)
2195     canvas->need_update = TRUE;
2196     add_idle (canvas);
2199 /**
2200  * Forces redraw of rectangular canvas area.
2201  */
2202 void
2203 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2205     NRRectL bbox;
2206     NRRectL visible;
2207     NRRectL clip;
2209     g_return_if_fail (canvas != NULL);
2210     g_return_if_fail (SP_IS_CANVAS (canvas));
2212     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2213     if ((x0 >= x1) || (y0 >= y1)) return;
2215     bbox.x0 = x0;
2216     bbox.y0 = y0;
2217     bbox.x1 = x1;
2218     bbox.y1 = y1;
2220     visible.x0 = canvas->x0;
2221     visible.y0 = canvas->y0;
2222     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2223     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2225     nr_rect_l_intersect (&clip, &bbox, &visible);
2227     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2228     add_idle (canvas);
2231 /**
2232  * Sets world coordinates from win and canvas.
2233  */
2234 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2236     g_return_if_fail (canvas != NULL);
2237     g_return_if_fail (SP_IS_CANVAS (canvas));
2239     if (worldx) *worldx = canvas->x0 + winx;
2240     if (worldy) *worldy = canvas->y0 + winy;
2243 /**
2244  * Sets win coordinates from world and canvas.
2245  */
2246 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2248     g_return_if_fail (canvas != NULL);
2249     g_return_if_fail (SP_IS_CANVAS (canvas));
2251     if (winx) *winx = worldx - canvas->x0;
2252     if (winy) *winy = worldy - canvas->y0;
2255 /**
2256  * Converts point from win to world coordinates.
2257  */
2258 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2260     g_assert (canvas != NULL);
2261     g_assert (SP_IS_CANVAS (canvas));
2263     return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2266 /**
2267  * Converts point from world to win coordinates.
2268  */
2269 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2271     g_assert (canvas != NULL);
2272     g_assert (SP_IS_CANVAS (canvas));
2274     return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2277 /**
2278  * Returns true if point given in world coordinates is inside window.
2279  */
2280 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2282     g_assert( canvas != NULL );
2283     g_assert(SP_IS_CANVAS(canvas));
2285     using NR::X;
2286     using NR::Y;
2287     GtkWidget const &w = *GTK_WIDGET(canvas);
2288     return ( ( canvas->x0 <= world[X] )  &&
2289              ( canvas->y0 <= world[Y] )  &&
2290              ( world[X] < canvas->x0 + w.allocation.width )  &&
2291              ( world[Y] < canvas->y0 + w.allocation.height ) );
2294 /**
2295  * Return canvas window coordinates as NR::Rect.
2296  */
2297 NR::Rect SPCanvas::getViewbox() const
2299     GtkWidget const *w = GTK_WIDGET(this);
2300     return NR::Rect(NR::Point(dx0, dy0),
2301                     NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2304 /**
2305  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2306  */
2307 NR::IRect SPCanvas::getViewboxIntegers() const
2309     GtkWidget const *w = GTK_WIDGET(this);
2310     return NR::IRect(NR::IPoint(x0, y0),
2311                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2314 inline int sp_canvas_tile_floor(int x)
2316     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2319 inline int sp_canvas_tile_ceil(int x)
2321     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2324 /**
2325  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2326  */
2327 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2329     if ( nl >= nr || nt >= nb ) {
2330         if ( canvas->tiles ) g_free(canvas->tiles);
2331         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2332         canvas->tileH=canvas->tileV=0;
2333         canvas->tiles=NULL;
2334         return;
2335     }
2336     int tl=sp_canvas_tile_floor(nl);
2337     int tt=sp_canvas_tile_floor(nt);
2338     int tr=sp_canvas_tile_ceil(nr);
2339     int tb=sp_canvas_tile_ceil(nb);
2341     int nh = tr-tl, nv = tb-tt;
2342     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2343     for (int i=tl; i<tr; i++) {
2344         for (int j=tt; j<tb; j++) {
2345             int ind = (i-tl) + (j-tt)*nh;
2346             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2347                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2348             } else {
2349                 ntiles[ind]=0; // newly exposed areas get 0
2350             }
2351         }
2352     }
2353     if ( canvas->tiles ) g_free(canvas->tiles);
2354     canvas->tiles=ntiles;
2355     canvas->tLeft=tl;
2356     canvas->tTop=tt;
2357     canvas->tRight=tr;
2358     canvas->tBottom=tb;
2359     canvas->tileH=nh;
2360     canvas->tileV=nv;
2363 /*
2364  * Helper that queues a canvas rectangle for redraw
2365  */
2366 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2367     canvas->need_redraw = TRUE;
2369     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2372 /**
2373  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2374  */
2375 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2377     if ( nl >= nr || nt >= nb ) {
2378         return;
2379     }
2380     int tl=sp_canvas_tile_floor(nl);
2381     int tt=sp_canvas_tile_floor(nt);
2382     int tr=sp_canvas_tile_ceil(nr);
2383     int tb=sp_canvas_tile_ceil(nb);
2384     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2385     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2386     if ( tr > canvas->tRight ) tr=canvas->tRight;
2387     if ( tt < canvas->tTop ) tt=canvas->tTop;
2388     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2390     for (int i=tl; i<tr; i++) {
2391         for (int j=tt; j<tb; j++) {
2392             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2393         }
2394     }
2398 /*
2399   Local Variables:
2400   mode:c++
2401   c-file-style:"stroustrup"
2402   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2403   indent-tabs-mode:nil
2404   fill-column:99
2405   End:
2406 */
2407 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :