Code

do not request redraw if an item has zero dimensions; also remove redundant redraw...
[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);
207 /**
208  * Helper function that requests redraw only if item's visible flag is set.
209  */
210 static void
211 redraw_if_visible (SPCanvasItem *item)
213     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
214         int x0 = (int)(item->x1);
215         int x1 = (int)(item->x2);
216         int y0 = (int)(item->y1);
217         int y1 = (int)(item->y2);
219         if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
220             sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
221         }
222     }
225 /**
226  * Callback that removes item from all referers and destroys it.
227  */
228 static void
229 sp_canvas_item_dispose (GObject *object)
231     SPCanvasItem *item = SP_CANVAS_ITEM (object);
233     redraw_if_visible (item);
234     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
236     if (item == item->canvas->current_item) {
237         item->canvas->current_item = NULL;
238         item->canvas->need_repick = TRUE;
239     }
241     if (item == item->canvas->new_current_item) {
242         item->canvas->new_current_item = NULL;
243         item->canvas->need_repick = TRUE;
244     }
246     if (item == item->canvas->grabbed_item) {
247         item->canvas->grabbed_item = NULL;
248         gdk_pointer_ungrab (GDK_CURRENT_TIME);
249     }
251     if (item == item->canvas->focused_item)
252         item->canvas->focused_item = NULL;
254     if (item->parent) {
255         group_remove (SP_CANVAS_GROUP (item->parent), item);
256     }
258     G_OBJECT_CLASS (item_parent_class)->dispose (object);
261 /**
262  * Helper function to update item and its children.
263  *
264  * NB! affine is parent2canvas.
265  */
266 static void
267 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
269     /* Apply the child item's transform */
270     NR::Matrix child_affine = item->xform * affine;
272     /* apply object flags to child flags */
273     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
275     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
276         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
278     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
279         child_flags |= SP_CANVAS_UPDATE_AFFINE;
281     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
282         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
283             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
284     }
286     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
287     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
290 /** 
291  * Helper function to invoke the point method of the item.  
292  *
293  * The argument x, y should be in the parent's item-relative coordinate 
294  * system.  This routine applies the inverse of the item's transform, 
295  * maintaining the affine invariant.
296  */
297 static double
298 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
300     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
301         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
303     return NR_HUGE;
306 /**
307  * Makes the item's affine transformation matrix be equal to the specified
308  * matrix.
309  * 
310  * @item: A canvas item.
311  * @affine: An affine transformation matrix.
312  */
313 void
314 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const& affine)
316     item->xform = affine;
318     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
319         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
320         if (item->parent != NULL) {
321             sp_canvas_item_request_update (item->parent);
322         } else {
323             sp_canvas_request_update (item->canvas);
324         }
325     }
327     item->canvas->need_repick = TRUE;
330 /**
331  * Convenience function to reorder items in a group's child list.  
332  *
333  * This puts the specified link after the "before" link.
334  */
335 static void
336 put_item_after (GList *link, GList *before)
338     if (link == before)
339         return;
341     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
343     if (before == NULL) {
344         if (link == parent->items) return;
346         link->prev->next = link->next;
348         if (link->next) {
349             link->next->prev = link->prev;
350         } else {
351             parent->last = link->prev;
352         }
354         link->prev = before;
355         link->next = parent->items;
356         link->next->prev = link;
357         parent->items = link;
358     } else {
359         if ((link == parent->last) && (before == parent->last->prev))
360             return;
362         if (link->next)
363             link->next->prev = link->prev;
365         if (link->prev)
366             link->prev->next = link->next;
367         else {
368             parent->items = link->next;
369             parent->items->prev = NULL;
370         }
372         link->prev = before;
373         link->next = before->next;
375         link->prev->next = link;
377         if (link->next)
378             link->next->prev = link;
379         else
380             parent->last = link;
381     }
385 /**
386  * Raises the item in its parent's stack by the specified number of positions.
387  * 
388  * \param item A canvas item.
389  * \param positions Number of steps to raise the item.
390  *
391  * If the number of positions is greater than the distance to the top of the
392  * stack, then the item is put at the top.
393  */
394 void
395 sp_canvas_item_raise (SPCanvasItem *item, int positions)
397     g_return_if_fail (item != NULL);
398     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
399     g_return_if_fail (positions >= 0);
401     if (!item->parent || positions == 0)
402         return;
404     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
405     GList *link = g_list_find (parent->items, item);
406     g_assert (link != NULL);
408     GList *before;
409     for (before = link; positions && before; positions--)
410         before = before->next;
412     if (!before)
413         before = parent->last;
415     put_item_after (link, before);
417     redraw_if_visible (item);
418     item->canvas->need_repick = TRUE;
422 /**
423  * Lowers the item in its parent's stack by the specified number of positions.
424  *
425  * \param item A canvas item.
426  * \param positions Number of steps to lower the item.
427  *
428  * If the number of positions is greater than the distance to the bottom of the
429  * stack, then the item is put at the bottom.
430  **/
431 void
432 sp_canvas_item_lower (SPCanvasItem *item, int positions)
434     g_return_if_fail (item != NULL);
435     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
436     g_return_if_fail (positions >= 1);
438     if (!item->parent || positions == 0)
439         return;
441     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
442     GList *link = g_list_find (parent->items, item);
443     g_assert (link != NULL);
445     GList *before;
446     if (link->prev)
447         for (before = link->prev; positions && before; positions--)
448             before = before->prev;
449     else
450         before = NULL;
452     put_item_after (link, before);
454     redraw_if_visible (item);
455     item->canvas->need_repick = TRUE;
458 /**
459  * Sets visible flag on item and requests a redraw.
460  */
461 void
462 sp_canvas_item_show (SPCanvasItem *item)
464     g_return_if_fail (item != NULL);
465     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
467     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
468         return;
470     item->flags |= SP_CANVAS_ITEM_VISIBLE;
472     int x0 = (int)(item->x1);
473     int x1 = (int)(item->x2);
474     int y0 = (int)(item->y1);
475     int y1 = (int)(item->y2);
477     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
478         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
479         item->canvas->need_repick = TRUE;
480     }
483 /**
484  * Clears visible flag on item and requests a redraw.
485  */
486 void
487 sp_canvas_item_hide (SPCanvasItem *item)
489     g_return_if_fail (item != NULL);
490     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
492     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
493         return;
495     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
497     int x0 = (int)(item->x1);
498     int x1 = (int)(item->x2);
499     int y0 = (int)(item->y1);
500     int y1 = (int)(item->y2);
502     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
503         sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
504         item->canvas->need_repick = TRUE;
505     }
508 /**
509  * Grab item under cursor.
510  * 
511  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
512  */
513 int
514 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
516     g_return_val_if_fail (item != NULL, -1);
517     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
518     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
520     if (item->canvas->grabbed_item)
521         return -1;
523     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
524         return -1;
526     /* fixme: Top hack (Lauris) */
527     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
528     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
529     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
530                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
531                       NULL, cursor, etime);
533     item->canvas->grabbed_item = item;
534     item->canvas->grabbed_event_mask = event_mask;
535     item->canvas->current_item = item; /* So that events go to the grabbed item */
537     return 0;
540 /**
541  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
542  * mouse.
543  * 
544  * \param item A canvas item that holds a grab.
545  * \param etime The timestamp for ungrabbing the mouse.
546  */
547 void
548 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
550     g_return_if_fail (item != NULL);
551     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
553     if (item->canvas->grabbed_item != item)
554         return;
556     item->canvas->grabbed_item = NULL;
558     gdk_pointer_ungrab (etime);
561 /**
562  * Returns the product of all transformation matrices from the root item down
563  * to the item.
564  */
565 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
567     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
569     NR::Matrix affine = NR::identity();
571     while (item) {
572         affine *= item->xform;
573         item = item->parent;
574     }
575     return affine;
578 /**
579  * Helper that returns true iff item is descendant of parent.
580  */
581 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
583     while (item) {
584         if (item == parent)
585             return true;
586         item = item->parent;
587     }
589     return false;
592 /**
593  * Focus canvas, and item under cursor if it is not already focussed.
594  */
595 void
596 sp_canvas_item_grab_focus (SPCanvasItem *item)
598     g_return_if_fail (item != NULL);
599     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
600     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
602     SPCanvasItem *focused_item = item->canvas->focused_item;
604     if (focused_item) {
605         GdkEvent ev;
606         ev.focus_change.type = GDK_FOCUS_CHANGE;
607         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
608         ev.focus_change.send_event = FALSE;
609         ev.focus_change.in = FALSE;
611         emit_event (item->canvas, &ev);
612     }
614     item->canvas->focused_item = item;
615     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
617     if (focused_item) {
618         GdkEvent ev;
619         ev.focus_change.type = GDK_FOCUS_CHANGE;
620         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
621         ev.focus_change.send_event = FALSE;
622         ev.focus_change.in = TRUE;
624         emit_event (item->canvas, &ev);
625     }
628 /**
629  * Requests that the canvas queue an update for the specified item.
630  * 
631  * To be used only by item implementations.
632  */
633 void
634 sp_canvas_item_request_update (SPCanvasItem *item)
636     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
637         return;
639     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
641     if (item->parent != NULL) {
642         /* Recurse up the tree */
643         sp_canvas_item_request_update (item->parent);
644     } else {
645         /* Have reached the top of the tree, make sure the update call gets scheduled. */
646         sp_canvas_request_update (item->canvas);
647     }
650 /**
651  * Returns position of item in group.
652  */
653 gint sp_canvas_item_order (SPCanvasItem * item)
655     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
658 /* SPCanvasGroup */
660 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
661 static void sp_canvas_group_init (SPCanvasGroup *group);
662 static void sp_canvas_group_destroy (GtkObject *object);
664 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
665 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
666 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
668 static SPCanvasItemClass *group_parent_class;
670 /**
671  * Registers SPCanvasGroup class with Gtk and returns its type number.
672  */
673 GtkType
674 sp_canvas_group_get_type (void)
676     static GtkType group_type = 0;
678     if (!group_type) {
679         static const GtkTypeInfo group_info = {
680             "SPCanvasGroup",
681             sizeof (SPCanvasGroup),
682             sizeof (SPCanvasGroupClass),
683             (GtkClassInitFunc) sp_canvas_group_class_init,
684             (GtkObjectInitFunc) sp_canvas_group_init,
685             NULL, NULL, NULL
686         };
688         group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
689     }
691     return group_type;
694 /**
695  * Class initialization function for SPCanvasGroupClass
696  */
697 static void
698 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
700     GtkObjectClass *object_class = (GtkObjectClass *) klass;
701     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
703     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
705     object_class->destroy = sp_canvas_group_destroy;
707     item_class->update = sp_canvas_group_update;
708     item_class->render = sp_canvas_group_render;
709     item_class->point = sp_canvas_group_point;
712 /**
713  * Callback. Empty.
714  */
715 static void
716 sp_canvas_group_init (SPCanvasGroup */*group*/)
718     /* Nothing here */
721 /**
722  * Callback that destroys all items in group and calls group's virtual 
723  * destroy() function.
724  */
725 static void
726 sp_canvas_group_destroy (GtkObject *object)
728     g_return_if_fail (object != NULL);
729     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
731     const SPCanvasGroup *group = SP_CANVAS_GROUP (object);
733     GList *list = group->items;
734     while (list) {
735         SPCanvasItem *child = (SPCanvasItem *)list->data;
736         list = list->next;
738         gtk_object_destroy (GTK_OBJECT (child));
739     }
741     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
742         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
745 /**
746  * Update handler for canvas groups
747  */
748 static void
749 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
751     const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
752     NR::ConvexHull corners(NR::Point(0, 0));
753     bool empty=true;
755     for (GList *list = group->items; list; list = list->next) {
756         SPCanvasItem *i = (SPCanvasItem *)list->data;
758         sp_canvas_item_invoke_update (i, affine, flags);
760         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
761             if (empty) {
762                 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
763                 empty = false;
764             } else {
765                 corners.add(NR::Point(i->x1, i->y1));
766             }
767             corners.add(NR::Point(i->x2, i->y2));
768         }
769     }
771     NR::Maybe<NR::Rect> const bounds = corners.bounds();
772     if (bounds) {
773         item->x1 = bounds->min()[NR::X];
774         item->y1 = bounds->min()[NR::Y];
775         item->x2 = bounds->max()[NR::X];
776         item->y2 = bounds->max()[NR::Y];
777     } else {
778         // FIXME ?
779         item->x1 = item->x2 = item->y1 = item->y2 = 0;
780     }
783 /**
784  * Point handler for canvas groups.
785  */
786 static double
787 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
789     const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
790     const double x = p[NR::X];
791     const double y = p[NR::Y];
792     int x1 = (int)(x - item->canvas->close_enough);
793     int y1 = (int)(y - item->canvas->close_enough);
794     int x2 = (int)(x + item->canvas->close_enough);
795     int y2 = (int)(y + item->canvas->close_enough);
797     double best = 0.0;
798     *actual_item = NULL;
800     double dist = 0.0;
802     for (GList *list = group->items; list; list = list->next) {
803         SPCanvasItem *child = (SPCanvasItem *)list->data;
805         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
806             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
808             int has_point;
809             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
810                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
811                 has_point = TRUE;
812             } else
813                 has_point = FALSE;
815             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
816                 best = dist;
817                 *actual_item = point_item;
818             }
819         }
820     }
822     return best;
825 /**
826  * Renders all visible canvas group items in buf rectangle.
827  */
828 static void
829 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
831     const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
833     for (GList *list = group->items; list; list = list->next) {
834         SPCanvasItem *child = (SPCanvasItem *)list->data;
835         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
836             if ((child->x1 < buf->rect.x1) &&
837                 (child->y1 < buf->rect.y1) &&
838                 (child->x2 > buf->rect.x0) &&
839                 (child->y2 > buf->rect.y0)) {
840                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
841                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
842             }
843         }
844     }
847 /**
848  * Adds an item to a canvas group.
849  */
850 static void
851 group_add (SPCanvasGroup *group, SPCanvasItem *item)
853     gtk_object_ref (GTK_OBJECT (item));
854     gtk_object_sink (GTK_OBJECT (item));
856     if (!group->items) {
857         group->items = g_list_append (group->items, item);
858         group->last = group->items;
859     } else {
860         group->last = g_list_append (group->last, item)->next;
861     }
863     sp_canvas_item_request_update (item);
866 /** 
867  * Removes an item from a canvas group
868  */
869 static void
870 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
872     g_return_if_fail (group != NULL);
873     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
874     g_return_if_fail (item != NULL);
876     for (GList *children = group->items; children; children = children->next) {
877         if (children->data == item) {
879             /* Unparent the child */
881             item->parent = NULL;
882             gtk_object_unref (GTK_OBJECT (item));
884             /* Remove it from the list */
886             if (children == group->last) group->last = children->prev;
888             group->items = g_list_remove_link (group->items, children);
889             g_list_free (children);
890             break;
891         }
892     }
895 /* SPCanvas */
897 static void sp_canvas_class_init (SPCanvasClass *klass);
898 static void sp_canvas_init (SPCanvas *canvas);
899 static void sp_canvas_destroy (GtkObject *object);
901 static void sp_canvas_realize (GtkWidget *widget);
902 static void sp_canvas_unrealize (GtkWidget *widget);
904 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
905 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
907 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
908 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
909 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
910 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
911 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
912 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
913 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
914 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
916 static GtkWidgetClass *canvas_parent_class;
918 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
919 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
920 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
921 static int do_update (SPCanvas *canvas);
923 /**
924  * Registers the SPCanvas class if necessary, and returns the type ID
925  * associated to it.
926  *
927  * \return The type ID of the SPCanvas class.
928  **/
929 GtkType
930 sp_canvas_get_type (void)
932     static GtkType canvas_type = 0;
934     if (!canvas_type) {
935         static const GtkTypeInfo canvas_info = {
936             "SPCanvas",
937             sizeof (SPCanvas),
938             sizeof (SPCanvasClass),
939             (GtkClassInitFunc) sp_canvas_class_init,
940             (GtkObjectInitFunc) sp_canvas_init,
941             NULL, NULL, NULL
942         };
944         canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
945     }
947     return canvas_type;
950 /**
951  * Class initialization function for SPCanvasClass.
952  */
953 static void
954 sp_canvas_class_init (SPCanvasClass *klass)
956     GtkObjectClass *object_class = (GtkObjectClass *) klass;
957     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
959     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
961     object_class->destroy = sp_canvas_destroy;
963     widget_class->realize = sp_canvas_realize;
964     widget_class->unrealize = sp_canvas_unrealize;
965     widget_class->size_request = sp_canvas_size_request;
966     widget_class->size_allocate = sp_canvas_size_allocate;
967     widget_class->button_press_event = sp_canvas_button;
968     widget_class->button_release_event = sp_canvas_button;
969     widget_class->motion_notify_event = sp_canvas_motion;
970     widget_class->scroll_event = sp_canvas_scroll;
971     widget_class->expose_event = sp_canvas_expose;
972     widget_class->key_press_event = sp_canvas_key;
973     widget_class->key_release_event = sp_canvas_key;
974     widget_class->enter_notify_event = sp_canvas_crossing;
975     widget_class->leave_notify_event = sp_canvas_crossing;
976     widget_class->focus_in_event = sp_canvas_focus_in;
977     widget_class->focus_out_event = sp_canvas_focus_out;
980 /** 
981  * Callback: object initialization for SPCanvas.
982  */
983 static void
984 sp_canvas_init (SPCanvas *canvas)
986     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
987     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
988     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
990     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
991     canvas->pick_event.crossing.x = 0;
992     canvas->pick_event.crossing.y = 0;
994     /* Create the root item as a special case */
995     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
996     canvas->root->canvas = canvas;
998     gtk_object_ref (GTK_OBJECT (canvas->root));
999     gtk_object_sink (GTK_OBJECT (canvas->root));
1001     canvas->need_repick = TRUE;
1003     // See comment at in sp-canvas.h.
1004     canvas->gen_all_enter_events = false;
1006     canvas->tiles=NULL;
1007     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1008     canvas->tileH=canvas->tileV=0;
1010     canvas->forced_redraw_count = 0;
1011     canvas->forced_redraw_limit = -1;
1013     canvas->is_scrolling = false;
1017 /**
1018  * Convenience function to remove the idle handler of a canvas.
1019  */
1020 static void
1021 remove_idle (SPCanvas *canvas)
1023     if (canvas->idle_id) {
1024         gtk_idle_remove (canvas->idle_id);
1025         canvas->idle_id = 0;
1026     }
1029 /*
1030  * Removes the transient state of the canvas (idle handler, grabs).
1031  */
1032 static void
1033 shutdown_transients (SPCanvas *canvas)
1035     /* We turn off the need_redraw flag, since if the canvas is mapped again
1036      * it will request a redraw anyways.  We do not turn off the need_update
1037      * flag, though, because updates are not queued when the canvas remaps
1038      * itself.
1039      */
1040     if (canvas->need_redraw) {
1041         canvas->need_redraw = FALSE;
1042     }
1043     if ( canvas->tiles ) g_free(canvas->tiles);
1044     canvas->tiles=NULL;
1045     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1046     canvas->tileH=canvas->tileV=0;
1048     if (canvas->grabbed_item) {
1049         canvas->grabbed_item = NULL;
1050         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1051     }
1053     remove_idle (canvas);
1056 /**
1057  * Destroy handler for SPCanvas.
1058  */
1059 static void
1060 sp_canvas_destroy (GtkObject *object)
1062     SPCanvas *canvas = SP_CANVAS (object);
1064     if (canvas->root) {
1065         gtk_object_unref (GTK_OBJECT (canvas->root));
1066         canvas->root = NULL;
1067     }
1069     shutdown_transients (canvas);
1071     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1072         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1075 /**
1076  * Returns new canvas as widget.
1077  */
1078 GtkWidget *
1079 sp_canvas_new_aa (void)
1081     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1083     return (GtkWidget *) canvas;
1086 /**
1087  * The canvas widget's realize callback.
1088  */
1089 static void
1090 sp_canvas_realize (GtkWidget *widget)
1092     SPCanvas *canvas = SP_CANVAS (widget);
1094     GdkWindowAttr attributes;
1095     attributes.window_type = GDK_WINDOW_CHILD;
1096     attributes.x = widget->allocation.x;
1097     attributes.y = widget->allocation.y;
1098     attributes.width = widget->allocation.width;
1099     attributes.height = widget->allocation.height;
1100     attributes.wclass = GDK_INPUT_OUTPUT;
1101     attributes.visual = gdk_rgb_get_visual ();
1102     attributes.colormap = gdk_rgb_get_cmap ();
1103     attributes.event_mask = (gtk_widget_get_events (widget) |
1104                              GDK_EXPOSURE_MASK |
1105                              GDK_BUTTON_PRESS_MASK |
1106                              GDK_BUTTON_RELEASE_MASK |
1107                              GDK_POINTER_MOTION_MASK |
1108                              GDK_PROXIMITY_IN_MASK |
1109                              GDK_PROXIMITY_OUT_MASK |
1110                              GDK_KEY_PRESS_MASK |
1111                              GDK_KEY_RELEASE_MASK |
1112                              GDK_ENTER_NOTIFY_MASK |
1113                              GDK_LEAVE_NOTIFY_MASK |
1114                              GDK_FOCUS_CHANGE_MASK);
1115     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1117     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1118     gdk_window_set_user_data (widget->window, widget);
1120     if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1121         gtk_widget_set_events(widget, attributes.event_mask);
1123     widget->style = gtk_style_attach (widget->style, widget->window);
1125     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1127     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1130 /**
1131  * The canvas widget's unrealize callback.
1132  */
1133 static void
1134 sp_canvas_unrealize (GtkWidget *widget)
1136     SPCanvas *canvas = SP_CANVAS (widget);
1138     shutdown_transients (canvas);
1140     gdk_gc_destroy (canvas->pixmap_gc);
1141     canvas->pixmap_gc = NULL;
1143     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1144         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1147 /**
1148  * The canvas widget's size_request callback.
1149  */
1150 static void
1151 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1153     static_cast<void>(SP_CANVAS (widget));
1155     req->width = 256;
1156     req->height = 256;
1159 /**
1160  * The canvas widget's size_allocate callback.
1161  */
1162 static void
1163 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1165     SPCanvas *canvas = SP_CANVAS (widget);
1167     /* Schedule redraw of new region */
1168     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1169     if (allocation->width > widget->allocation.width) {
1170         sp_canvas_request_redraw (canvas,
1171                                   canvas->x0 + widget->allocation.width,
1172                                   0,
1173                                   canvas->x0 + allocation->width,
1174                                   canvas->y0 + allocation->height);
1175     }
1176     if (allocation->height > widget->allocation.height) {
1177         sp_canvas_request_redraw (canvas,
1178                                   0,
1179                                   canvas->y0 + widget->allocation.height,
1180                                   canvas->x0 + allocation->width,
1181                                   canvas->y0 + allocation->height);
1182     }
1184     widget->allocation = *allocation;
1186     if (GTK_WIDGET_REALIZED (widget)) {
1187         gdk_window_move_resize (widget->window,
1188                                 widget->allocation.x, widget->allocation.y,
1189                                 widget->allocation.width, widget->allocation.height);
1190     }
1193 /**
1194  * Helper that emits an event for an item in the canvas, be it the current 
1195  * item, grabbed item, or focused item, as appropriate.
1196  */
1197 static int
1198 emit_event (SPCanvas *canvas, GdkEvent *event)
1200     guint mask;
1202     if (canvas->grabbed_item) {
1203         switch (event->type) {
1204         case GDK_ENTER_NOTIFY:
1205             mask = GDK_ENTER_NOTIFY_MASK;
1206             break;
1207         case GDK_LEAVE_NOTIFY:
1208             mask = GDK_LEAVE_NOTIFY_MASK;
1209             break;
1210         case GDK_MOTION_NOTIFY:
1211             mask = GDK_POINTER_MOTION_MASK;
1212             break;
1213         case GDK_BUTTON_PRESS:
1214         case GDK_2BUTTON_PRESS:
1215         case GDK_3BUTTON_PRESS:
1216             mask = GDK_BUTTON_PRESS_MASK;
1217             break;
1218         case GDK_BUTTON_RELEASE:
1219             mask = GDK_BUTTON_RELEASE_MASK;
1220             break;
1221         case GDK_KEY_PRESS:
1222             mask = GDK_KEY_PRESS_MASK;
1223             break;
1224         case GDK_KEY_RELEASE:
1225             mask = GDK_KEY_RELEASE_MASK;
1226             break;
1227         case GDK_SCROLL:
1228             mask = GDK_SCROLL;
1229             break;
1230         default:
1231             mask = 0;
1232             break;
1233         }
1235         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1236     }
1238     /* Convert to world coordinates -- we have two cases because of diferent
1239      * offsets of the fields in the event structures.
1240      */
1242     GdkEvent ev = *event;
1244     switch (ev.type) {
1245     case GDK_ENTER_NOTIFY:
1246     case GDK_LEAVE_NOTIFY:
1247         ev.crossing.x += canvas->x0;
1248         ev.crossing.y += canvas->y0;
1249         break;
1250     case GDK_MOTION_NOTIFY:
1251     case GDK_BUTTON_PRESS:
1252     case GDK_2BUTTON_PRESS:
1253     case GDK_3BUTTON_PRESS:
1254     case GDK_BUTTON_RELEASE:
1255         ev.motion.x += canvas->x0;
1256         ev.motion.y += canvas->y0;
1257         break;
1258     default:
1259         break;
1260     }
1262     /* Choose where we send the event */
1264     /* canvas->current_item becomes NULL in some cases under Win32
1265     ** (e.g. if the pointer leaves the window).  So this is a hack that
1266     ** Lauris applied to SP to get around the problem.
1267     */
1268     SPCanvasItem* item = NULL;
1269     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1270         item = canvas->grabbed_item;
1271     } else {
1272         item = canvas->current_item;
1273     }
1275     if (canvas->focused_item &&
1276         ((event->type == GDK_KEY_PRESS) ||
1277          (event->type == GDK_KEY_RELEASE) ||
1278          (event->type == GDK_FOCUS_CHANGE))) {
1279         item = canvas->focused_item;
1280     }
1282     /* The event is propagated up the hierarchy (for if someone connected to
1283      * a group instead of a leaf event), and emission is stopped if a
1284      * handler returns TRUE, just like for GtkWidget events.
1285      */
1287     gint finished = FALSE;
1289     while (item && !finished) {
1290         gtk_object_ref (GTK_OBJECT (item));
1291         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1292         SPCanvasItem *parent = item->parent;
1293         gtk_object_unref (GTK_OBJECT (item));
1294         item = parent;
1295     }
1297     return finished;
1300 /**
1301  * Helper that re-picks the current item in the canvas, based on the event's 
1302  * coordinates and emits enter/leave events for items as appropriate.
1303  */
1304 static int
1305 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1307     int button_down = 0;
1308     double x, y;
1310     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1311         return FALSE;
1313     int retval = FALSE;
1315     if (canvas->gen_all_enter_events == false) {
1316         // If a button is down, we'll perform enter and leave events on the
1317         // current item, but not enter on any other item.  This is more or
1318         // less like X pointer grabbing for canvas items.
1319         //
1320         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1321                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1323         if (!button_down) canvas->left_grabbed_item = FALSE;
1324     }
1326     /* Save the event in the canvas.  This is used to synthesize enter and
1327      * leave events in case the current item changes.  It is also used to
1328      * re-pick the current item if the current one gets deleted.  Also,
1329      * synthesize an enter event.
1330      */
1331     if (event != &canvas->pick_event) {
1332         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1333             /* these fields have the same offsets in both types of events */
1335             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1336             canvas->pick_event.crossing.window     = event->motion.window;
1337             canvas->pick_event.crossing.send_event = event->motion.send_event;
1338             canvas->pick_event.crossing.subwindow  = NULL;
1339             canvas->pick_event.crossing.x          = event->motion.x;
1340             canvas->pick_event.crossing.y          = event->motion.y;
1341             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1342             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1343             canvas->pick_event.crossing.focus      = FALSE;
1344             canvas->pick_event.crossing.state      = event->motion.state;
1346             /* these fields don't have the same offsets in both types of events */
1348             if (event->type == GDK_MOTION_NOTIFY) {
1349                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1350                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1351             } else {
1352                 canvas->pick_event.crossing.x_root = event->button.x_root;
1353                 canvas->pick_event.crossing.y_root = event->button.y_root;
1354             }
1355         } else {
1356             canvas->pick_event = *event;
1357         }
1358     }
1360     /* Don't do anything else if this is a recursive call */
1361     if (canvas->in_repick) return retval;
1363     /* LeaveNotify means that there is no current item, so we don't look for one */
1364     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1365         /* these fields don't have the same offsets in both types of events */
1367         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1368             x = canvas->pick_event.crossing.x;
1369             y = canvas->pick_event.crossing.y;
1370         } else {
1371             x = canvas->pick_event.motion.x;
1372             y = canvas->pick_event.motion.y;
1373         }
1375         /* world coords */
1376         x += canvas->x0;
1377         y += canvas->y0;
1379         /* find the closest item */
1380         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1381             sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1382         } else {
1383             canvas->new_current_item = NULL;
1384         }
1385     } else {
1386         canvas->new_current_item = NULL;
1387     }
1389     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1390         return retval; /* current item did not change */
1391     }
1393     /* Synthesize events for old and new current items */
1395     if ((canvas->new_current_item != canvas->current_item)
1396         && (canvas->current_item != NULL)
1397         && !canvas->left_grabbed_item) {
1398         GdkEvent new_event;
1399         SPCanvasItem *item;
1401         item = canvas->current_item;
1403         new_event = canvas->pick_event;
1404         new_event.type = GDK_LEAVE_NOTIFY;
1406         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1407         new_event.crossing.subwindow = NULL;
1408         canvas->in_repick = TRUE;
1409         retval = emit_event (canvas, &new_event);
1410         canvas->in_repick = FALSE;
1411     }
1413     if (canvas->gen_all_enter_events == false) {
1414         // new_current_item may have been set to NULL during the call to
1415         // emit_event() above
1416         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1417             canvas->left_grabbed_item = TRUE;
1418             return retval;
1419         }
1420     }
1422     /* Handle the rest of cases */
1424     canvas->left_grabbed_item = FALSE;
1425     canvas->current_item = canvas->new_current_item;
1427     if (canvas->current_item != NULL) {
1428         GdkEvent new_event;
1430         new_event = canvas->pick_event;
1431         new_event.type = GDK_ENTER_NOTIFY;
1432         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1433         new_event.crossing.subwindow = NULL;
1434         retval = emit_event (canvas, &new_event);
1435     }
1437     return retval;
1440 /**
1441  * Button event handler for the canvas.
1442  */
1443 static gint
1444 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1446     SPCanvas *canvas = SP_CANVAS (widget);
1448     int retval = FALSE;
1450     /* dispatch normally regardless of the event's window if an item has
1451        has a pointer grab in effect */
1452     if (!canvas->grabbed_item && 
1453         event->window != SP_CANVAS_WINDOW (canvas))
1454         return retval;
1456     int mask;
1457     switch (event->button) {
1458     case 1:
1459         mask = GDK_BUTTON1_MASK;
1460         break;
1461     case 2:
1462         mask = GDK_BUTTON2_MASK;
1463         break;
1464     case 3:
1465         mask = GDK_BUTTON3_MASK;
1466         break;
1467     case 4:
1468         mask = GDK_BUTTON4_MASK;
1469         break;
1470     case 5:
1471         mask = GDK_BUTTON5_MASK;
1472         break;
1473     default:
1474         mask = 0;
1475     }
1477     switch (event->type) {
1478     case GDK_BUTTON_PRESS:
1479     case GDK_2BUTTON_PRESS:
1480     case GDK_3BUTTON_PRESS:
1481         /* Pick the current item as if the button were not pressed, and
1482          * then process the event.
1483          */
1484         canvas->state = event->state;
1485         pick_current_item (canvas, (GdkEvent *) event);
1486         canvas->state ^= mask;
1487         retval = emit_event (canvas, (GdkEvent *) event);
1488         break;
1490     case GDK_BUTTON_RELEASE:
1491         /* Process the event as if the button were pressed, then repick
1492          * after the button has been released
1493          */
1494         canvas->state = event->state;
1495         retval = emit_event (canvas, (GdkEvent *) event);
1496         event->state ^= mask;
1497         canvas->state = event->state;
1498         pick_current_item (canvas, (GdkEvent *) event);
1499         event->state ^= mask;
1500         break;
1502     default:
1503         g_assert_not_reached ();
1504     }
1506     return retval;
1509 /**
1510  * Scroll event handler for the canvas.
1511  *
1512  * \todo FIXME: generate motion events to re-select items.
1513  */
1514 static gint
1515 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1517     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1520 /**
1521  * Motion event handler for the canvas.
1522  */
1523 static int
1524 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1526     SPCanvas *canvas = SP_CANVAS (widget);
1528     if (event->window != SP_CANVAS_WINDOW (canvas))
1529         return FALSE;
1531     if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1532         gint x, y;
1533         gdk_window_get_pointer (widget->window, &x, &y, NULL);
1534         event->x = x;
1535         event->y = y;
1536     }
1538     canvas->state = event->state;
1539     pick_current_item (canvas, (GdkEvent *) event);
1541     return emit_event (canvas, (GdkEvent *) event);
1544 static void
1545 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)
1547     GtkWidget *widget = GTK_WIDGET (canvas);
1549     SPCanvasBuf buf;
1550     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1551         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1552     } else {
1553         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1554     }
1556     // Mark the region clean
1557     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1559     buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1560     buf.rect.x0 = x0;
1561     buf.rect.y0 = y0;
1562     buf.rect.x1 = x1;
1563     buf.rect.y1 = y1;
1564     buf.visible_rect.x0 = draw_x1;
1565     buf.visible_rect.y0 = draw_y1;
1566     buf.visible_rect.x1 = draw_x2;
1567     buf.visible_rect.y1 = draw_y2;
1568     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1569     buf.bg_color = (((color->red & 0xff00) << 8)
1570                     | (color->green & 0xff00)
1571                     | (color->blue >> 8));
1572     buf.is_empty = true;
1573       
1574     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1575         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1576     }
1577       
1578     if (buf.is_empty) {
1579         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1580         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1581                             canvas->pixmap_gc,
1582                             TRUE,
1583                             x0 - canvas->x0, y0 - canvas->y0,
1584                             x1 - x0, y1 - y0);
1585     } else {
1586 /*
1587 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1588 // Why this must not be done currently:
1589 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1590 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)  
1591 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels, 
1592 // we need more bufs to paint a given area and as a result it's even a bit slower
1594     cairo_surface_t* cst = cairo_image_surface_create_for_data (
1595         buf.buf,
1596         CAIRO_FORMAT_RGB24,  // unpacked, i.e. 32 bits! one byte is unused
1597         x1 - x0, y1 - y0,
1598         buf.buf_rowstride
1599         );
1600         cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1601         cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1602         cairo_paint (ct);
1603     cairo_destroy (ct);
1604     cairo_surface_finish (cst);
1605     cairo_surface_destroy (cst);
1606 */
1608         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1609                                       canvas->pixmap_gc,
1610                                       x0 - canvas->x0, y0 - canvas->y0,
1611                                       x1 - x0, y1 - y0,
1612                                       GDK_RGB_DITHER_MAX,
1613                                       buf.buf,
1614                                       sw * 3,
1615                                       x0 - canvas->x0, y0 - canvas->y0);
1616     }
1618     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1619         nr_pixelstore_256K_free (buf.buf);
1620     } else {
1621         nr_pixelstore_1M_free (buf.buf);
1622     }
1625 struct PaintRectSetup {
1626     SPCanvas* canvas;
1627     NRRectL big_rect;
1628     GTimeVal start_time;
1629     int max_pixels;
1630     NR::Point mouse_loc;
1631 };
1633 /**
1634  * Paint the given rect, recursively subdividing the region until it is the size of a single
1635  * buffer.
1636  *
1637  * @return true if the drawing completes
1638  */
1639 static int
1640 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1642     GTimeVal now;
1643     g_get_current_time (&now);
1645     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1646         + (now.tv_usec - setup->start_time.tv_usec);
1648     // Allow only very fast buffers to be run together;
1649     // as soon as the total redraw time exceeds 1ms, cancel;
1650     // this returns control to the idle loop and allows Inkscape to process user input
1651     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1652     // it will get back and finish painting what remains to paint.
1653     if (elapsed > 1000) {
1655         // Interrupting redraw isn't always good.
1656         // For example, when you drag one node of a big path, only the buffer containing
1657         // the mouse cursor will be redrawn again and again, and the rest of the path
1658         // will remain stale because Inkscape never has enough idle time to redraw all 
1659         // of the screen. To work around this, such operations set a forced_redraw_limit > 0. 
1660         // If this limit is set, and if we have aborted redraw more times than is allowed,
1661         // interrupting is blocked and we're forced to redraw full screen once 
1662         // (after which we can again interrupt forced_redraw_limit times).
1663         if (setup->canvas->forced_redraw_limit < 0 || 
1664             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1666             if (setup->canvas->forced_redraw_limit != -1) {
1667                 setup->canvas->forced_redraw_count++;
1668             }
1670             return false;
1671         }
1672     }
1674     // Find the optimal buffer dimensions
1675     int bw = this_rect.x1 - this_rect.x0;
1676     int bh = this_rect.y1 - this_rect.y0;
1677     if ((bw < 1) || (bh < 1))
1678         return 0;
1680     if (bw * bh < setup->max_pixels) {
1681         // We are small enough
1682         sp_canvas_paint_single_buffer (setup->canvas,
1683                                        this_rect.x0, this_rect.y0,
1684                                        this_rect.x1, this_rect.y1,
1685                                        setup->big_rect.x0, setup->big_rect.y0,
1686                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1687         return 1;
1688     }
1690     NRRectL lo = this_rect;
1691     NRRectL hi = this_rect;
1693 /* 
1694 This test determines the redraw strategy: 
1696 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1697 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1698 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1699 and seems to be faster for drawings with many smaller objects at zoom-out.
1701 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1702 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1703 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1704 faster.
1706 The default for now is the strips mode.
1707 */
1708     if (bw < bh) {
1709         int mid = (this_rect.x0 + this_rect.x1) / 2;
1710         // Make sure that mid lies on a tile boundary
1711         mid = (mid / TILE_SIZE) * TILE_SIZE;
1713         lo.x1 = mid;
1714         hi.x0 = mid;
1716         if (setup->mouse_loc[NR::X] < mid) {
1717             // Always paint towards the mouse first
1718             return sp_canvas_paint_rect_internal(setup, lo)
1719                 && sp_canvas_paint_rect_internal(setup, hi);
1720         } else {
1721             return sp_canvas_paint_rect_internal(setup, hi)
1722                 && sp_canvas_paint_rect_internal(setup, lo);
1723         }
1724     } else {
1725         int mid = (this_rect.y0 + this_rect.y1) / 2;
1726         // Make sure that mid lies on a tile boundary
1727         mid = (mid / TILE_SIZE) * TILE_SIZE;
1729         lo.y1 = mid;
1730         hi.y0 = mid;
1732         if (setup->mouse_loc[NR::Y] < mid) {
1733             // Always paint towards the mouse first
1734             return sp_canvas_paint_rect_internal(setup, lo)
1735                 && sp_canvas_paint_rect_internal(setup, hi);
1736         } else {
1737             return sp_canvas_paint_rect_internal(setup, hi)
1738                 && sp_canvas_paint_rect_internal(setup, lo);
1739         }
1740     }
1744 /**
1745  * Helper that draws a specific rectangular part of the canvas.
1746  *
1747  * @return true if the rectangle painting succeeds.
1748  */
1749 static bool
1750 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1752     g_return_val_if_fail (!canvas->need_update, false);
1753  
1754     NRRectL rect;
1755     rect.x0 = xx0;
1756     rect.x1 = xx1;
1757     rect.y0 = yy0;
1758     rect.y1 = yy1;
1760     // Clip rect-to-draw by the current visible area
1761     rect.x0 = MAX (rect.x0, canvas->x0);
1762     rect.y0 = MAX (rect.y0, canvas->y0);
1763     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1764     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1766 #ifdef DEBUG_REDRAW
1767     // paint the area to redraw yellow
1768     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1769     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1770                             canvas->pixmap_gc,
1771                             TRUE,
1772                             rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1773                                               rect.x1 - rect.x0, rect.y1 - rect.y0);
1774 #endif                      
1776     PaintRectSetup setup;
1778     setup.canvas = canvas;
1779     setup.big_rect = rect;
1781     // Save the mouse location
1782     gint x, y;
1783     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1784     setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1786     // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1787     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1788         // use 256K as a compromise to not slow down gradients
1789         // 256K is the cached buffer and we need 3 channels
1790         setup.max_pixels = 87381; // 256K/3
1791     } else {
1792         // paths only, so 1M works faster
1793         // 1M is the cached buffer and we need 3 channels
1794         setup.max_pixels = 349525;
1795     }
1797     // Start the clock
1798     g_get_current_time(&(setup.start_time));
1800     // Go
1801     return sp_canvas_paint_rect_internal(&setup, rect);
1804 /**
1805  * Force a full redraw after a specified number of interrupted redraws
1806  */
1807 void
1808 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1809   g_return_if_fail(canvas != NULL);
1810   
1811   canvas->forced_redraw_limit = count;
1812   canvas->forced_redraw_count = 0;
1815 /**
1816  * End forced full redraw requests
1817  */
1818 void
1819 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1820   g_return_if_fail(canvas != NULL);
1822   canvas->forced_redraw_limit = -1;
1825 /**
1826  * The canvas widget's expose callback.
1827  */
1828 static gint
1829 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1831     SPCanvas *canvas = SP_CANVAS (widget);
1833     if (!GTK_WIDGET_DRAWABLE (widget) || 
1834         (event->window != SP_CANVAS_WINDOW (canvas)))
1835         return FALSE;
1837     int n_rects;
1838     GdkRectangle *rects;
1839     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1841     for (int i = 0; i < n_rects; i++) {
1842         NRRectL rect;
1843                 
1844         rect.x0 = rects[i].x + canvas->x0;
1845         rect.y0 = rects[i].y + canvas->y0;
1846         rect.x1 = rect.x0 + rects[i].width;
1847         rect.y1 = rect.y0 + rects[i].height;
1849         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1850     }
1852     if (n_rects > 0)
1853         g_free (rects);
1855     return FALSE;
1858 /**
1859  * The canvas widget's keypress callback.
1860  */
1861 static gint
1862 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1864     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1867 /**
1868  * Crossing event handler for the canvas.
1869  */
1870 static gint
1871 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1873     SPCanvas *canvas = SP_CANVAS (widget);
1875     if (event->window != SP_CANVAS_WINDOW (canvas))
1876         return FALSE;
1878     canvas->state = event->state;
1879     return pick_current_item (canvas, (GdkEvent *) event);
1882 /**
1883  * Focus in handler for the canvas.
1884  */
1885 static gint
1886 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1888     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1890     SPCanvas *canvas = SP_CANVAS (widget);
1892     if (canvas->focused_item) {
1893         return emit_event (canvas, (GdkEvent *) event);
1894     } else {
1895         return FALSE;
1896     }
1899 /**
1900  * Focus out handler for the canvas.
1901  */
1902 static gint
1903 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1905     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1907     SPCanvas *canvas = SP_CANVAS (widget);
1909     if (canvas->focused_item)
1910         return emit_event (canvas, (GdkEvent *) event);
1911     else
1912         return FALSE;
1915 /**
1916  * Helper that repaints the areas in the canvas that need it.
1917  *
1918  * @return true if all the dirty parts have been redrawn
1919  */
1920 static int
1921 paint (SPCanvas *canvas)
1923     if (canvas->need_update) {
1924         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1925         canvas->need_update = FALSE;
1926     }
1928     if (!canvas->need_redraw)
1929         return TRUE;
1931     Gdk::Region to_paint;
1933     for (int j=canvas->tTop; j<canvas->tBottom; j++) { 
1934         for (int i=canvas->tLeft; i<canvas->tRight; i++) { 
1935             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
1937             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
1938                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
1939                                    TILE_SIZE, TILE_SIZE));
1940             }
1942         }
1943     }
1945     if (!to_paint.empty()) {
1946         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
1947         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
1948         for (Iter i=rect.begin(); i != rect.end(); ++i) {
1949             int x0 = (*i).get_x();
1950             int y0 = (*i).get_y();
1951             int x1 = x0 + (*i).get_width();
1952             int y1 = y0 + (*i).get_height();
1953             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
1954                 // Aborted
1955                 return FALSE;
1956             };
1957         }
1958     }
1960     canvas->need_redraw = FALSE;
1962     // we've had a full unaborted redraw, reset the full redraw counter
1963     if (canvas->forced_redraw_limit != -1) {
1964         canvas->forced_redraw_count = 0;
1965     }
1967     return TRUE;
1970 /**
1971  * Helper that invokes update, paint, and repick on canvas.
1972  */
1973 static int
1974 do_update (SPCanvas *canvas)
1976     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1977         return TRUE;
1979     /* Cause the update if necessary */
1980     if (canvas->need_update) {
1981         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1982         canvas->need_update = FALSE;
1983     }
1985     /* Paint if able to */
1986     if (GTK_WIDGET_DRAWABLE (canvas)) {
1987             return paint (canvas);
1988     }
1990     /* Pick new current item */
1991     while (canvas->need_repick) {
1992         canvas->need_repick = FALSE;
1993         pick_current_item (canvas, &canvas->pick_event);
1994     }
1996     return TRUE;
1999 /**
2000  * Idle handler for the canvas that deals with pending updates and redraws.
2001  */
2002 static gint
2003 idle_handler (gpointer data)
2005     GDK_THREADS_ENTER ();
2007     SPCanvas *canvas = SP_CANVAS (data);
2009     const int ret = do_update (canvas);
2011     if (ret) {
2012         /* Reset idle id */
2013         canvas->idle_id = 0;
2014     }
2016     GDK_THREADS_LEAVE ();
2018     return !ret;
2021 /**
2022  * Convenience function to add an idle handler to a canvas.
2023  */
2024 static void
2025 add_idle (SPCanvas *canvas)
2027     if (canvas->idle_id != 0)
2028         return;
2030     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2033 /**
2034  * Returns the root group of the specified canvas.
2035  */
2036 SPCanvasGroup *
2037 sp_canvas_root (SPCanvas *canvas)
2039     g_return_val_if_fail (canvas != NULL, NULL);
2040     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2042     return SP_CANVAS_GROUP (canvas->root);
2045 /**
2046  * Scrolls canvas to specific position.
2047  */
2048 void
2049 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2051     g_return_if_fail (canvas != NULL);
2052     g_return_if_fail (SP_IS_CANVAS (canvas));
2054     int ix = (int) (cx + 0.5);
2055     int iy = (int) (cy + 0.5);
2056     int dx = ix - canvas->x0;
2057     int dy = iy - canvas->y0;
2059     canvas->dx0 = cx;
2060     canvas->dy0 = cy;
2061     canvas->x0 = ix;
2062     canvas->y0 = iy;
2064     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2066     if (!clear) {
2067         // scrolling without zoom; redraw only the newly exposed areas
2068         if ((dx != 0) || (dy != 0)) {
2069             canvas->is_scrolling = is_scrolling;
2070             if (GTK_WIDGET_REALIZED (canvas)) {
2071                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2072             }
2073         }
2074     } else {
2075         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2076     }
2078     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
2079     SPEventContext *ec = inkscape_active_event_context();
2080     if (SP_IS_3DBOX_CONTEXT (ec)) {
2081         // We could avoid redraw during panning by checking the status of is_scrolling, but this is
2082         // neither faster nor does it get rid of artefacts, so we update PLs unconditionally
2083         SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec);
2084         bc->_vpdrag->updateLines();
2085     }
2088 /** 
2089  * Updates canvas if necessary.
2090  */
2091 void
2092 sp_canvas_update_now (SPCanvas *canvas)
2094     g_return_if_fail (canvas != NULL);
2095     g_return_if_fail (SP_IS_CANVAS (canvas));
2097     if (!(canvas->need_update ||
2098           canvas->need_redraw))
2099         return;
2101     do_update (canvas);
2104 /**
2105  * Update callback for canvas widget.
2106  */
2107 static void
2108 sp_canvas_request_update (SPCanvas *canvas)
2110     canvas->need_update = TRUE;
2111     add_idle (canvas);
2114 /**
2115  * Forces redraw of rectangular canvas area.
2116  */
2117 void
2118 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2120     NRRectL bbox;
2121     NRRectL visible;
2122     NRRectL clip;
2124     g_return_if_fail (canvas != NULL);
2125     g_return_if_fail (SP_IS_CANVAS (canvas));
2127     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2128     if ((x0 >= x1) || (y0 >= y1)) return;
2130     bbox.x0 = x0;
2131     bbox.y0 = y0;
2132     bbox.x1 = x1;
2133     bbox.y1 = y1;
2135     visible.x0 = canvas->x0;
2136     visible.y0 = canvas->y0;
2137     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2138     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2140     nr_rect_l_intersect (&clip, &bbox, &visible);
2142     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2143     add_idle (canvas);
2146 /**
2147  * Sets world coordinates from win and canvas.
2148  */
2149 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2151     g_return_if_fail (canvas != NULL);
2152     g_return_if_fail (SP_IS_CANVAS (canvas));
2154     if (worldx) *worldx = canvas->x0 + winx;
2155     if (worldy) *worldy = canvas->y0 + winy;
2158 /**
2159  * Sets win coordinates from world and canvas.
2160  */
2161 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2163     g_return_if_fail (canvas != NULL);
2164     g_return_if_fail (SP_IS_CANVAS (canvas));
2166     if (winx) *winx = worldx - canvas->x0;
2167     if (winy) *winy = worldy - canvas->y0;
2170 /**
2171  * Converts point from win to world coordinates.
2172  */
2173 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2175     g_assert (canvas != NULL);
2176     g_assert (SP_IS_CANVAS (canvas));
2178     return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2181 /**
2182  * Converts point from world to win coordinates.
2183  */
2184 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2186     g_assert (canvas != NULL);
2187     g_assert (SP_IS_CANVAS (canvas));
2189     return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2192 /**
2193  * Returns true if point given in world coordinates is inside window.
2194  */
2195 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2197     g_assert( canvas != NULL );
2198     g_assert(SP_IS_CANVAS(canvas));
2200     using NR::X;
2201     using NR::Y;
2202     GtkWidget const &w = *GTK_WIDGET(canvas);
2203     return ( ( canvas->x0 <= world[X] )  &&
2204              ( canvas->y0 <= world[Y] )  &&
2205              ( world[X] < canvas->x0 + w.allocation.width )  &&
2206              ( world[Y] < canvas->y0 + w.allocation.height ) );
2209 /**
2210  * Return canvas window coordinates as NR::Rect.
2211  */
2212 NR::Rect SPCanvas::getViewbox() const
2214     GtkWidget const *w = GTK_WIDGET(this);
2216     return NR::Rect(NR::Point(dx0, dy0),
2217                     NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2220 inline int sp_canvas_tile_floor(int x)
2222     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2225 inline int sp_canvas_tile_ceil(int x)
2227     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2230 /**
2231  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2232  */
2233 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2235     if ( nl >= nr || nt >= nb ) {
2236         if ( canvas->tiles ) g_free(canvas->tiles);
2237         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2238         canvas->tileH=canvas->tileV=0;
2239         canvas->tiles=NULL;
2240         return;
2241     }
2242     int tl=sp_canvas_tile_floor(nl);
2243     int tt=sp_canvas_tile_floor(nt);
2244     int tr=sp_canvas_tile_ceil(nr);
2245     int tb=sp_canvas_tile_ceil(nb);
2247     int nh = tr-tl, nv = tb-tt;
2248     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2249     for (int i=tl; i<tr; i++) {
2250         for (int j=tt; j<tb; j++) {
2251             int ind = (i-tl) + (j-tt)*nh;
2252             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2253                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2254             } else {
2255                 ntiles[ind]=0; // newly exposed areas get 0
2256             }
2257         }
2258     }
2259     if ( canvas->tiles ) g_free(canvas->tiles);
2260     canvas->tiles=ntiles;
2261     canvas->tLeft=tl;
2262     canvas->tTop=tt;
2263     canvas->tRight=tr;
2264     canvas->tBottom=tb;
2265     canvas->tileH=nh;
2266     canvas->tileV=nv;
2269 /*
2270  * Helper that queues a canvas rectangle for redraw
2271  */
2272 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2273     canvas->need_redraw = TRUE;
2275     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2278 /**
2279  * Helper that marks specific canvas rectangle for redraw
2280  */
2281 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2283     if ( nl >= nr || nt >= nb ) {
2284         return;
2285     }
2286     int tl=sp_canvas_tile_floor(nl);
2287     int tt=sp_canvas_tile_floor(nt);
2288     int tr=sp_canvas_tile_ceil(nr);
2289     int tb=sp_canvas_tile_ceil(nb);
2290     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2291     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2292     if ( tr > canvas->tRight ) tr=canvas->tRight;
2293     if ( tt < canvas->tTop ) tt=canvas->tTop;
2294     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2296     for (int i=tl; i<tr; i++) {
2297         for (int j=tt; j<tb; j++) {
2298             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2299         }
2300     }
2304 /*
2305   Local Variables:
2306   mode:c++
2307   c-file-style:"stroustrup"
2308   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2309   indent-tabs-mode:nil
2310   fill-column:99
2311   End:
2312 */
2313 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :