Code

patch by Dennis Lin: optionally for debugging paint the rect-to-redraw yellow
[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>
28 #include <gtkmm.h>
30 #include <helper/sp-marshal.h>
31 #include <display/sp-canvas.h>
32 #include "display-forward.h"
33 #include <libnr/nr-matrix-fns.h>
34 #include <libnr/nr-matrix-ops.h>
35 #include <libnr/nr-convex-hull.h>
36 #include "prefs-utils.h"
37 #include "box3d-context.h"
38 #include "inkscape.h"
40 // Define this to visualize the regions to be redrawn
41 //#define DEBUG_REDRAW 1;
43 // Tiles are a way to minimize the number of redraws, eliminating too small redraws. 
44 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
45 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
46 #define TILE_SIZE 32
48 enum {
49         RENDERMODE_NORMAL,
50         RENDERMODE_NOAA,
51         RENDERMODE_OUTLINE
52 };
54 const gint sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
56 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
58 enum {
59     SP_CANVAS_ITEM_VISIBLE = 1 << 7,
60     SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
61     SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
62 };
64 /**
65  * A group of Items.
66  */
67 struct SPCanvasGroup {
68     SPCanvasItem item;
70     GList *items, *last;
71 };
73 /**
74  * The SPCanvasGroup vtable.
75  */
76 struct SPCanvasGroupClass {
77     SPCanvasItemClass parent_class;
78 };
80 /**
81  * The SPCanvas vtable.
82  */
83 struct SPCanvasClass {
84     GtkWidgetClass parent_class;
85 };
87 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
88 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
90 /* SPCanvasItem */
92 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
95 static void sp_canvas_request_update (SPCanvas *canvas);
97 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
98 static void sp_canvas_item_init (SPCanvasItem *item);
99 static void sp_canvas_item_dispose (GObject *object);
100 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args);
102 static int emit_event (SPCanvas *canvas, GdkEvent *event);
104 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
106 static GtkObjectClass *item_parent_class;
108 /**
109  * Registers the SPCanvasItem class with Glib and returns its type number.
110  */
111 GType
112 sp_canvas_item_get_type (void)
114     static GType type = 0;
115     if (!type) {
116         static const GTypeInfo info = {
117             sizeof (SPCanvasItemClass),
118             NULL, NULL,
119             (GClassInitFunc) sp_canvas_item_class_init,
120             NULL, NULL,
121             sizeof (SPCanvasItem),
122             0,
123             (GInstanceInitFunc) sp_canvas_item_init,
124             NULL
125         };
126         type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
127     }
129     return type;
132 /**
133  * Initializes the SPCanvasItem vtable and the "event" signal.
134  */
135 static void
136 sp_canvas_item_class_init (SPCanvasItemClass *klass)
138     GObjectClass *object_class = (GObjectClass *) klass;
140     /* fixme: Derive from GObject */
141     item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
143     item_signals[ITEM_EVENT] = g_signal_new ("event",
144                                              G_TYPE_FROM_CLASS (klass),
145                                              G_SIGNAL_RUN_LAST,
146                                              G_STRUCT_OFFSET (SPCanvasItemClass, event),
147                                              NULL, NULL,
148                                              sp_marshal_BOOLEAN__POINTER,
149                                              G_TYPE_BOOLEAN, 1,
150                                              GDK_TYPE_EVENT);
152     object_class->dispose = sp_canvas_item_dispose;
155 /**
156  * Callback for initialization of SPCanvasItem.
157  */
158 static void
159 sp_canvas_item_init (SPCanvasItem *item)
161     item->flags |= SP_CANVAS_ITEM_VISIBLE;
162     item->xform = NR::Matrix(NR::identity());
165 /**
166  * Constructs new SPCanvasItem on SPCanvasGroup.
167  */
168 SPCanvasItem *
169 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, const gchar *first_arg_name, ...)
171     va_list args;
173     g_return_val_if_fail (parent != NULL, NULL);
174     g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
175     g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
177     SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
179     va_start (args, first_arg_name);
180     sp_canvas_item_construct (item, parent, first_arg_name, args);
181     va_end (args);
183     return item;
186 /**
187  * Sets up the newly created SPCanvasItem.
188  *
189  * We make it static for encapsulation reasons since it was nowhere used.
190  */
191 static void
192 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args)
194     g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
195     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
197     item->parent = SP_CANVAS_ITEM (parent);
198     item->canvas = item->parent->canvas;
200     g_object_set_valist (G_OBJECT (item), first_arg_name, args);
202     group_add (SP_CANVAS_GROUP (item->parent), item);
204     sp_canvas_item_request_update (item);
205     sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
206     item->canvas->need_repick = TRUE;
209 /**
210  * Helper function that requests redraw only if item's visible flag is set.
211  */
212 static void
213 redraw_if_visible (SPCanvasItem *item)
215     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
216         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
217     }
220 /**
221  * Callback that removes item from all referers and destroys it.
222  */
223 static void
224 sp_canvas_item_dispose (GObject *object)
226     SPCanvasItem *item = SP_CANVAS_ITEM (object);
228     redraw_if_visible (item);
229     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
231     if (item == item->canvas->current_item) {
232         item->canvas->current_item = NULL;
233         item->canvas->need_repick = TRUE;
234     }
236     if (item == item->canvas->new_current_item) {
237         item->canvas->new_current_item = NULL;
238         item->canvas->need_repick = TRUE;
239     }
241     if (item == item->canvas->grabbed_item) {
242         item->canvas->grabbed_item = NULL;
243         gdk_pointer_ungrab (GDK_CURRENT_TIME);
244     }
246     if (item == item->canvas->focused_item)
247         item->canvas->focused_item = NULL;
249     if (item->parent) {
250         group_remove (SP_CANVAS_GROUP (item->parent), item);
251     }
253     G_OBJECT_CLASS (item_parent_class)->dispose (object);
256 /**
257  * Helper function to update item and its children.
258  *
259  * NB! affine is parent2canvas.
260  */
261 static void
262 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
264     /* Apply the child item's transform */
265     NR::Matrix child_affine = item->xform * affine;
267     /* apply object flags to child flags */
268     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
270     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
271         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
273     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
274         child_flags |= SP_CANVAS_UPDATE_AFFINE;
276     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
277         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
278             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
279     }
281     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
282     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
285 /** 
286  * Helper function to invoke the point method of the item.  
287  *
288  * The argument x, y should be in the parent's item-relative coordinate 
289  * system.  This routine applies the inverse of the item's transform, 
290  * maintaining the affine invariant.
291  */
292 static double
293 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
295     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
296         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
298     return NR_HUGE;
301 /**
302  * Makes the item's affine transformation matrix be equal to the specified
303  * matrix.
304  * 
305  * @item: A canvas item.
306  * @affine: An affine transformation matrix.
307  */
308 void
309 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const& affine)
311     item->xform = affine;
313     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
314         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
315         if (item->parent != NULL) {
316             sp_canvas_item_request_update (item->parent);
317         } else {
318             sp_canvas_request_update (item->canvas);
319         }
320     }
322     item->canvas->need_repick = TRUE;
325 /**
326  * Convenience function to reorder items in a group's child list.  
327  *
328  * This puts the specified link after the "before" link.
329  */
330 static void
331 put_item_after (GList *link, GList *before)
333     if (link == before)
334         return;
336     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
338     if (before == NULL) {
339         if (link == parent->items) return;
341         link->prev->next = link->next;
343         if (link->next) {
344             link->next->prev = link->prev;
345         } else {
346             parent->last = link->prev;
347         }
349         link->prev = before;
350         link->next = parent->items;
351         link->next->prev = link;
352         parent->items = link;
353     } else {
354         if ((link == parent->last) && (before == parent->last->prev))
355             return;
357         if (link->next)
358             link->next->prev = link->prev;
360         if (link->prev)
361             link->prev->next = link->next;
362         else {
363             parent->items = link->next;
364             parent->items->prev = NULL;
365         }
367         link->prev = before;
368         link->next = before->next;
370         link->prev->next = link;
372         if (link->next)
373             link->next->prev = link;
374         else
375             parent->last = link;
376     }
380 /**
381  * Raises the item in its parent's stack by the specified number of positions.
382  * 
383  * \param item A canvas item.
384  * \param positions Number of steps to raise the item.
385  *
386  * If the number of positions is greater than the distance to the top of the
387  * stack, then the item is put at the top.
388  */
389 void
390 sp_canvas_item_raise (SPCanvasItem *item, int positions)
392     g_return_if_fail (item != NULL);
393     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
394     g_return_if_fail (positions >= 0);
396     if (!item->parent || positions == 0)
397         return;
399     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
400     GList *link = g_list_find (parent->items, item);
401     g_assert (link != NULL);
403     GList *before;
404     for (before = link; positions && before; positions--)
405         before = before->next;
407     if (!before)
408         before = parent->last;
410     put_item_after (link, before);
412     redraw_if_visible (item);
413     item->canvas->need_repick = TRUE;
417 /**
418  * Lowers the item in its parent's stack by the specified number of positions.
419  *
420  * \param item A canvas item.
421  * \param positions Number of steps to lower the item.
422  *
423  * If the number of positions is greater than the distance to the bottom of the
424  * stack, then the item is put at the bottom.
425  **/
426 void
427 sp_canvas_item_lower (SPCanvasItem *item, int positions)
429     g_return_if_fail (item != NULL);
430     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
431     g_return_if_fail (positions >= 1);
433     if (!item->parent || positions == 0)
434         return;
436     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
437     GList *link = g_list_find (parent->items, item);
438     g_assert (link != NULL);
440     GList *before;
441     if (link->prev)
442         for (before = link->prev; positions && before; positions--)
443             before = before->prev;
444     else
445         before = NULL;
447     put_item_after (link, before);
449     redraw_if_visible (item);
450     item->canvas->need_repick = TRUE;
453 /**
454  * Sets visible flag on item and requests a redraw.
455  */
456 void
457 sp_canvas_item_show (SPCanvasItem *item)
459     g_return_if_fail (item != NULL);
460     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
462     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
463         return;
465     item->flags |= SP_CANVAS_ITEM_VISIBLE;
467     sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
468     item->canvas->need_repick = TRUE;
471 /**
472  * Clears visible flag on item and requests a redraw.
473  */
474 void
475 sp_canvas_item_hide (SPCanvasItem *item)
477     g_return_if_fail (item != NULL);
478     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
480     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
481         return;
483     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
485     sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
486     item->canvas->need_repick = TRUE;
489 /**
490  * Grab item under cursor.
491  * 
492  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
493  */
494 int
495 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
497     g_return_val_if_fail (item != NULL, -1);
498     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
499     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
501     if (item->canvas->grabbed_item)
502         return -1;
504     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
505         return -1;
507     /* fixme: Top hack (Lauris) */
508     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
509     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
510     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
511                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
512                       NULL, cursor, etime);
514     item->canvas->grabbed_item = item;
515     item->canvas->grabbed_event_mask = event_mask;
516     item->canvas->current_item = item; /* So that events go to the grabbed item */
518     return 0;
521 /**
522  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
523  * mouse.
524  * 
525  * \param item A canvas item that holds a grab.
526  * \param etime The timestamp for ungrabbing the mouse.
527  */
528 void
529 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
531     g_return_if_fail (item != NULL);
532     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
534     if (item->canvas->grabbed_item != item)
535         return;
537     item->canvas->grabbed_item = NULL;
539     gdk_pointer_ungrab (etime);
542 /**
543  * Returns the product of all transformation matrices from the root item down
544  * to the item.
545  */
546 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
548     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
550     NR::Matrix affine = NR::identity();
552     while (item) {
553         affine *= item->xform;
554         item = item->parent;
555     }
556     return affine;
559 /**
560  * Helper that returns true iff item is descendant of parent.
561  */
562 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
564     while (item) {
565         if (item == parent)
566             return true;
567         item = item->parent;
568     }
570     return false;
573 /**
574  * Focus canvas, and item under cursor if it is not already focussed.
575  */
576 void
577 sp_canvas_item_grab_focus (SPCanvasItem *item)
579     g_return_if_fail (item != NULL);
580     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
581     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
583     SPCanvasItem *focused_item = item->canvas->focused_item;
585     if (focused_item) {
586         GdkEvent ev;
587         ev.focus_change.type = GDK_FOCUS_CHANGE;
588         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
589         ev.focus_change.send_event = FALSE;
590         ev.focus_change.in = FALSE;
592         emit_event (item->canvas, &ev);
593     }
595     item->canvas->focused_item = item;
596     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
598     if (focused_item) {
599         GdkEvent ev;
600         ev.focus_change.type = GDK_FOCUS_CHANGE;
601         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
602         ev.focus_change.send_event = FALSE;
603         ev.focus_change.in = TRUE;
605         emit_event (item->canvas, &ev);
606     }
609 /**
610  * Requests that the canvas queue an update for the specified item.
611  * 
612  * To be used only by item implementations.
613  */
614 void
615 sp_canvas_item_request_update (SPCanvasItem *item)
617     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
618         return;
620     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
622     if (item->parent != NULL) {
623         /* Recurse up the tree */
624         sp_canvas_item_request_update (item->parent);
625     } else {
626         /* Have reached the top of the tree, make sure the update call gets scheduled. */
627         sp_canvas_request_update (item->canvas);
628     }
631 /**
632  * Returns position of item in group.
633  */
634 gint sp_canvas_item_order (SPCanvasItem * item)
636     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
639 /* SPCanvasGroup */
641 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
642 static void sp_canvas_group_init (SPCanvasGroup *group);
643 static void sp_canvas_group_destroy (GtkObject *object);
645 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
646 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
647 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
649 static SPCanvasItemClass *group_parent_class;
651 /**
652  * Registers SPCanvasGroup class with Gtk and returns its type number.
653  */
654 GtkType
655 sp_canvas_group_get_type (void)
657     static GtkType group_type = 0;
659     if (!group_type) {
660         static const GtkTypeInfo group_info = {
661             "SPCanvasGroup",
662             sizeof (SPCanvasGroup),
663             sizeof (SPCanvasGroupClass),
664             (GtkClassInitFunc) sp_canvas_group_class_init,
665             (GtkObjectInitFunc) sp_canvas_group_init,
666             NULL, NULL, NULL
667         };
669         group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
670     }
672     return group_type;
675 /**
676  * Class initialization function for SPCanvasGroupClass
677  */
678 static void
679 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
681     GtkObjectClass *object_class = (GtkObjectClass *) klass;
682     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
684     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
686     object_class->destroy = sp_canvas_group_destroy;
688     item_class->update = sp_canvas_group_update;
689     item_class->render = sp_canvas_group_render;
690     item_class->point = sp_canvas_group_point;
693 /**
694  * Callback. Empty.
695  */
696 static void
697 sp_canvas_group_init (SPCanvasGroup */*group*/)
699     /* Nothing here */
702 /**
703  * Callback that destroys all items in group and calls group's virtual 
704  * destroy() function.
705  */
706 static void
707 sp_canvas_group_destroy (GtkObject *object)
709     g_return_if_fail (object != NULL);
710     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
712     const SPCanvasGroup *group = SP_CANVAS_GROUP (object);
714     GList *list = group->items;
715     while (list) {
716         SPCanvasItem *child = (SPCanvasItem *)list->data;
717         list = list->next;
719         gtk_object_destroy (GTK_OBJECT (child));
720     }
722     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
723         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
726 /**
727  * Update handler for canvas groups
728  */
729 static void
730 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
732     const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
733     NR::ConvexHull corners(NR::Point(0, 0));
734     bool empty=true;
736     for (GList *list = group->items; list; list = list->next) {
737         SPCanvasItem *i = (SPCanvasItem *)list->data;
739         sp_canvas_item_invoke_update (i, affine, flags);
741         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
742             if (empty) {
743                 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
744                 empty = false;
745             } else {
746                 corners.add(NR::Point(i->x1, i->y1));
747             }
748             corners.add(NR::Point(i->x2, i->y2));
749         }
750     }
752     NR::Maybe<NR::Rect> const bounds = corners.bounds();
753     if (bounds) {
754         item->x1 = bounds->min()[NR::X];
755         item->y1 = bounds->min()[NR::Y];
756         item->x2 = bounds->max()[NR::X];
757         item->y2 = bounds->max()[NR::Y];
758     } else {
759         // FIXME ?
760         item->x1 = item->x2 = item->y1 = item->y2 = 0;
761     }
764 /**
765  * Point handler for canvas groups.
766  */
767 static double
768 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
770     const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
771     const double x = p[NR::X];
772     const double y = p[NR::Y];
773     int x1 = (int)(x - item->canvas->close_enough);
774     int y1 = (int)(y - item->canvas->close_enough);
775     int x2 = (int)(x + item->canvas->close_enough);
776     int y2 = (int)(y + item->canvas->close_enough);
778     double best = 0.0;
779     *actual_item = NULL;
781     double dist = 0.0;
783     for (GList *list = group->items; list; list = list->next) {
784         SPCanvasItem *child = (SPCanvasItem *)list->data;
786         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
787             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
789             int has_point;
790             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
791                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
792                 has_point = TRUE;
793             } else
794                 has_point = FALSE;
796             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
797                 best = dist;
798                 *actual_item = point_item;
799             }
800         }
801     }
803     return best;
806 /**
807  * Renders all visible canvas group items in buf rectangle.
808  */
809 static void
810 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
812     const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
814     for (GList *list = group->items; list; list = list->next) {
815         SPCanvasItem *child = (SPCanvasItem *)list->data;
816         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
817             if ((child->x1 < buf->rect.x1) &&
818                 (child->y1 < buf->rect.y1) &&
819                 (child->x2 > buf->rect.x0) &&
820                 (child->y2 > buf->rect.y0)) {
821                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
822                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
823             }
824         }
825     }
828 /**
829  * Adds an item to a canvas group.
830  */
831 static void
832 group_add (SPCanvasGroup *group, SPCanvasItem *item)
834     gtk_object_ref (GTK_OBJECT (item));
835     gtk_object_sink (GTK_OBJECT (item));
837     if (!group->items) {
838         group->items = g_list_append (group->items, item);
839         group->last = group->items;
840     } else {
841         group->last = g_list_append (group->last, item)->next;
842     }
844     sp_canvas_item_request_update (item);
847 /** 
848  * Removes an item from a canvas group
849  */
850 static void
851 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
853     g_return_if_fail (group != NULL);
854     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
855     g_return_if_fail (item != NULL);
857     for (GList *children = group->items; children; children = children->next) {
858         if (children->data == item) {
860             /* Unparent the child */
862             item->parent = NULL;
863             gtk_object_unref (GTK_OBJECT (item));
865             /* Remove it from the list */
867             if (children == group->last) group->last = children->prev;
869             group->items = g_list_remove_link (group->items, children);
870             g_list_free (children);
871             break;
872         }
873     }
876 /* SPCanvas */
878 static void sp_canvas_class_init (SPCanvasClass *klass);
879 static void sp_canvas_init (SPCanvas *canvas);
880 static void sp_canvas_destroy (GtkObject *object);
882 static void sp_canvas_realize (GtkWidget *widget);
883 static void sp_canvas_unrealize (GtkWidget *widget);
885 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
886 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
888 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
889 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
890 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
891 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
892 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
893 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
894 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
895 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
897 static GtkWidgetClass *canvas_parent_class;
899 void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb);
900 void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb);
901 static int do_update (SPCanvas *canvas);
903 /**
904  * Registers the SPCanvas class if necessary, and returns the type ID
905  * associated to it.
906  *
907  * \return The type ID of the SPCanvas class.
908  **/
909 GtkType
910 sp_canvas_get_type (void)
912     static GtkType canvas_type = 0;
914     if (!canvas_type) {
915         static const GtkTypeInfo canvas_info = {
916             "SPCanvas",
917             sizeof (SPCanvas),
918             sizeof (SPCanvasClass),
919             (GtkClassInitFunc) sp_canvas_class_init,
920             (GtkObjectInitFunc) sp_canvas_init,
921             NULL, NULL, NULL
922         };
924         canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
925     }
927     return canvas_type;
930 /**
931  * Class initialization function for SPCanvasClass.
932  */
933 static void
934 sp_canvas_class_init (SPCanvasClass *klass)
936     GtkObjectClass *object_class = (GtkObjectClass *) klass;
937     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
939     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
941     object_class->destroy = sp_canvas_destroy;
943     widget_class->realize = sp_canvas_realize;
944     widget_class->unrealize = sp_canvas_unrealize;
945     widget_class->size_request = sp_canvas_size_request;
946     widget_class->size_allocate = sp_canvas_size_allocate;
947     widget_class->button_press_event = sp_canvas_button;
948     widget_class->button_release_event = sp_canvas_button;
949     widget_class->motion_notify_event = sp_canvas_motion;
950     widget_class->scroll_event = sp_canvas_scroll;
951     widget_class->expose_event = sp_canvas_expose;
952     widget_class->key_press_event = sp_canvas_key;
953     widget_class->key_release_event = sp_canvas_key;
954     widget_class->enter_notify_event = sp_canvas_crossing;
955     widget_class->leave_notify_event = sp_canvas_crossing;
956     widget_class->focus_in_event = sp_canvas_focus_in;
957     widget_class->focus_out_event = sp_canvas_focus_out;
960 /** 
961  * Callback: object initialization for SPCanvas.
962  */
963 static void
964 sp_canvas_init (SPCanvas *canvas)
966     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
967     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
968     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
970     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
971     canvas->pick_event.crossing.x = 0;
972     canvas->pick_event.crossing.y = 0;
974     /* Create the root item as a special case */
975     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
976     canvas->root->canvas = canvas;
978     gtk_object_ref (GTK_OBJECT (canvas->root));
979     gtk_object_sink (GTK_OBJECT (canvas->root));
981     canvas->need_repick = TRUE;
983     // See comment at in sp-canvas.h.
984     canvas->gen_all_enter_events = false;
986     canvas->tiles=NULL;
987     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
988     canvas->tileH=canvas->tileV=0;
990     canvas->redraw_aborted.x0 = NR_HUGE_L;
991     canvas->redraw_aborted.x1 = -NR_HUGE_L;
992     canvas->redraw_aborted.y0 = NR_HUGE_L;
993     canvas->redraw_aborted.y1 = -NR_HUGE_L;
994     
995     canvas->redraw_count = 0;
996     
997     canvas->forced_redraw_count = 0;
998     canvas->forced_redraw_limit = -1;
1000     canvas->slowest_buffer = 0;
1002     canvas->is_scrolling = false;
1006 /**
1007  * Convenience function to remove the idle handler of a canvas.
1008  */
1009 static void
1010 remove_idle (SPCanvas *canvas)
1012     if (canvas->idle_id) {
1013         gtk_idle_remove (canvas->idle_id);
1014         canvas->idle_id = 0;
1015     }
1018 /*
1019  * Removes the transient state of the canvas (idle handler, grabs).
1020  */
1021 static void
1022 shutdown_transients (SPCanvas *canvas)
1024     /* We turn off the need_redraw flag, since if the canvas is mapped again
1025      * it will request a redraw anyways.  We do not turn off the need_update
1026      * flag, though, because updates are not queued when the canvas remaps
1027      * itself.
1028      */
1029     if (canvas->need_redraw) {
1030         canvas->need_redraw = FALSE;
1031     }
1032     if ( canvas->tiles ) g_free(canvas->tiles);
1033     canvas->tiles=NULL;
1034     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1035     canvas->tileH=canvas->tileV=0;
1037     if (canvas->grabbed_item) {
1038         canvas->grabbed_item = NULL;
1039         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1040     }
1042     remove_idle (canvas);
1045 /**
1046  * Destroy handler for SPCanvas.
1047  */
1048 static void
1049 sp_canvas_destroy (GtkObject *object)
1051     SPCanvas *canvas = SP_CANVAS (object);
1053     if (canvas->root) {
1054         gtk_object_unref (GTK_OBJECT (canvas->root));
1055         canvas->root = NULL;
1056     }
1058     shutdown_transients (canvas);
1060     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1061         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1064 /**
1065  * Returns new canvas as widget.
1066  */
1067 GtkWidget *
1068 sp_canvas_new_aa (void)
1070     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1072     return (GtkWidget *) canvas;
1075 /**
1076  * The canvas widget's realize callback.
1077  */
1078 static void
1079 sp_canvas_realize (GtkWidget *widget)
1081     SPCanvas *canvas = SP_CANVAS (widget);
1083     GdkWindowAttr attributes;
1084     attributes.window_type = GDK_WINDOW_CHILD;
1085     attributes.x = widget->allocation.x;
1086     attributes.y = widget->allocation.y;
1087     attributes.width = widget->allocation.width;
1088     attributes.height = widget->allocation.height;
1089     attributes.wclass = GDK_INPUT_OUTPUT;
1090     attributes.visual = gdk_rgb_get_visual ();
1091     attributes.colormap = gdk_rgb_get_cmap ();
1092     attributes.event_mask = (gtk_widget_get_events (widget) |
1093                              GDK_EXPOSURE_MASK |
1094                              GDK_BUTTON_PRESS_MASK |
1095                              GDK_BUTTON_RELEASE_MASK |
1096                              GDK_POINTER_MOTION_MASK |
1097                              GDK_PROXIMITY_IN_MASK |
1098                              GDK_PROXIMITY_OUT_MASK |
1099                              GDK_KEY_PRESS_MASK |
1100                              GDK_KEY_RELEASE_MASK |
1101                              GDK_ENTER_NOTIFY_MASK |
1102                              GDK_LEAVE_NOTIFY_MASK |
1103                              GDK_FOCUS_CHANGE_MASK);
1104     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1106     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1107     gdk_window_set_user_data (widget->window, widget);
1109     if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1110         gtk_widget_set_events(widget, attributes.event_mask);
1112     widget->style = gtk_style_attach (widget->style, widget->window);
1114     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1116     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1119 /**
1120  * The canvas widget's unrealize callback.
1121  */
1122 static void
1123 sp_canvas_unrealize (GtkWidget *widget)
1125     SPCanvas *canvas = SP_CANVAS (widget);
1127     shutdown_transients (canvas);
1129     gdk_gc_destroy (canvas->pixmap_gc);
1130     canvas->pixmap_gc = NULL;
1132     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1133         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1136 /**
1137  * The canvas widget's size_request callback.
1138  */
1139 static void
1140 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1142     static_cast<void>(SP_CANVAS (widget));
1144     req->width = 256;
1145     req->height = 256;
1148 /**
1149  * The canvas widget's size_allocate callback.
1150  */
1151 static void
1152 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1154     SPCanvas *canvas = SP_CANVAS (widget);
1156     /* Schedule redraw of new region */
1157     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1158     if (allocation->width > widget->allocation.width) {
1159         sp_canvas_request_redraw (canvas,
1160                                   canvas->x0 + widget->allocation.width,
1161                                   0,
1162                                   canvas->x0 + allocation->width,
1163                                   canvas->y0 + allocation->height);
1164     }
1165     if (allocation->height > widget->allocation.height) {
1166         sp_canvas_request_redraw (canvas,
1167                                   0,
1168                                   canvas->y0 + widget->allocation.height,
1169                                   canvas->x0 + allocation->width,
1170                                   canvas->y0 + allocation->height);
1171     }
1173     widget->allocation = *allocation;
1175     if (GTK_WIDGET_REALIZED (widget)) {
1176         gdk_window_move_resize (widget->window,
1177                                 widget->allocation.x, widget->allocation.y,
1178                                 widget->allocation.width, widget->allocation.height);
1179     }
1182 /**
1183  * Helper that emits an event for an item in the canvas, be it the current 
1184  * item, grabbed item, or focused item, as appropriate.
1185  */
1186 static int
1187 emit_event (SPCanvas *canvas, GdkEvent *event)
1189     guint mask;
1191     if (canvas->grabbed_item) {
1192         switch (event->type) {
1193         case GDK_ENTER_NOTIFY:
1194             mask = GDK_ENTER_NOTIFY_MASK;
1195             break;
1196         case GDK_LEAVE_NOTIFY:
1197             mask = GDK_LEAVE_NOTIFY_MASK;
1198             break;
1199         case GDK_MOTION_NOTIFY:
1200             mask = GDK_POINTER_MOTION_MASK;
1201             break;
1202         case GDK_BUTTON_PRESS:
1203         case GDK_2BUTTON_PRESS:
1204         case GDK_3BUTTON_PRESS:
1205             mask = GDK_BUTTON_PRESS_MASK;
1206             break;
1207         case GDK_BUTTON_RELEASE:
1208             mask = GDK_BUTTON_RELEASE_MASK;
1209             break;
1210         case GDK_KEY_PRESS:
1211             mask = GDK_KEY_PRESS_MASK;
1212             break;
1213         case GDK_KEY_RELEASE:
1214             mask = GDK_KEY_RELEASE_MASK;
1215             break;
1216         case GDK_SCROLL:
1217             mask = GDK_SCROLL;
1218             break;
1219         default:
1220             mask = 0;
1221             break;
1222         }
1224         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1225     }
1227     /* Convert to world coordinates -- we have two cases because of diferent
1228      * offsets of the fields in the event structures.
1229      */
1231     GdkEvent ev = *event;
1233     switch (ev.type) {
1234     case GDK_ENTER_NOTIFY:
1235     case GDK_LEAVE_NOTIFY:
1236         ev.crossing.x += canvas->x0;
1237         ev.crossing.y += canvas->y0;
1238         break;
1239     case GDK_MOTION_NOTIFY:
1240     case GDK_BUTTON_PRESS:
1241     case GDK_2BUTTON_PRESS:
1242     case GDK_3BUTTON_PRESS:
1243     case GDK_BUTTON_RELEASE:
1244         ev.motion.x += canvas->x0;
1245         ev.motion.y += canvas->y0;
1246         break;
1247     default:
1248         break;
1249     }
1251     /* Choose where we send the event */
1253     /* canvas->current_item becomes NULL in some cases under Win32
1254     ** (e.g. if the pointer leaves the window).  So this is a hack that
1255     ** Lauris applied to SP to get around the problem.
1256     */
1257     SPCanvasItem* item = NULL;
1258     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1259         item = canvas->grabbed_item;
1260     } else {
1261         item = canvas->current_item;
1262     }
1264     if (canvas->focused_item &&
1265         ((event->type == GDK_KEY_PRESS) ||
1266          (event->type == GDK_KEY_RELEASE) ||
1267          (event->type == GDK_FOCUS_CHANGE))) {
1268         item = canvas->focused_item;
1269     }
1271     /* The event is propagated up the hierarchy (for if someone connected to
1272      * a group instead of a leaf event), and emission is stopped if a
1273      * handler returns TRUE, just like for GtkWidget events.
1274      */
1276     gint finished = FALSE;
1278     while (item && !finished) {
1279         gtk_object_ref (GTK_OBJECT (item));
1280         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1281         SPCanvasItem *parent = item->parent;
1282         gtk_object_unref (GTK_OBJECT (item));
1283         item = parent;
1284     }
1286     return finished;
1289 /**
1290  * Helper that re-picks the current item in the canvas, based on the event's 
1291  * coordinates and emits enter/leave events for items as appropriate.
1292  */
1293 static int
1294 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1296     int button_down = 0;
1297     double x, y;
1299     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1300         return FALSE;
1302     int retval = FALSE;
1304     if (canvas->gen_all_enter_events == false) {
1305         // If a button is down, we'll perform enter and leave events on the
1306         // current item, but not enter on any other item.  This is more or
1307         // less like X pointer grabbing for canvas items.
1308         //
1309         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1310                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1312         if (!button_down) canvas->left_grabbed_item = FALSE;
1313     }
1315     /* Save the event in the canvas.  This is used to synthesize enter and
1316      * leave events in case the current item changes.  It is also used to
1317      * re-pick the current item if the current one gets deleted.  Also,
1318      * synthesize an enter event.
1319      */
1320     if (event != &canvas->pick_event) {
1321         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1322             /* these fields have the same offsets in both types of events */
1324             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1325             canvas->pick_event.crossing.window     = event->motion.window;
1326             canvas->pick_event.crossing.send_event = event->motion.send_event;
1327             canvas->pick_event.crossing.subwindow  = NULL;
1328             canvas->pick_event.crossing.x          = event->motion.x;
1329             canvas->pick_event.crossing.y          = event->motion.y;
1330             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1331             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1332             canvas->pick_event.crossing.focus      = FALSE;
1333             canvas->pick_event.crossing.state      = event->motion.state;
1335             /* these fields don't have the same offsets in both types of events */
1337             if (event->type == GDK_MOTION_NOTIFY) {
1338                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1339                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1340             } else {
1341                 canvas->pick_event.crossing.x_root = event->button.x_root;
1342                 canvas->pick_event.crossing.y_root = event->button.y_root;
1343             }
1344         } else {
1345             canvas->pick_event = *event;
1346         }
1347     }
1349     /* Don't do anything else if this is a recursive call */
1350     if (canvas->in_repick) return retval;
1352     /* LeaveNotify means that there is no current item, so we don't look for one */
1353     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1354         /* these fields don't have the same offsets in both types of events */
1356         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1357             x = canvas->pick_event.crossing.x;
1358             y = canvas->pick_event.crossing.y;
1359         } else {
1360             x = canvas->pick_event.motion.x;
1361             y = canvas->pick_event.motion.y;
1362         }
1364         /* world coords */
1365         x += canvas->x0;
1366         y += canvas->y0;
1368         /* find the closest item */
1369         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1370             sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1371         } else {
1372             canvas->new_current_item = NULL;
1373         }
1374     } else {
1375         canvas->new_current_item = NULL;
1376     }
1378     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1379         return retval; /* current item did not change */
1380     }
1382     /* Synthesize events for old and new current items */
1384     if ((canvas->new_current_item != canvas->current_item)
1385         && (canvas->current_item != NULL)
1386         && !canvas->left_grabbed_item) {
1387         GdkEvent new_event;
1388         SPCanvasItem *item;
1390         item = canvas->current_item;
1392         new_event = canvas->pick_event;
1393         new_event.type = GDK_LEAVE_NOTIFY;
1395         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1396         new_event.crossing.subwindow = NULL;
1397         canvas->in_repick = TRUE;
1398         retval = emit_event (canvas, &new_event);
1399         canvas->in_repick = FALSE;
1400     }
1402     if (canvas->gen_all_enter_events == false) {
1403         // new_current_item may have been set to NULL during the call to
1404         // emit_event() above
1405         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1406             canvas->left_grabbed_item = TRUE;
1407             return retval;
1408         }
1409     }
1411     /* Handle the rest of cases */
1413     canvas->left_grabbed_item = FALSE;
1414     canvas->current_item = canvas->new_current_item;
1416     if (canvas->current_item != NULL) {
1417         GdkEvent new_event;
1419         new_event = canvas->pick_event;
1420         new_event.type = GDK_ENTER_NOTIFY;
1421         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1422         new_event.crossing.subwindow = NULL;
1423         retval = emit_event (canvas, &new_event);
1424     }
1426     return retval;
1429 /**
1430  * Button event handler for the canvas.
1431  */
1432 static gint
1433 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1435     SPCanvas *canvas = SP_CANVAS (widget);
1437     int retval = FALSE;
1439     /* dispatch normally regardless of the event's window if an item has
1440        has a pointer grab in effect */
1441     if (!canvas->grabbed_item && 
1442         event->window != SP_CANVAS_WINDOW (canvas))
1443         return retval;
1445     int mask;
1446     switch (event->button) {
1447     case 1:
1448         mask = GDK_BUTTON1_MASK;
1449         break;
1450     case 2:
1451         mask = GDK_BUTTON2_MASK;
1452         break;
1453     case 3:
1454         mask = GDK_BUTTON3_MASK;
1455         break;
1456     case 4:
1457         mask = GDK_BUTTON4_MASK;
1458         break;
1459     case 5:
1460         mask = GDK_BUTTON5_MASK;
1461         break;
1462     default:
1463         mask = 0;
1464     }
1466     switch (event->type) {
1467     case GDK_BUTTON_PRESS:
1468     case GDK_2BUTTON_PRESS:
1469     case GDK_3BUTTON_PRESS:
1470         /* Pick the current item as if the button were not pressed, and
1471          * then process the event.
1472          */
1473         canvas->state = event->state;
1474         pick_current_item (canvas, (GdkEvent *) event);
1475         canvas->state ^= mask;
1476         retval = emit_event (canvas, (GdkEvent *) event);
1477         break;
1479     case GDK_BUTTON_RELEASE:
1480         /* Process the event as if the button were pressed, then repick
1481          * after the button has been released
1482          */
1483         canvas->state = event->state;
1484         retval = emit_event (canvas, (GdkEvent *) event);
1485         event->state ^= mask;
1486         canvas->state = event->state;
1487         pick_current_item (canvas, (GdkEvent *) event);
1488         event->state ^= mask;
1489         break;
1491     default:
1492         g_assert_not_reached ();
1493     }
1495     return retval;
1498 /**
1499  * Scroll event handler for the canvas.
1500  *
1501  * \todo FIXME: generate motion events to re-select items.
1502  */
1503 static gint
1504 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1506     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1509 /**
1510  * Motion event handler for the canvas.
1511  */
1512 static int
1513 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1515     SPCanvas *canvas = SP_CANVAS (widget);
1517     if (event->window != SP_CANVAS_WINDOW (canvas))
1518         return FALSE;
1520     if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1521         gint x, y;
1522         gdk_window_get_pointer (widget->window, &x, &y, NULL);
1523         event->x = x;
1524         event->y = y;
1525     }
1527     canvas->state = event->state;
1528     pick_current_item (canvas, (GdkEvent *) event);
1530     return emit_event (canvas, (GdkEvent *) event);
1533 static void
1534 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)
1536     GtkWidget *widget = GTK_WIDGET (canvas);
1538     SPCanvasBuf buf;
1539     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1540         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1541     } else {
1542         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1543     }
1545     buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1546     buf.rect.x0 = x0;
1547     buf.rect.y0 = y0;
1548     buf.rect.x1 = x1;
1549     buf.rect.y1 = y1;
1550     buf.visible_rect.x0 = draw_x1;
1551     buf.visible_rect.y0 = draw_y1;
1552     buf.visible_rect.x1 = draw_x2;
1553     buf.visible_rect.y1 = draw_y2;
1554     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1555     buf.bg_color = (((color->red & 0xff00) << 8)
1556                     | (color->green & 0xff00)
1557                     | (color->blue >> 8));
1558     buf.is_empty = true;
1559       
1560     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1561         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1562     }
1563       
1564     if (buf.is_empty) {
1565         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1566         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1567                             canvas->pixmap_gc,
1568                             TRUE,
1569                             x0 - canvas->x0, y0 - canvas->y0,
1570                             x1 - x0, y1 - y0);
1571     } else {
1572 /*
1573 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1574 // Why this must not be done currently:
1575 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1576 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)  
1577 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels, 
1578 // we need more bufs to paint a given area and as a result it's even a bit slower
1580     cairo_surface_t* cst = cairo_image_surface_create_for_data (
1581         buf.buf,
1582         CAIRO_FORMAT_RGB24,  // unpacked, i.e. 32 bits! one byte is unused
1583         x1 - x0, y1 - y0,
1584         buf.buf_rowstride
1585         );
1586         cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1587         cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1588         cairo_paint (ct);
1589     cairo_destroy (ct);
1590     cairo_surface_finish (cst);
1591     cairo_surface_destroy (cst);
1592 */
1594         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1595                                       canvas->pixmap_gc,
1596                                       x0 - canvas->x0, y0 - canvas->y0,
1597                                       x1 - x0, y1 - y0,
1598                                       GDK_RGB_DITHER_MAX,
1599                                       buf.buf,
1600                                       sw * 3,
1601                                       x0 - canvas->x0, y0 - canvas->y0);
1602     }
1604     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1605         nr_pixelstore_256K_free (buf.buf);
1606     } else {
1607         nr_pixelstore_1M_free (buf.buf);
1608     }
1611 /* Paint the given rect, while updating canvas->redraw_aborted and running iterations after each
1612  * buffer; make sure canvas->redraw_aborted never goes past aborted_limit (used for 2-rect
1613  * optimized repaint)
1614  */
1615 static int
1616 sp_canvas_paint_rect_internal (SPCanvas *canvas, NRRectL *rect, NR::ICoord *x_aborted_limit, NR::ICoord *y_aborted_limit)
1618     int draw_x1 = rect->x0;
1619     int draw_x2 = rect->x1;
1620     int draw_y1 = rect->y0;
1621     int draw_y2 = rect->y1;
1623     // Here we'll store the time it took to draw the slowest buffer of this paint. 
1624     glong slowest_buffer = 0;
1626     // Find the optimal buffer dimensions
1627     int bw = draw_x2 - draw_x1;
1628     int bh = draw_y2 - draw_y1;
1629     if ((bw < 1) || (bh < 1))
1630         return 0;
1632     int sw, sh;  // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1633     if (canvas->rendermode != RENDERMODE_OUTLINE) { // use 256K as a compromise to not slow down gradients
1634         /* 256K is the cached buffer and we need 3 channels */
1635         if (bw * bh <  87381) { // 256K/3
1636             // We can go with single buffer 
1637             sw = bw;
1638             sh = bh;
1639         } else if (bw <= (16 * 341)) {
1640             // Go with row buffer 
1641             sw = bw;
1642             sh =  87381 / bw;
1643         } else if (bh <= (16 * 256)) {
1644             // Go with column buffer 
1645             sw = 87381 / bh;
1646             sh = bh;
1647         } else {
1648             sw = 341;
1649             sh = 256;
1650         }
1651     } else {  // paths only, so 1M works faster
1652         /* 1M is the cached buffer and we need 3 channels */
1653         if (bw * bh <  349525) { // 1M/3
1654             // We can go with single buffer 
1655             sw = bw;
1656             sh = bh;
1657         } else if (bw <= (16 * 682)) {
1658             // Go with row buffer 
1659             sw = bw;
1660             sh =  349525 / bw;
1661         } else if (bh <= (16 * 512)) {
1662             // Go with column buffer 
1663             sw = 349525 / bh;
1664             sh = bh;
1665         } else {
1666             sw = 682;
1667             sh = 512;
1668         }
1669     }
1670     
1671     // Will this paint require more than one buffer?
1672     bool multiple_buffers = (((draw_y2 - draw_y1) > sh) || ((draw_x2 - draw_x1) > sw)); // or two_rects
1674     // remember the counter during this paint
1675     long this_count = canvas->redraw_count;
1677     // Time values to measure each buffer's paint time
1678     GTimeVal tstart, tfinish;
1680     // paint from the corner nearest the mouse pointer
1682     gint x, y;
1683     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1684     NR::Point pw = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1686     bool reverse_x = (pw[NR::X] > ((draw_x2 + draw_x1) / 2));
1687     bool reverse_y = (pw[NR::Y] > ((draw_y2 + draw_y1) / 2));
1689     if ((bw > bh) && (sh > sw)) {
1690       int t = sw; sw = sh; sh = t;
1691     }
1693     // This is the main loop which corresponds to the visible left-to-right, top-to-bottom drawing
1694     // of screen blocks (buffers).
1695     for (int y0 = draw_y1; y0 < draw_y2; y0 += sh) {
1696         int y1 = MIN (y0 + sh, draw_y2);
1697         for (int x0 = draw_x1; x0 < draw_x2; x0 += sw) {
1698             int x1 = MIN (x0 + sw, draw_x2);
1700             int dx0 = x0;
1701             int dx1 = x1;
1702             int dy0 = y0;
1703             int dy1 = y1;
1705             if (reverse_x) { 
1706               dx0 = (draw_x2 - (x0 + sw)) + draw_x1;
1707               dx0 = MAX (dx0, draw_x1);
1708               dx1 = (draw_x2 - x0) + draw_x1;
1709               dx1 = MIN (dx1, draw_x2);
1710             }
1711             if (reverse_y) { 
1712               dy0 = (draw_y2 - (y0 + sh)) + draw_y1;
1713               dy0 = MAX (dy0, draw_y1);
1714               dy1 = (draw_y2 - y0) + draw_y1;
1715               dy1 = MIN (dy1, draw_y2);
1716             }
1718             // SMOOTH SCROLLING: if we are scrolling, process pending events even before doing any rendering. 
1719             // This allows for scrolling smoothly without hiccups. Any accumulated redraws will be made 
1720             // when scrolling stops. The scrolling flag is set by sp_canvas_scroll_to for each scroll and zeroed
1721             // here for each redraw, to ensure it never gets stuck. 
1723             // OPTIMIZATION IDEA: if drawing is really slow (as measured by canvas->slowest
1724             // buffer), do the same - process some events even before we paint any buffers
1726             if (canvas->is_scrolling) {
1727                 while (Gtk::Main::events_pending()) { // process any events
1728                     Gtk::Main::iteration(false);
1729                 }
1730                 canvas->is_scrolling = false;
1731                 if (this_count != canvas->redraw_count) { // if there was redraw,
1732                     return 1; // interrupt this one
1733                 }
1734             }
1735             
1736             // Paint one buffer; measure how long it takes.
1737             g_get_current_time (&tstart);
1738             sp_canvas_paint_single_buffer (canvas, dx0, dy0, dx1, dy1, draw_x1, draw_y1, draw_x2, draw_y2, sw);
1739             g_get_current_time (&tfinish);
1741             // Remember the slowest_buffer of this paint.
1742             glong this_buffer = (tfinish.tv_sec - tstart.tv_sec) * 1000000 + (tfinish.tv_usec - tstart.tv_usec);
1743             if (this_buffer > slowest_buffer) 
1744                 slowest_buffer = this_buffer;
1746             // After each successful buffer, reduce the rect remaining to redraw by what is already redrawn
1747             if (x1 >= draw_x2) {
1748               if (reverse_y) {
1749                 if (canvas->redraw_aborted.y1 > dy0) { canvas->redraw_aborted.y1 = dy0; }
1750               } else {
1751                 if (canvas->redraw_aborted.y0 < y1)  { canvas->redraw_aborted.y0 = y1; }
1752               }
1753             }
1755             if (y1 >= draw_y2) {
1756               if (reverse_x) {
1757                 if (canvas->redraw_aborted.x1 > dx0) { canvas->redraw_aborted.x1 = dx0; }
1758               } else {
1759                 if (canvas->redraw_aborted.x0 < x1)  { canvas->redraw_aborted.x0 = x1; }
1760               }
1761             }
1763             // INTERRUPTIBLE DISPLAY:
1764             // Process events that may have arrived while we were busy drawing;
1765             // only if we're drawing multiple buffers, and only if this one was not very fast,
1766             // and only if we're allowed to interrupt this redraw
1767             bool ok_to_interrupt = (multiple_buffers && this_buffer > 25000);
1768             if (ok_to_interrupt && (canvas->forced_redraw_limit != -1)) {
1769                 ok_to_interrupt = (canvas->forced_redraw_count < canvas->forced_redraw_limit);
1770             }
1772             if (ok_to_interrupt) {
1773                 // Run at most max_iterations of the main loop; we cannot process ALL events
1774                 // here because some things (e.g. rubberband) flood with dirtying events but will
1775                 // not redraw themselves
1776                 int max_iterations = 10;
1777                 int iterations = 0;
1778                 while (Gtk::Main::events_pending() && iterations++ < max_iterations) {
1779                     Gtk::Main::iteration(false);
1780                     // If one of the iterations has redrawn by itself, abort
1781                     if (this_count != canvas->redraw_count) {
1782                         canvas->slowest_buffer = slowest_buffer;
1783                         if (canvas->forced_redraw_limit != -1) {
1784                             canvas->forced_redraw_count++;
1785                         }
1786                         return 1; // interrupted
1787                     }
1788                 }
1789    
1790                 // If not aborted so far, check if the events set redraw or update flags; 
1791                 // if so, force update and abort
1792                 if (canvas->need_redraw || canvas->need_update) {
1793                     canvas->slowest_buffer = slowest_buffer;
1794                     if (canvas->forced_redraw_limit != -1) {
1795                         canvas->forced_redraw_count++;
1796                     }
1797                     do_update (canvas);
1798                     return 1; // interrupted
1799                 }
1800             }
1801         }
1802     }
1804     // Remember the slowest buffer of this paint in canvas
1805     canvas->slowest_buffer = slowest_buffer;
1807     return 0; // finished
1811 /**
1812  * Helper that draws a specific rectangular part of the canvas.
1813  */
1814 static void
1815 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1817     g_return_if_fail (!canvas->need_update);
1818  
1819     // Monotonously increment the canvas-global counter on each paint. This will let us find out
1820     // when a new paint happened in event processing during this paint, so we can abort it.
1821     canvas->redraw_count++;
1823     NRRectL rect;
1824     rect.x0 = xx0;
1825     rect.x1 = xx1;
1826     rect.y0 = yy0;
1827     rect.y1 = yy1;
1829     // Clip rect-to-draw by the current visible area
1830     rect.x0 = MAX (rect.x0, canvas->x0);
1831     rect.y0 = MAX (rect.y0, canvas->y0);
1832     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1833     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1835 #ifdef DEBUG_REDRAW
1836     // paint the area to redraw yellow
1837     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1838     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1839                             canvas->pixmap_gc,
1840                             TRUE,
1841                             rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1842                             rect.x1 - rect.x0, rect.y1 - rect.y0);
1843 #endif                      
1845     // Clip rect-aborted-last-time by the current visible area
1846     canvas->redraw_aborted.x0 = MAX (canvas->redraw_aborted.x0, canvas->x0);
1847     canvas->redraw_aborted.y0 = MAX (canvas->redraw_aborted.y0, canvas->y0);
1848     canvas->redraw_aborted.x1 = MIN (canvas->redraw_aborted.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1849     canvas->redraw_aborted.y1 = MIN (canvas->redraw_aborted.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1851     if (canvas->redraw_aborted.x0 < canvas->redraw_aborted.x1 && canvas->redraw_aborted.y0 < canvas->redraw_aborted.y1) {
1852         // There was an aborted redraw last time, now we need to redraw BOTH it and the new rect.
1854         // save the old aborted rect in case we decide to paint it separately (see below)
1855         NRRectL aborted = canvas->redraw_aborted;
1857         // calculate the rectangle union of the both rects (the smallest rectangle which covers both) 
1858         NRRectL nion;
1859         nr_rect_l_union (&nion, &rect, &aborted);
1861         // subtract one of the rects-to-draw from the other (the smallest rectangle which covers
1862         // all of the first not covered by the second)
1863         NRRectL rect_minus_aborted;
1864         nr_rect_l_subtract (&rect_minus_aborted, &rect, &aborted);
1866         // Initially, the rect to redraw later (in case we're aborted) is the same as the union of both rects
1867         canvas->redraw_aborted = nion;
1869         // calculate areas of the three rects
1870         if ((nr_rect_l_area(&rect_minus_aborted) + nr_rect_l_area(&aborted)) * 1.2 < nr_rect_l_area(&nion)) {
1871             // If the summary area of the two rects is significantly (at least by 20%) less than
1872             // the area of their rectangular union, it makes sense to paint the two rects
1873             // separately instead of painting their union. This gives a significant speedup when,
1874             // for example, your current canvas is almost painted, with only a strip at bottom
1875             // left, and at that moment you abort it by scrolling down which reveals a new strip at
1876             // the top. Straightforward painting of the union of the aborted rect and the new rect
1877             // will have to repaint the entire canvas! By contrast, the optimized approach below
1878             // paints the two narrow strips in order which is much faster.
1880             // find out which rect to draw first - compare them first by y then by x of the top left corners
1881             NRRectL *first;
1882             NRRectL *second;
1883             if (rect.y0 == aborted.y0) {
1884                 if (rect.x0 < aborted.x0) {
1885                     first = &rect;
1886                     second = &aborted;
1887                 } else {
1888                     second = &rect;
1889                     first = &aborted;
1890                 }
1891             } else if (rect.y0 < aborted.y0) {
1892                 first = &rect;
1893                 second = &aborted;
1894             } else {
1895                 second = &rect;
1896                 first = &aborted;
1897             }
1899             NRRectL second_minus_first;
1900             nr_rect_l_subtract (&second_minus_first, second, first);
1902             // paint the first rect;
1903             if (sp_canvas_paint_rect_internal (canvas, first, &(second_minus_first.x0), &(second_minus_first.y0))) {
1904                 // aborted!
1905                 return;
1906             }
1908             // if not aborted, assign (second rect minus first) as the new redraw_aborted and paint the same
1909             canvas->redraw_aborted = second_minus_first;
1910             if (sp_canvas_paint_rect_internal (canvas, &second_minus_first, NULL, NULL)) {
1911                 return; // aborted
1912             }
1914         } else {
1915             // no need for separate drawing, just draw the union as one rect
1916             if (sp_canvas_paint_rect_internal (canvas, &nion, NULL, NULL)) {
1917                 return; // aborted
1918             }
1919         }
1920     } else {
1921         // Nothing was aborted last time, just draw the rect we're given
1923         // Initially, the rect to redraw later (in case we're aborted) is the same as the one we're going to draw now.
1924         canvas->redraw_aborted = rect;
1926         if (sp_canvas_paint_rect_internal (canvas, &rect, NULL, NULL)) {
1927             return; // aborted
1928         }
1929     }
1931     // we've had a full unaborted redraw, reset the full redraw counter
1932     if (canvas->forced_redraw_limit != -1) {
1933         canvas->forced_redraw_count = 0;
1934     }
1937 /**
1938  * Force a full redraw after a specified number of interrupted redraws
1939  */
1940 void
1941 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1942   g_return_if_fail(canvas != NULL);
1943   
1944   canvas->forced_redraw_limit = count;
1945   canvas->forced_redraw_count = 0;
1948 /**
1949  * End forced full redraw requests
1950  */
1951 void
1952 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1953   g_return_if_fail(canvas != NULL);
1955   canvas->forced_redraw_limit = -1;
1958 /**
1959  * The canvas widget's expose callback.
1960  */
1961 static gint
1962 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1964     SPCanvas *canvas = SP_CANVAS (widget);
1966     if (!GTK_WIDGET_DRAWABLE (widget) || 
1967         (event->window != SP_CANVAS_WINDOW (canvas)))
1968         return FALSE;
1970     int n_rects;
1971     GdkRectangle *rects;
1972     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1974     for (int i = 0; i < n_rects; i++) {
1975         NRRectL rect;
1976                 
1977         rect.x0 = rects[i].x + canvas->x0;
1978         rect.y0 = rects[i].y + canvas->y0;
1979         rect.x1 = rect.x0 + rects[i].width;
1980         rect.y1 = rect.y0 + rects[i].height;
1982         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1983     }
1985     if (n_rects > 0)
1986         g_free (rects);
1988     return FALSE;
1991 /**
1992  * The canvas widget's keypress callback.
1993  */
1994 static gint
1995 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1997     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
2000 /**
2001  * Crossing event handler for the canvas.
2002  */
2003 static gint
2004 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
2006     SPCanvas *canvas = SP_CANVAS (widget);
2008     if (event->window != SP_CANVAS_WINDOW (canvas))
2009         return FALSE;
2011     canvas->state = event->state;
2012     return pick_current_item (canvas, (GdkEvent *) event);
2015 /**
2016  * Focus in handler for the canvas.
2017  */
2018 static gint
2019 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2021     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2023     SPCanvas *canvas = SP_CANVAS (widget);
2025     if (canvas->focused_item) {
2026         return emit_event (canvas, (GdkEvent *) event);
2027     } else {
2028         return FALSE;
2029     }
2032 /**
2033  * Focus out handler for the canvas.
2034  */
2035 static gint
2036 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2038     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2040     SPCanvas *canvas = SP_CANVAS (widget);
2042     if (canvas->focused_item)
2043         return emit_event (canvas, (GdkEvent *) event);
2044     else
2045         return FALSE;
2048 /**
2049  * Helper that repaints the areas in the canvas that need it.
2050  */
2051 static int
2052 paint (SPCanvas *canvas)
2054     if (canvas->need_update) {
2055         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2056         canvas->need_update = FALSE;
2057     }
2059     if (!canvas->need_redraw)
2060         return TRUE;
2062     Gdk::Region to_paint;
2064     for (int j=canvas->tTop; j<canvas->tBottom; j++) { 
2065         for (int i=canvas->tLeft; i<canvas->tRight; i++) { 
2067             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2069             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2070                 to_paint.union_with_rect(
2071                     Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2072                                      TILE_SIZE,   TILE_SIZE));
2073             }
2075             canvas->tiles[tile_index] = 0; // undirty this tile
2076         }
2077     }
2079     canvas->need_redraw = FALSE;
2081     if (~to_paint.empty()) {
2082         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2083         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2084         for (Iter i=rect.begin(); i != rect.end(); ++i) {
2085             int x0 = (*i).get_x();
2086             int y0 = (*i).get_y();
2087             int x1 = x0 + (*i).get_width();
2088             int y1 = y0 + (*i).get_height();
2089             sp_canvas_paint_rect (canvas, x0, y0, x1, y1);
2090         }
2091     }
2093     return TRUE;
2096 /**
2097  * Helper that invokes update, paint, and repick on canvas.
2098  */
2099 static int
2100 do_update (SPCanvas *canvas)
2102     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2103         return TRUE;
2105     /* Cause the update if necessary */
2106     if (canvas->need_update) {
2107         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2108         canvas->need_update = FALSE;
2109     }
2111     /* Paint if able to */
2112     if (GTK_WIDGET_DRAWABLE (canvas)) {
2113             return paint (canvas);
2114     }
2116     /* Pick new current item */
2117     while (canvas->need_repick) {
2118         canvas->need_repick = FALSE;
2119         pick_current_item (canvas, &canvas->pick_event);
2120     }
2122     return TRUE;
2125 /**
2126  * Idle handler for the canvas that deals with pending updates and redraws.
2127  */
2128 static gint
2129 idle_handler (gpointer data)
2131     GDK_THREADS_ENTER ();
2133     SPCanvas *canvas = SP_CANVAS (data);
2135     const int ret = do_update (canvas);
2137     if (ret) {
2138         /* Reset idle id */
2139         canvas->idle_id = 0;
2140     }
2142     GDK_THREADS_LEAVE ();
2144     return !ret;
2147 /**
2148  * Convenience function to add an idle handler to a canvas.
2149  */
2150 static void
2151 add_idle (SPCanvas *canvas)
2153     if (canvas->idle_id != 0)
2154         return;
2156     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2159 /**
2160  * Returns the root group of the specified canvas.
2161  */
2162 SPCanvasGroup *
2163 sp_canvas_root (SPCanvas *canvas)
2165     g_return_val_if_fail (canvas != NULL, NULL);
2166     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2168     return SP_CANVAS_GROUP (canvas->root);
2171 /**
2172  * Scrolls canvas to specific position.
2173  */
2174 void
2175 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2177     g_return_if_fail (canvas != NULL);
2178     g_return_if_fail (SP_IS_CANVAS (canvas));
2180     int ix = (int) (cx + 0.5);
2181     int iy = (int) (cy + 0.5);
2182     int dx = ix - canvas->x0;
2183     int dy = iy - canvas->y0;
2185     canvas->dx0 = cx;
2186     canvas->dy0 = cy;
2187     canvas->x0 = ix;
2188     canvas->y0 = iy;
2190     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2192     if (!clear) {
2193         // scrolling without zoom; redraw only the newly exposed areas
2194         if ((dx != 0) || (dy != 0)) {
2195             canvas->is_scrolling = is_scrolling;
2196             if (GTK_WIDGET_REALIZED (canvas)) {
2197                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2198             }
2199         }
2200     } else {
2201         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2202     }
2204     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
2205     SPEventContext *ec = inkscape_active_event_context();
2206     if (SP_IS_3DBOX_CONTEXT (ec)) {
2207         // We could avoid redraw during panning by checking the status of is_scrolling, but this is
2208         // neither faster nor does it get rid of artefacts, so we update PLs unconditionally
2209         SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec);
2210         bc->_vpdrag->updateLines();
2211     }
2214 /** 
2215  * Updates canvas if necessary.
2216  */
2217 void
2218 sp_canvas_update_now (SPCanvas *canvas)
2220     g_return_if_fail (canvas != NULL);
2221     g_return_if_fail (SP_IS_CANVAS (canvas));
2223     if (!(canvas->need_update ||
2224           canvas->need_redraw))
2225         return;
2227     remove_idle (canvas);
2228     do_update (canvas);
2231 /**
2232  * Update callback for canvas widget.
2233  */
2234 static void
2235 sp_canvas_request_update (SPCanvas *canvas)
2237     canvas->need_update = TRUE;
2238     add_idle (canvas);
2241 /**
2242  * Forces redraw of rectangular canvas area.
2243  */
2244 void
2245 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2247     NRRectL bbox;
2248     NRRectL visible;
2249     NRRectL clip;
2251     g_return_if_fail (canvas != NULL);
2252     g_return_if_fail (SP_IS_CANVAS (canvas));
2254     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2255     if ((x0 >= x1) || (y0 >= y1)) return;
2257     bbox.x0 = x0;
2258     bbox.y0 = y0;
2259     bbox.x1 = x1;
2260     bbox.y1 = y1;
2262     visible.x0 = canvas->x0;
2263     visible.y0 = canvas->y0;
2264     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2265     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2267     nr_rect_l_intersect (&clip, &bbox, &visible);
2269     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2270     add_idle (canvas);
2273 /**
2274  * Sets world coordinates from win and canvas.
2275  */
2276 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2278     g_return_if_fail (canvas != NULL);
2279     g_return_if_fail (SP_IS_CANVAS (canvas));
2281     if (worldx) *worldx = canvas->x0 + winx;
2282     if (worldy) *worldy = canvas->y0 + winy;
2285 /**
2286  * Sets win coordinates from world and canvas.
2287  */
2288 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2290     g_return_if_fail (canvas != NULL);
2291     g_return_if_fail (SP_IS_CANVAS (canvas));
2293     if (winx) *winx = worldx - canvas->x0;
2294     if (winy) *winy = worldy - canvas->y0;
2297 /**
2298  * Converts point from win to world coordinates.
2299  */
2300 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2302     g_assert (canvas != NULL);
2303     g_assert (SP_IS_CANVAS (canvas));
2305     return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2308 /**
2309  * Converts point from world to win coordinates.
2310  */
2311 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2313     g_assert (canvas != NULL);
2314     g_assert (SP_IS_CANVAS (canvas));
2316     return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2319 /**
2320  * Returns true if point given in world coordinates is inside window.
2321  */
2322 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2324     g_assert( canvas != NULL );
2325     g_assert(SP_IS_CANVAS(canvas));
2327     using NR::X;
2328     using NR::Y;
2329     GtkWidget const &w = *GTK_WIDGET(canvas);
2330     return ( ( canvas->x0 <= world[X] )  &&
2331              ( canvas->y0 <= world[Y] )  &&
2332              ( world[X] < canvas->x0 + w.allocation.width )  &&
2333              ( world[Y] < canvas->y0 + w.allocation.height ) );
2336 /**
2337  * Return canvas window coordinates as NR::Rect.
2338  */
2339 NR::Rect SPCanvas::getViewbox() const
2341     GtkWidget const *w = GTK_WIDGET(this);
2343     return NR::Rect(NR::Point(dx0, dy0),
2344                     NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2347 inline int sp_canvas_tile_floor(int x)
2349     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2352 inline int sp_canvas_tile_ceil(int x)
2354     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2357 /**
2358  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2359  */
2360 void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2362     if ( nl >= nr || nt >= nb ) {
2363         if ( canvas->tiles ) g_free(canvas->tiles);
2364         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2365         canvas->tileH=canvas->tileV=0;
2366         canvas->tiles=NULL;
2367         return;
2368     }
2369     int tl=sp_canvas_tile_floor(nl);
2370     int tt=sp_canvas_tile_floor(nt);
2371     int tr=sp_canvas_tile_ceil(nr);
2372     int tb=sp_canvas_tile_ceil(nb);
2374     int nh = tr-tl, nv = tb-tt;
2375     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2376     for (int i=tl; i<tr; i++) {
2377         for (int j=tt; j<tb; j++) {
2378             int ind = (i-tl) + (j-tt)*nh;
2379             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2380                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2381             } else {
2382                 ntiles[ind]=0; // newly exposed areas get 0
2383             }
2384         }
2385     }
2386     if ( canvas->tiles ) g_free(canvas->tiles);
2387     canvas->tiles=ntiles;
2388     canvas->tLeft=tl;
2389     canvas->tTop=tt;
2390     canvas->tRight=tr;
2391     canvas->tBottom=tb;
2392     canvas->tileH=nh;
2393     canvas->tileV=nv;
2396 /**
2397  * Helper that marks specific canvas rectangle for redraw by dirtying its tiles
2398  */
2399 void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2401     if ( nl >= nr || nt >= nb ) {
2402         return;
2403     }
2404     int tl=sp_canvas_tile_floor(nl);
2405     int tt=sp_canvas_tile_floor(nt);
2406     int tr=sp_canvas_tile_ceil(nr);
2407     int tb=sp_canvas_tile_ceil(nb);
2408     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2409     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2410     if ( tr > canvas->tRight ) tr=canvas->tRight;
2411     if ( tt < canvas->tTop ) tt=canvas->tTop;
2412     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2414     canvas->need_redraw = TRUE;
2416     for (int i=tl; i<tr; i++) {
2417         for (int j=tt; j<tb; j++) {
2418             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = 1;
2419         }
2420     }
2424 /*
2425   Local Variables:
2426   mode:c++
2427   c-file-style:"stroustrup"
2428   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2429   indent-tabs-mode:nil
2430   fill-column:99
2431   End:
2432 */
2433 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :