Code

75ffeed846cb82c530a7133d855be596c36e8e0d
[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"
38 // Tiles are a way to minimize the number of redraws, eliminating too small redraws. 
39 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
40 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
41 #define TILE_SIZE 32
43 enum {
44         RENDERMODE_NORMAL,
45         RENDERMODE_NOAA,
46         RENDERMODE_OUTLINE
47 };
49 const gint sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
51 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
53 enum {
54     SP_CANVAS_ITEM_VISIBLE = 1 << 7,
55     SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
56     SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
57 };
59 /**
60  * A group of Items.
61  */
62 struct SPCanvasGroup {
63     SPCanvasItem item;
65     GList *items, *last;
66 };
68 /**
69  * The SPCanvasGroup vtable.
70  */
71 struct SPCanvasGroupClass {
72     SPCanvasItemClass parent_class;
73 };
75 /**
76  * The SPCanvas vtable.
77  */
78 struct SPCanvasClass {
79     GtkWidgetClass parent_class;
80 };
82 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
83 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
85 /* SPCanvasItem */
87 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
90 static void sp_canvas_request_update (SPCanvas *canvas);
92 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
93 static void sp_canvas_item_init (SPCanvasItem *item);
94 static void sp_canvas_item_dispose (GObject *object);
95 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args);
97 static int emit_event (SPCanvas *canvas, GdkEvent *event);
99 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
101 static GtkObjectClass *item_parent_class;
103 /**
104  * Registers the SPCanvasItem class with Glib and returns its type number.
105  */
106 GType
107 sp_canvas_item_get_type (void)
109     static GType type = 0;
110     if (!type) {
111         static const GTypeInfo info = {
112             sizeof (SPCanvasItemClass),
113             NULL, NULL,
114             (GClassInitFunc) sp_canvas_item_class_init,
115             NULL, NULL,
116             sizeof (SPCanvasItem),
117             0,
118             (GInstanceInitFunc) sp_canvas_item_init,
119             NULL
120         };
121         type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
122     }
124     return type;
127 /**
128  * Initializes the SPCanvasItem vtable and the "event" signal.
129  */
130 static void
131 sp_canvas_item_class_init (SPCanvasItemClass *klass)
133     GObjectClass *object_class = (GObjectClass *) klass;
135     /* fixme: Derive from GObject */
136     item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
138     item_signals[ITEM_EVENT] = g_signal_new ("event",
139                                              G_TYPE_FROM_CLASS (klass),
140                                              G_SIGNAL_RUN_LAST,
141                                              G_STRUCT_OFFSET (SPCanvasItemClass, event),
142                                              NULL, NULL,
143                                              sp_marshal_BOOLEAN__POINTER,
144                                              G_TYPE_BOOLEAN, 1,
145                                              GDK_TYPE_EVENT);
147     object_class->dispose = sp_canvas_item_dispose;
150 /**
151  * Callback for initialization of SPCanvasItem.
152  */
153 static void
154 sp_canvas_item_init (SPCanvasItem *item)
156     item->flags |= SP_CANVAS_ITEM_VISIBLE;
157     item->xform = NR::Matrix(NR::identity());
160 /**
161  * Constructs new SPCanvasItem on SPCanvasGroup.
162  */
163 SPCanvasItem *
164 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, const gchar *first_arg_name, ...)
166     va_list args;
168     g_return_val_if_fail (parent != NULL, NULL);
169     g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
170     g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
172     SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
174     va_start (args, first_arg_name);
175     sp_canvas_item_construct (item, parent, first_arg_name, args);
176     va_end (args);
178     return item;
181 /**
182  * Sets up the newly created SPCanvasItem.
183  *
184  * We make it static for encapsulation reasons since it was nowhere used.
185  */
186 static void
187 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args)
189     g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
190     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
192     item->parent = SP_CANVAS_ITEM (parent);
193     item->canvas = item->parent->canvas;
195     g_object_set_valist (G_OBJECT (item), first_arg_name, args);
197     group_add (SP_CANVAS_GROUP (item->parent), item);
199     sp_canvas_item_request_update (item);
200     sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
201     item->canvas->need_repick = TRUE;
204 /**
205  * Helper function that requests redraw only if item's visible flag is set.
206  */
207 static void
208 redraw_if_visible (SPCanvasItem *item)
210     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
211         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
212     }
215 /**
216  * Callback that removes item from all referers and destroys it.
217  */
218 static void
219 sp_canvas_item_dispose (GObject *object)
221     SPCanvasItem *item = SP_CANVAS_ITEM (object);
223     redraw_if_visible (item);
224     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
226     if (item == item->canvas->current_item) {
227         item->canvas->current_item = NULL;
228         item->canvas->need_repick = TRUE;
229     }
231     if (item == item->canvas->new_current_item) {
232         item->canvas->new_current_item = NULL;
233         item->canvas->need_repick = TRUE;
234     }
236     if (item == item->canvas->grabbed_item) {
237         item->canvas->grabbed_item = NULL;
238         gdk_pointer_ungrab (GDK_CURRENT_TIME);
239     }
241     if (item == item->canvas->focused_item)
242         item->canvas->focused_item = NULL;
244     if (item->parent) {
245         group_remove (SP_CANVAS_GROUP (item->parent), item);
246     }
248     G_OBJECT_CLASS (item_parent_class)->dispose (object);
251 /**
252  * Helper function to update item and its children.
253  *
254  * NB! affine is parent2canvas.
255  */
256 static void
257 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
259     /* Apply the child item's transform */
260     NR::Matrix child_affine = item->xform * affine;
262     /* apply object flags to child flags */
263     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
265     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
266         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
268     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
269         child_flags |= SP_CANVAS_UPDATE_AFFINE;
271     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
272         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
273             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
274     }
276     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
277     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
280 /** 
281  * Helper function to invoke the point method of the item.  
282  *
283  * The argument x, y should be in the parent's item-relative coordinate 
284  * system.  This routine applies the inverse of the item's transform, 
285  * maintaining the affine invariant.
286  */
287 static double
288 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
290     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
291         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
293     return NR_HUGE;
296 /**
297  * Makes the item's affine transformation matrix be equal to the specified
298  * matrix.
299  * 
300  * @item: A canvas item.
301  * @affine: An affine transformation matrix.
302  */
303 void
304 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const& affine)
306     item->xform = affine;
308     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
309         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
310         if (item->parent != NULL) {
311             sp_canvas_item_request_update (item->parent);
312         } else {
313             sp_canvas_request_update (item->canvas);
314         }
315     }
317     item->canvas->need_repick = TRUE;
320 /**
321  * Convenience function to reorder items in a group's child list.  
322  *
323  * This puts the specified link after the "before" link.
324  */
325 static void
326 put_item_after (GList *link, GList *before)
328     if (link == before)
329         return;
331     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
333     if (before == NULL) {
334         if (link == parent->items) return;
336         link->prev->next = link->next;
338         if (link->next) {
339             link->next->prev = link->prev;
340         } else {
341             parent->last = link->prev;
342         }
344         link->prev = before;
345         link->next = parent->items;
346         link->next->prev = link;
347         parent->items = link;
348     } else {
349         if ((link == parent->last) && (before == parent->last->prev))
350             return;
352         if (link->next)
353             link->next->prev = link->prev;
355         if (link->prev)
356             link->prev->next = link->next;
357         else {
358             parent->items = link->next;
359             parent->items->prev = NULL;
360         }
362         link->prev = before;
363         link->next = before->next;
365         link->prev->next = link;
367         if (link->next)
368             link->next->prev = link;
369         else
370             parent->last = link;
371     }
375 /**
376  * Raises the item in its parent's stack by the specified number of positions.
377  * 
378  * \param item A canvas item.
379  * \param positions Number of steps to raise the item.
380  *
381  * If the number of positions is greater than the distance to the top of the
382  * stack, then the item is put at the top.
383  */
384 void
385 sp_canvas_item_raise (SPCanvasItem *item, int positions)
387     g_return_if_fail (item != NULL);
388     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
389     g_return_if_fail (positions >= 0);
391     if (!item->parent || positions == 0)
392         return;
394     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
395     GList *link = g_list_find (parent->items, item);
396     g_assert (link != NULL);
398     GList *before;
399     for (before = link; positions && before; positions--)
400         before = before->next;
402     if (!before)
403         before = parent->last;
405     put_item_after (link, before);
407     redraw_if_visible (item);
408     item->canvas->need_repick = TRUE;
412 /**
413  * Lowers the item in its parent's stack by the specified number of positions.
414  *
415  * \param item A canvas item.
416  * \param positions Number of steps to lower the item.
417  *
418  * If the number of positions is greater than the distance to the bottom of the
419  * stack, then the item is put at the bottom.
420  **/
421 void
422 sp_canvas_item_lower (SPCanvasItem *item, int positions)
424     g_return_if_fail (item != NULL);
425     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
426     g_return_if_fail (positions >= 1);
428     if (!item->parent || positions == 0)
429         return;
431     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
432     GList *link = g_list_find (parent->items, item);
433     g_assert (link != NULL);
435     GList *before;
436     if (link->prev)
437         for (before = link->prev; positions && before; positions--)
438             before = before->prev;
439     else
440         before = NULL;
442     put_item_after (link, before);
444     redraw_if_visible (item);
445     item->canvas->need_repick = TRUE;
448 /**
449  * Sets visible flag on item and requests a redraw.
450  */
451 void
452 sp_canvas_item_show (SPCanvasItem *item)
454     g_return_if_fail (item != NULL);
455     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
457     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
458         return;
460     item->flags |= SP_CANVAS_ITEM_VISIBLE;
462     sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
463     item->canvas->need_repick = TRUE;
466 /**
467  * Clears visible flag on item and requests a redraw.
468  */
469 void
470 sp_canvas_item_hide (SPCanvasItem *item)
472     g_return_if_fail (item != NULL);
473     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
475     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
476         return;
478     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
480     sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
481     item->canvas->need_repick = TRUE;
484 /**
485  * Grab item under cursor.
486  * 
487  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
488  */
489 int
490 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
492     g_return_val_if_fail (item != NULL, -1);
493     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
494     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
496     if (item->canvas->grabbed_item)
497         return -1;
499     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
500         return -1;
502     /* fixme: Top hack (Lauris) */
503     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
504     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
505     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
506                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
507                       NULL, cursor, etime);
509     item->canvas->grabbed_item = item;
510     item->canvas->grabbed_event_mask = event_mask;
511     item->canvas->current_item = item; /* So that events go to the grabbed item */
513     return 0;
516 /**
517  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
518  * mouse.
519  * 
520  * \param item A canvas item that holds a grab.
521  * \param etime The timestamp for ungrabbing the mouse.
522  */
523 void
524 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
526     g_return_if_fail (item != NULL);
527     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
529     if (item->canvas->grabbed_item != item)
530         return;
532     item->canvas->grabbed_item = NULL;
534     gdk_pointer_ungrab (etime);
537 /**
538  * Returns the product of all transformation matrices from the root item down
539  * to the item.
540  */
541 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
543     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
545     NR::Matrix affine = NR::identity();
547     while (item) {
548         affine *= item->xform;
549         item = item->parent;
550     }
551     return affine;
554 /**
555  * Helper that returns true iff item is descendant of parent.
556  */
557 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
559     while (item) {
560         if (item == parent)
561             return true;
562         item = item->parent;
563     }
565     return false;
568 /**
569  * Focus canvas, and item under cursor if it is not already focussed.
570  */
571 void
572 sp_canvas_item_grab_focus (SPCanvasItem *item)
574     g_return_if_fail (item != NULL);
575     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
576     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
578     SPCanvasItem *focused_item = item->canvas->focused_item;
580     if (focused_item) {
581         GdkEvent ev;
582         ev.focus_change.type = GDK_FOCUS_CHANGE;
583         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
584         ev.focus_change.send_event = FALSE;
585         ev.focus_change.in = FALSE;
587         emit_event (item->canvas, &ev);
588     }
590     item->canvas->focused_item = item;
591     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
593     if (focused_item) {
594         GdkEvent ev;
595         ev.focus_change.type = GDK_FOCUS_CHANGE;
596         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
597         ev.focus_change.send_event = FALSE;
598         ev.focus_change.in = TRUE;
600         emit_event (item->canvas, &ev);
601     }
604 /**
605  * Requests that the canvas queue an update for the specified item.
606  * 
607  * To be used only by item implementations.
608  */
609 void
610 sp_canvas_item_request_update (SPCanvasItem *item)
612     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
613         return;
615     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
617     if (item->parent != NULL) {
618         /* Recurse up the tree */
619         sp_canvas_item_request_update (item->parent);
620     } else {
621         /* Have reached the top of the tree, make sure the update call gets scheduled. */
622         sp_canvas_request_update (item->canvas);
623     }
626 /**
627  * Returns position of item in group.
628  */
629 gint sp_canvas_item_order (SPCanvasItem * item)
631     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
634 /* SPCanvasGroup */
636 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
637 static void sp_canvas_group_init (SPCanvasGroup *group);
638 static void sp_canvas_group_destroy (GtkObject *object);
640 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
641 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
642 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
644 static SPCanvasItemClass *group_parent_class;
646 /**
647  * Registers SPCanvasGroup class with Gtk and returns its type number.
648  */
649 GtkType
650 sp_canvas_group_get_type (void)
652     static GtkType group_type = 0;
654     if (!group_type) {
655         static const GtkTypeInfo group_info = {
656             "SPCanvasGroup",
657             sizeof (SPCanvasGroup),
658             sizeof (SPCanvasGroupClass),
659             (GtkClassInitFunc) sp_canvas_group_class_init,
660             (GtkObjectInitFunc) sp_canvas_group_init,
661             NULL, NULL, NULL
662         };
664         group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
665     }
667     return group_type;
670 /**
671  * Class initialization function for SPCanvasGroupClass
672  */
673 static void
674 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
676     GtkObjectClass *object_class = (GtkObjectClass *) klass;
677     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
679     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
681     object_class->destroy = sp_canvas_group_destroy;
683     item_class->update = sp_canvas_group_update;
684     item_class->render = sp_canvas_group_render;
685     item_class->point = sp_canvas_group_point;
688 /**
689  * Callback. Empty.
690  */
691 static void
692 sp_canvas_group_init (SPCanvasGroup */*group*/)
694     /* Nothing here */
697 /**
698  * Callback that destroys all items in group and calls group's virtual 
699  * destroy() function.
700  */
701 static void
702 sp_canvas_group_destroy (GtkObject *object)
704     g_return_if_fail (object != NULL);
705     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
707     const SPCanvasGroup *group = SP_CANVAS_GROUP (object);
709     GList *list = group->items;
710     while (list) {
711         SPCanvasItem *child = (SPCanvasItem *)list->data;
712         list = list->next;
714         gtk_object_destroy (GTK_OBJECT (child));
715     }
717     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
718         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
721 /**
722  * Update handler for canvas groups
723  */
724 static void
725 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
727     const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
728     NR::ConvexHull corners(NR::Point(0, 0));
729     bool empty=true;
731     for (GList *list = group->items; list; list = list->next) {
732         SPCanvasItem *i = (SPCanvasItem *)list->data;
734         sp_canvas_item_invoke_update (i, affine, flags);
736         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
737             if (empty) {
738                 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
739                 empty = false;
740             } else {
741                 corners.add(NR::Point(i->x1, i->y1));
742             }
743             corners.add(NR::Point(i->x2, i->y2));
744         }
745     }
747     NR::Rect const &bounds = corners.bounds();
748     item->x1 = bounds.min()[NR::X];
749     item->y1 = bounds.min()[NR::Y];
750     item->x2 = bounds.max()[NR::X];
751     item->y2 = bounds.max()[NR::Y];
754 /**
755  * Point handler for canvas groups.
756  */
757 static double
758 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
760     const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
761     const double x = p[NR::X];
762     const double y = p[NR::Y];
763     int x1 = (int)(x - item->canvas->close_enough);
764     int y1 = (int)(y - item->canvas->close_enough);
765     int x2 = (int)(x + item->canvas->close_enough);
766     int y2 = (int)(y + item->canvas->close_enough);
768     double best = 0.0;
769     *actual_item = NULL;
771     double dist = 0.0;
773     for (GList *list = group->items; list; list = list->next) {
774         SPCanvasItem *child = (SPCanvasItem *)list->data;
776         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
777             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
779             int has_point;
780             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
781                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
782                 has_point = TRUE;
783             } else
784                 has_point = FALSE;
786             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
787                 best = dist;
788                 *actual_item = point_item;
789             }
790         }
791     }
793     return best;
796 /**
797  * Renders all visible canvas group items in buf rectangle.
798  */
799 static void
800 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
802     const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
804     for (GList *list = group->items; list; list = list->next) {
805         SPCanvasItem *child = (SPCanvasItem *)list->data;
806         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
807             if ((child->x1 < buf->rect.x1) &&
808                 (child->y1 < buf->rect.y1) &&
809                 (child->x2 > buf->rect.x0) &&
810                 (child->y2 > buf->rect.y0)) {
811                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
812                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
813             }
814         }
815     }
818 /**
819  * Adds an item to a canvas group.
820  */
821 static void
822 group_add (SPCanvasGroup *group, SPCanvasItem *item)
824     gtk_object_ref (GTK_OBJECT (item));
825     gtk_object_sink (GTK_OBJECT (item));
827     if (!group->items) {
828         group->items = g_list_append (group->items, item);
829         group->last = group->items;
830     } else {
831         group->last = g_list_append (group->last, item)->next;
832     }
834     sp_canvas_item_request_update (item);
837 /** 
838  * Removes an item from a canvas group
839  */
840 static void
841 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
843     g_return_if_fail (group != NULL);
844     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
845     g_return_if_fail (item != NULL);
847     for (GList *children = group->items; children; children = children->next) {
848         if (children->data == item) {
850             /* Unparent the child */
852             item->parent = NULL;
853             gtk_object_unref (GTK_OBJECT (item));
855             /* Remove it from the list */
857             if (children == group->last) group->last = children->prev;
859             group->items = g_list_remove_link (group->items, children);
860             g_list_free (children);
861             break;
862         }
863     }
866 /* SPCanvas */
868 static void sp_canvas_class_init (SPCanvasClass *klass);
869 static void sp_canvas_init (SPCanvas *canvas);
870 static void sp_canvas_destroy (GtkObject *object);
872 static void sp_canvas_realize (GtkWidget *widget);
873 static void sp_canvas_unrealize (GtkWidget *widget);
875 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
876 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
878 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
879 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
880 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
881 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
882 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
883 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
884 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
885 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
887 static GtkWidgetClass *canvas_parent_class;
889 void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb);
890 void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb);
891 static int do_update (SPCanvas *canvas);
893 /**
894  * Registers the SPCanvas class if necessary, and returns the type ID
895  * associated to it.
896  *
897  * \return The type ID of the SPCanvas class.
898  **/
899 GtkType
900 sp_canvas_get_type (void)
902     static GtkType canvas_type = 0;
904     if (!canvas_type) {
905         static const GtkTypeInfo canvas_info = {
906             "SPCanvas",
907             sizeof (SPCanvas),
908             sizeof (SPCanvasClass),
909             (GtkClassInitFunc) sp_canvas_class_init,
910             (GtkObjectInitFunc) sp_canvas_init,
911             NULL, NULL, NULL
912         };
914         canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
915     }
917     return canvas_type;
920 /**
921  * Class initialization function for SPCanvasClass.
922  */
923 static void
924 sp_canvas_class_init (SPCanvasClass *klass)
926     GtkObjectClass *object_class = (GtkObjectClass *) klass;
927     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
929     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
931     object_class->destroy = sp_canvas_destroy;
933     widget_class->realize = sp_canvas_realize;
934     widget_class->unrealize = sp_canvas_unrealize;
935     widget_class->size_request = sp_canvas_size_request;
936     widget_class->size_allocate = sp_canvas_size_allocate;
937     widget_class->button_press_event = sp_canvas_button;
938     widget_class->button_release_event = sp_canvas_button;
939     widget_class->motion_notify_event = sp_canvas_motion;
940     widget_class->scroll_event = sp_canvas_scroll;
941     widget_class->expose_event = sp_canvas_expose;
942     widget_class->key_press_event = sp_canvas_key;
943     widget_class->key_release_event = sp_canvas_key;
944     widget_class->enter_notify_event = sp_canvas_crossing;
945     widget_class->leave_notify_event = sp_canvas_crossing;
946     widget_class->focus_in_event = sp_canvas_focus_in;
947     widget_class->focus_out_event = sp_canvas_focus_out;
950 /** 
951  * Callback: object initialization for SPCanvas.
952  */
953 static void
954 sp_canvas_init (SPCanvas *canvas)
956     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
957     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
958     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
960     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
961     canvas->pick_event.crossing.x = 0;
962     canvas->pick_event.crossing.y = 0;
964     /* Create the root item as a special case */
965     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
966     canvas->root->canvas = canvas;
968     gtk_object_ref (GTK_OBJECT (canvas->root));
969     gtk_object_sink (GTK_OBJECT (canvas->root));
971     canvas->need_repick = TRUE;
973     // See comment at in sp-canvas.h.
974     canvas->gen_all_enter_events = false;
976     canvas->tiles=NULL;
977     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
978     canvas->tileH=canvas->tileV=0;
980     canvas->redraw_aborted.x0 = NR_HUGE_L;
981     canvas->redraw_aborted.x1 = -NR_HUGE_L;
982     canvas->redraw_aborted.y0 = NR_HUGE_L;
983     canvas->redraw_aborted.y1 = -NR_HUGE_L;
984     
985     canvas->redraw_count = 0;
986     
987     canvas->forced_redraw_count = 0;
988     canvas->forced_redraw_limit = -1;
990     canvas->slowest_buffer = 0;
992     canvas->is_scrolling = false;
996 /**
997  * Convenience function to remove the idle handler of a canvas.
998  */
999 static void
1000 remove_idle (SPCanvas *canvas)
1002     if (canvas->idle_id) {
1003         gtk_idle_remove (canvas->idle_id);
1004         canvas->idle_id = 0;
1005     }
1008 /*
1009  * Removes the transient state of the canvas (idle handler, grabs).
1010  */
1011 static void
1012 shutdown_transients (SPCanvas *canvas)
1014     /* We turn off the need_redraw flag, since if the canvas is mapped again
1015      * it will request a redraw anyways.  We do not turn off the need_update
1016      * flag, though, because updates are not queued when the canvas remaps
1017      * itself.
1018      */
1019     if (canvas->need_redraw) {
1020         canvas->need_redraw = FALSE;
1021     }
1022     if ( canvas->tiles ) g_free(canvas->tiles);
1023     canvas->tiles=NULL;
1024     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1025     canvas->tileH=canvas->tileV=0;
1027     if (canvas->grabbed_item) {
1028         canvas->grabbed_item = NULL;
1029         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1030     }
1032     remove_idle (canvas);
1035 /**
1036  * Destroy handler for SPCanvas.
1037  */
1038 static void
1039 sp_canvas_destroy (GtkObject *object)
1041     SPCanvas *canvas = SP_CANVAS (object);
1043     if (canvas->root) {
1044         gtk_object_unref (GTK_OBJECT (canvas->root));
1045         canvas->root = NULL;
1046     }
1048     shutdown_transients (canvas);
1050     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1051         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1054 /**
1055  * Returns new canvas as widget.
1056  */
1057 GtkWidget *
1058 sp_canvas_new_aa (void)
1060     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1062     return (GtkWidget *) canvas;
1065 /**
1066  * The canvas widget's realize callback.
1067  */
1068 static void
1069 sp_canvas_realize (GtkWidget *widget)
1071     SPCanvas *canvas = SP_CANVAS (widget);
1073     GdkWindowAttr attributes;
1074     attributes.window_type = GDK_WINDOW_CHILD;
1075     attributes.x = widget->allocation.x;
1076     attributes.y = widget->allocation.y;
1077     attributes.width = widget->allocation.width;
1078     attributes.height = widget->allocation.height;
1079     attributes.wclass = GDK_INPUT_OUTPUT;
1080     attributes.visual = gdk_rgb_get_visual ();
1081     attributes.colormap = gdk_rgb_get_cmap ();
1082     attributes.event_mask = (gtk_widget_get_events (widget) |
1083                              GDK_EXPOSURE_MASK |
1084                              GDK_BUTTON_PRESS_MASK |
1085                              GDK_BUTTON_RELEASE_MASK |
1086                              GDK_POINTER_MOTION_MASK |
1087                              GDK_PROXIMITY_IN_MASK |
1088                              GDK_PROXIMITY_OUT_MASK |
1089                              GDK_KEY_PRESS_MASK |
1090                              GDK_KEY_RELEASE_MASK |
1091                              GDK_ENTER_NOTIFY_MASK |
1092                              GDK_LEAVE_NOTIFY_MASK |
1093                              GDK_FOCUS_CHANGE_MASK);
1094     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1096     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1097     gdk_window_set_user_data (widget->window, widget);
1099     if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1100         gtk_widget_set_events(widget, attributes.event_mask);
1102     widget->style = gtk_style_attach (widget->style, widget->window);
1104     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1106     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1109 /**
1110  * The canvas widget's unrealize callback.
1111  */
1112 static void
1113 sp_canvas_unrealize (GtkWidget *widget)
1115     SPCanvas *canvas = SP_CANVAS (widget);
1117     shutdown_transients (canvas);
1119     gdk_gc_destroy (canvas->pixmap_gc);
1120     canvas->pixmap_gc = NULL;
1122     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1123         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1126 /**
1127  * The canvas widget's size_request callback.
1128  */
1129 static void
1130 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1132     static_cast<void>(SP_CANVAS (widget));
1134     req->width = 256;
1135     req->height = 256;
1138 /**
1139  * The canvas widget's size_allocate callback.
1140  */
1141 static void
1142 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1144     SPCanvas *canvas = SP_CANVAS (widget);
1146     /* Schedule redraw of new region */
1147     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1148     if (allocation->width > widget->allocation.width) {
1149         sp_canvas_request_redraw (canvas,
1150                                   canvas->x0 + widget->allocation.width,
1151                                   0,
1152                                   canvas->x0 + allocation->width,
1153                                   canvas->y0 + allocation->height);
1154     }
1155     if (allocation->height > widget->allocation.height) {
1156         sp_canvas_request_redraw (canvas,
1157                                   0,
1158                                   canvas->y0 + widget->allocation.height,
1159                                   canvas->x0 + allocation->width,
1160                                   canvas->y0 + allocation->height);
1161     }
1163     widget->allocation = *allocation;
1165     if (GTK_WIDGET_REALIZED (widget)) {
1166         gdk_window_move_resize (widget->window,
1167                                 widget->allocation.x, widget->allocation.y,
1168                                 widget->allocation.width, widget->allocation.height);
1169     }
1172 /**
1173  * Helper that emits an event for an item in the canvas, be it the current 
1174  * item, grabbed item, or focused item, as appropriate.
1175  */
1176 static int
1177 emit_event (SPCanvas *canvas, GdkEvent *event)
1179     guint mask;
1181     if (canvas->grabbed_item) {
1182         switch (event->type) {
1183         case GDK_ENTER_NOTIFY:
1184             mask = GDK_ENTER_NOTIFY_MASK;
1185             break;
1186         case GDK_LEAVE_NOTIFY:
1187             mask = GDK_LEAVE_NOTIFY_MASK;
1188             break;
1189         case GDK_MOTION_NOTIFY:
1190             mask = GDK_POINTER_MOTION_MASK;
1191             break;
1192         case GDK_BUTTON_PRESS:
1193         case GDK_2BUTTON_PRESS:
1194         case GDK_3BUTTON_PRESS:
1195             mask = GDK_BUTTON_PRESS_MASK;
1196             break;
1197         case GDK_BUTTON_RELEASE:
1198             mask = GDK_BUTTON_RELEASE_MASK;
1199             break;
1200         case GDK_KEY_PRESS:
1201             mask = GDK_KEY_PRESS_MASK;
1202             break;
1203         case GDK_KEY_RELEASE:
1204             mask = GDK_KEY_RELEASE_MASK;
1205             break;
1206         case GDK_SCROLL:
1207             mask = GDK_SCROLL;
1208             break;
1209         default:
1210             mask = 0;
1211             break;
1212         }
1214         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1215     }
1217     /* Convert to world coordinates -- we have two cases because of diferent
1218      * offsets of the fields in the event structures.
1219      */
1221     GdkEvent ev = *event;
1223     switch (ev.type) {
1224     case GDK_ENTER_NOTIFY:
1225     case GDK_LEAVE_NOTIFY:
1226         ev.crossing.x += canvas->x0;
1227         ev.crossing.y += canvas->y0;
1228         break;
1229     case GDK_MOTION_NOTIFY:
1230     case GDK_BUTTON_PRESS:
1231     case GDK_2BUTTON_PRESS:
1232     case GDK_3BUTTON_PRESS:
1233     case GDK_BUTTON_RELEASE:
1234         ev.motion.x += canvas->x0;
1235         ev.motion.y += canvas->y0;
1236         break;
1237     default:
1238         break;
1239     }
1241     /* Choose where we send the event */
1243     /* canvas->current_item becomes NULL in some cases under Win32
1244     ** (e.g. if the pointer leaves the window).  So this is a hack that
1245     ** Lauris applied to SP to get around the problem.
1246     */
1247     SPCanvasItem* item = NULL;
1248     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1249         item = canvas->grabbed_item;
1250     } else {
1251         item = canvas->current_item;
1252     }
1254     if (canvas->focused_item &&
1255         ((event->type == GDK_KEY_PRESS) ||
1256          (event->type == GDK_KEY_RELEASE) ||
1257          (event->type == GDK_FOCUS_CHANGE))) {
1258         item = canvas->focused_item;
1259     }
1261     /* The event is propagated up the hierarchy (for if someone connected to
1262      * a group instead of a leaf event), and emission is stopped if a
1263      * handler returns TRUE, just like for GtkWidget events.
1264      */
1266     gint finished = FALSE;
1268     while (item && !finished) {
1269         gtk_object_ref (GTK_OBJECT (item));
1270         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1271         SPCanvasItem *parent = item->parent;
1272         gtk_object_unref (GTK_OBJECT (item));
1273         item = parent;
1274     }
1276     return finished;
1279 /**
1280  * Helper that re-picks the current item in the canvas, based on the event's 
1281  * coordinates and emits enter/leave events for items as appropriate.
1282  */
1283 static int
1284 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1286     int button_down = 0;
1287     double x, y;
1289     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1290         return FALSE;
1292     int retval = FALSE;
1294     if (canvas->gen_all_enter_events == false) {
1295         // If a button is down, we'll perform enter and leave events on the
1296         // current item, but not enter on any other item.  This is more or
1297         // less like X pointer grabbing for canvas items.
1298         //
1299         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1300                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1302         if (!button_down) canvas->left_grabbed_item = FALSE;
1303     }
1305     /* Save the event in the canvas.  This is used to synthesize enter and
1306      * leave events in case the current item changes.  It is also used to
1307      * re-pick the current item if the current one gets deleted.  Also,
1308      * synthesize an enter event.
1309      */
1310     if (event != &canvas->pick_event) {
1311         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1312             /* these fields have the same offsets in both types of events */
1314             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1315             canvas->pick_event.crossing.window     = event->motion.window;
1316             canvas->pick_event.crossing.send_event = event->motion.send_event;
1317             canvas->pick_event.crossing.subwindow  = NULL;
1318             canvas->pick_event.crossing.x          = event->motion.x;
1319             canvas->pick_event.crossing.y          = event->motion.y;
1320             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1321             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1322             canvas->pick_event.crossing.focus      = FALSE;
1323             canvas->pick_event.crossing.state      = event->motion.state;
1325             /* these fields don't have the same offsets in both types of events */
1327             if (event->type == GDK_MOTION_NOTIFY) {
1328                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1329                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1330             } else {
1331                 canvas->pick_event.crossing.x_root = event->button.x_root;
1332                 canvas->pick_event.crossing.y_root = event->button.y_root;
1333             }
1334         } else {
1335             canvas->pick_event = *event;
1336         }
1337     }
1339     /* Don't do anything else if this is a recursive call */
1340     if (canvas->in_repick) return retval;
1342     /* LeaveNotify means that there is no current item, so we don't look for one */
1343     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1344         /* these fields don't have the same offsets in both types of events */
1346         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1347             x = canvas->pick_event.crossing.x;
1348             y = canvas->pick_event.crossing.y;
1349         } else {
1350             x = canvas->pick_event.motion.x;
1351             y = canvas->pick_event.motion.y;
1352         }
1354         /* world coords */
1355         x += canvas->x0;
1356         y += canvas->y0;
1358         /* find the closest item */
1359         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1360             sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1361         } else {
1362             canvas->new_current_item = NULL;
1363         }
1364     } else {
1365         canvas->new_current_item = NULL;
1366     }
1368     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1369         return retval; /* current item did not change */
1370     }
1372     /* Synthesize events for old and new current items */
1374     if ((canvas->new_current_item != canvas->current_item)
1375         && (canvas->current_item != NULL)
1376         && !canvas->left_grabbed_item) {
1377         GdkEvent new_event;
1378         SPCanvasItem *item;
1380         item = canvas->current_item;
1382         new_event = canvas->pick_event;
1383         new_event.type = GDK_LEAVE_NOTIFY;
1385         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1386         new_event.crossing.subwindow = NULL;
1387         canvas->in_repick = TRUE;
1388         retval = emit_event (canvas, &new_event);
1389         canvas->in_repick = FALSE;
1390     }
1392     if (canvas->gen_all_enter_events == false) {
1393         // new_current_item may have been set to NULL during the call to
1394         // emit_event() above
1395         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1396             canvas->left_grabbed_item = TRUE;
1397             return retval;
1398         }
1399     }
1401     /* Handle the rest of cases */
1403     canvas->left_grabbed_item = FALSE;
1404     canvas->current_item = canvas->new_current_item;
1406     if (canvas->current_item != NULL) {
1407         GdkEvent new_event;
1409         new_event = canvas->pick_event;
1410         new_event.type = GDK_ENTER_NOTIFY;
1411         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1412         new_event.crossing.subwindow = NULL;
1413         retval = emit_event (canvas, &new_event);
1414     }
1416     return retval;
1419 /**
1420  * Button event handler for the canvas.
1421  */
1422 static gint
1423 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1425     SPCanvas *canvas = SP_CANVAS (widget);
1427     int retval = FALSE;
1429     /* dispatch normally regardless of the event's window if an item has
1430        has a pointer grab in effect */
1431     if (!canvas->grabbed_item && 
1432         event->window != SP_CANVAS_WINDOW (canvas))
1433         return retval;
1435     int mask;
1436     switch (event->button) {
1437     case 1:
1438         mask = GDK_BUTTON1_MASK;
1439         break;
1440     case 2:
1441         mask = GDK_BUTTON2_MASK;
1442         break;
1443     case 3:
1444         mask = GDK_BUTTON3_MASK;
1445         break;
1446     case 4:
1447         mask = GDK_BUTTON4_MASK;
1448         break;
1449     case 5:
1450         mask = GDK_BUTTON5_MASK;
1451         break;
1452     default:
1453         mask = 0;
1454     }
1456     switch (event->type) {
1457     case GDK_BUTTON_PRESS:
1458     case GDK_2BUTTON_PRESS:
1459     case GDK_3BUTTON_PRESS:
1460         /* Pick the current item as if the button were not pressed, and
1461          * then process the event.
1462          */
1463         canvas->state = event->state;
1464         pick_current_item (canvas, (GdkEvent *) event);
1465         canvas->state ^= mask;
1466         retval = emit_event (canvas, (GdkEvent *) event);
1467         break;
1469     case GDK_BUTTON_RELEASE:
1470         /* Process the event as if the button were pressed, then repick
1471          * after the button has been released
1472          */
1473         canvas->state = event->state;
1474         retval = emit_event (canvas, (GdkEvent *) event);
1475         event->state ^= mask;
1476         canvas->state = event->state;
1477         pick_current_item (canvas, (GdkEvent *) event);
1478         event->state ^= mask;
1479         break;
1481     default:
1482         g_assert_not_reached ();
1483     }
1485     return retval;
1488 /**
1489  * Scroll event handler for the canvas.
1490  *
1491  * \todo FIXME: generate motion events to re-select items.
1492  */
1493 static gint
1494 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1496     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1499 /**
1500  * Motion event handler for the canvas.
1501  */
1502 static int
1503 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1505     SPCanvas *canvas = SP_CANVAS (widget);
1507     if (event->window != SP_CANVAS_WINDOW (canvas))
1508         return FALSE;
1510     if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1511         gint x, y;
1512         gdk_window_get_pointer (widget->window, &x, &y, NULL);
1513         event->x = x;
1514         event->y = y;
1515     }
1517     canvas->state = event->state;
1518     pick_current_item (canvas, (GdkEvent *) event);
1520     return emit_event (canvas, (GdkEvent *) event);
1523 static void
1524 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)
1526     GtkWidget *widget = GTK_WIDGET (canvas);
1528     SPCanvasBuf buf;
1529     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1530         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1531     } else {
1532         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1533     }
1535     buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1536     buf.rect.x0 = x0;
1537     buf.rect.y0 = y0;
1538     buf.rect.x1 = x1;
1539     buf.rect.y1 = y1;
1540     buf.visible_rect.x0 = draw_x1;
1541     buf.visible_rect.y0 = draw_y1;
1542     buf.visible_rect.x1 = draw_x2;
1543     buf.visible_rect.y1 = draw_y2;
1544     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1545     buf.bg_color = (((color->red & 0xff00) << 8)
1546                     | (color->green & 0xff00)
1547                     | (color->blue >> 8));
1548     buf.is_empty = true;
1549       
1550     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1551         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1552     }
1553       
1554     if (buf.is_empty) {
1555         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1556         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1557                             canvas->pixmap_gc,
1558                             TRUE,
1559                             x0 - canvas->x0, y0 - canvas->y0,
1560                             x1 - x0, y1 - y0);
1561     } else {
1562 /*
1563 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1564 // Why this must not be done currently:
1565 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1566 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)  
1567 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels, 
1568 // we need more bufs to paint a given area and as a result it's even a bit slower
1570     cairo_surface_t* cst = cairo_image_surface_create_for_data (
1571         buf.buf,
1572         CAIRO_FORMAT_RGB24,  // unpacked, i.e. 32 bits! one byte is unused
1573         x1 - x0, y1 - y0,
1574         buf.buf_rowstride
1575         );
1576         cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1577         cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1578         cairo_paint (ct);
1579     cairo_destroy (ct);
1580     cairo_surface_finish (cst);
1581     cairo_surface_destroy (cst);
1582 */
1584         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1585                                       canvas->pixmap_gc,
1586                                       x0 - canvas->x0, y0 - canvas->y0,
1587                                       x1 - x0, y1 - y0,
1588                                       GDK_RGB_DITHER_MAX,
1589                                       buf.buf,
1590                                       sw * 3,
1591                                       x0 - canvas->x0, y0 - canvas->y0);
1592     }
1594     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1595         nr_pixelstore_256K_free (buf.buf);
1596     } else {
1597         nr_pixelstore_1M_free (buf.buf);
1598     }
1601 /* Paint the given rect, while updating canvas->redraw_aborted and running iterations after each
1602  * buffer; make sure canvas->redraw_aborted never goes past aborted_limit (used for 2-rect
1603  * optimized repaint)
1604  */
1605 static int
1606 sp_canvas_paint_rect_internal (SPCanvas *canvas, NRRectL *rect, NR::ICoord *x_aborted_limit, NR::ICoord *y_aborted_limit)
1608     int draw_x1 = rect->x0;
1609     int draw_x2 = rect->x1;
1610     int draw_y1 = rect->y0;
1611     int draw_y2 = rect->y1;
1613     // Here we'll store the time it took to draw the slowest buffer of this paint. 
1614     glong slowest_buffer = 0;
1616     // Find the optimal buffer dimensions
1617     int bw = draw_x2 - draw_x1;
1618     int bh = draw_y2 - draw_y1;
1619     if ((bw < 1) || (bh < 1))
1620         return 0;
1622     int sw, sh;  // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1623     if (canvas->rendermode != RENDERMODE_OUTLINE) { // use 256K as a compromise to not slow down gradients
1624         /* 256K is the cached buffer and we need 3 channels */
1625         if (bw * bh <  87381) { // 256K/3
1626             // We can go with single buffer 
1627             sw = bw;
1628             sh = bh;
1629         } else if (bw <= (16 * 341)) {
1630             // Go with row buffer 
1631             sw = bw;
1632             sh =  87381 / bw;
1633         } else if (bh <= (16 * 256)) {
1634             // Go with column buffer 
1635             sw = 87381 / bh;
1636             sh = bh;
1637         } else {
1638             sw = 341;
1639             sh = 256;
1640         }
1641     } else {  // paths only, so 1M works faster
1642         /* 1M is the cached buffer and we need 3 channels */
1643         if (bw * bh <  349525) { // 1M/3
1644             // We can go with single buffer 
1645             sw = bw;
1646             sh = bh;
1647         } else if (bw <= (16 * 682)) {
1648             // Go with row buffer 
1649             sw = bw;
1650             sh =  349525 / bw;
1651         } else if (bh <= (16 * 512)) {
1652             // Go with column buffer 
1653             sw = 349525 / bh;
1654             sh = bh;
1655         } else {
1656             sw = 682;
1657             sh = 512;
1658         }
1659     }
1660     
1661     // Will this paint require more than one buffer?
1662     bool multiple_buffers = (((draw_y2 - draw_y1) > sh) || ((draw_x2 - draw_x1) > sw)); // or two_rects
1664     // remember the counter during this paint
1665     long this_count = canvas->redraw_count;
1667     // Time values to measure each buffer's paint time
1668     GTimeVal tstart, tfinish;
1670     // paint from the corner nearest the mouse pointer
1672     gint x, y;
1673     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1674     NR::Point pw = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1676     bool reverse_x = (pw[NR::X] > ((draw_x2 + draw_x1) / 2));
1677     bool reverse_y = (pw[NR::Y] > ((draw_y2 + draw_y1) / 2));
1679     if ((bw > bh) && (sh > sw)) {
1680       int t = sw; sw = sh; sh = t;
1681     }
1683     // This is the main loop which corresponds to the visible left-to-right, top-to-bottom drawing
1684     // of screen blocks (buffers).
1685     for (int y0 = draw_y1; y0 < draw_y2; y0 += sh) {
1686         int y1 = MIN (y0 + sh, draw_y2);
1687         for (int x0 = draw_x1; x0 < draw_x2; x0 += sw) {
1688             int x1 = MIN (x0 + sw, draw_x2);
1690             int dx0 = x0;
1691             int dx1 = x1;
1692             int dy0 = y0;
1693             int dy1 = y1;
1695             if (reverse_x) { 
1696               dx0 = (draw_x2 - (x0 + sw)) + draw_x1;
1697               dx0 = MAX (dx0, draw_x1);
1698               dx1 = (draw_x2 - x0) + draw_x1;
1699               dx1 = MIN (dx1, draw_x2);
1700             }
1701             if (reverse_y) { 
1702               dy0 = (draw_y2 - (y0 + sh)) + draw_y1;
1703               dy0 = MAX (dy0, draw_y1);
1704               dy1 = (draw_y2 - y0) + draw_y1;
1705               dy1 = MIN (dy1, draw_y2);
1706             }
1708             // SMOOTH SCROLLING: if we are scrolling, process pending events even before doing any rendering. 
1709             // This allows for scrolling smoothly without hiccups. Any accumulated redraws will be made 
1710             // when scrolling stops. The scrolling flag is set by sp_canvas_scroll_to for each scroll and zeroed
1711             // here for each redraw, to ensure it never gets stuck. 
1713             // OPTIMIZATION IDEA: if drawing is really slow (as measured by canvas->slowest
1714             // buffer), do the same - process some events even before we paint any buffers
1716             if (canvas->is_scrolling) {
1717                 while (Gtk::Main::events_pending()) { // process any events
1718                     Gtk::Main::iteration(false);
1719                 }
1720                 canvas->is_scrolling = false;
1721                 if (this_count != canvas->redraw_count) { // if there was redraw,
1722                     return 1; // interrupt this one
1723                 }
1724             }
1725             
1726             // Paint one buffer; measure how long it takes.
1727             g_get_current_time (&tstart);
1728             sp_canvas_paint_single_buffer (canvas, dx0, dy0, dx1, dy1, draw_x1, draw_y1, draw_x2, draw_y2, sw);
1729             g_get_current_time (&tfinish);
1731             // Remember the slowest_buffer of this paint.
1732             glong this_buffer = (tfinish.tv_sec - tstart.tv_sec) * 1000000 + (tfinish.tv_usec - tstart.tv_usec);
1733             if (this_buffer > slowest_buffer) 
1734                 slowest_buffer = this_buffer;
1736             // After each successful buffer, reduce the rect remaining to redraw by what is already redrawn
1737             if (x1 >= draw_x2) {
1738               if (reverse_y) {
1739                 if (canvas->redraw_aborted.y1 > dy0) { canvas->redraw_aborted.y1 = dy0; }
1740               } else {
1741                 if (canvas->redraw_aborted.y0 < y1)  { canvas->redraw_aborted.y0 = y1; }
1742               }
1743             }
1745             if (y1 >= draw_y2) {
1746               if (reverse_x) {
1747                 if (canvas->redraw_aborted.x1 > dx0) { canvas->redraw_aborted.x1 = dx0; }
1748               } else {
1749                 if (canvas->redraw_aborted.x0 < x1)  { canvas->redraw_aborted.x0 = x1; }
1750               }
1751             }
1753             // INTERRUPTIBLE DISPLAY:
1754             // Process events that may have arrived while we were busy drawing;
1755             // only if we're drawing multiple buffers, and only if this one was not very fast,
1756             // and only if we're allowed to interrupt this redraw
1757             bool ok_to_interrupt = (multiple_buffers && this_buffer > 25000);
1758             if (ok_to_interrupt && (canvas->forced_redraw_limit != -1)) {
1759                 ok_to_interrupt = (canvas->forced_redraw_count < canvas->forced_redraw_limit);
1760             }
1762             if (ok_to_interrupt) {
1763                 // Run at most max_iterations of the main loop; we cannot process ALL events
1764                 // here because some things (e.g. rubberband) flood with dirtying events but will
1765                 // not redraw themselves
1766                 int max_iterations = 10;
1767                 int iterations = 0;
1768                 while (Gtk::Main::events_pending() && iterations++ < max_iterations) {
1769                     Gtk::Main::iteration(false);
1770                     // If one of the iterations has redrawn by itself, abort
1771                     if (this_count != canvas->redraw_count) {
1772                         canvas->slowest_buffer = slowest_buffer;
1773                         if (canvas->forced_redraw_limit != -1) {
1774                             canvas->forced_redraw_count++;
1775                         }
1776                         return 1; // interrupted
1777                     }
1778                 }
1779    
1780                 // If not aborted so far, check if the events set redraw or update flags; 
1781                 // if so, force update and abort
1782                 if (canvas->need_redraw || canvas->need_update) {
1783                     canvas->slowest_buffer = slowest_buffer;
1784                     if (canvas->forced_redraw_limit != -1) {
1785                         canvas->forced_redraw_count++;
1786                     }
1787                     do_update (canvas);
1788                     return 1; // interrupted
1789                 }
1790             }
1791         }
1792     }
1794     // Remember the slowest buffer of this paint in canvas
1795     canvas->slowest_buffer = slowest_buffer;
1797     return 0; // finished
1801 /**
1802  * Helper that draws a specific rectangular part of the canvas.
1803  */
1804 static void
1805 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1807     g_return_if_fail (!canvas->need_update);
1808  
1809     // Monotonously increment the canvas-global counter on each paint. This will let us find out
1810     // when a new paint happened in event processing during this paint, so we can abort it.
1811     canvas->redraw_count++;
1813     NRRectL rect;
1814     rect.x0 = xx0;
1815     rect.x1 = xx1;
1816     rect.y0 = yy0;
1817     rect.y1 = yy1;
1819     // Clip rect-to-draw by the current visible area
1820     rect.x0 = MAX (rect.x0, canvas->x0);
1821     rect.y0 = MAX (rect.y0, canvas->y0);
1822     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1823     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1825     // Clip rect-aborted-last-time by the current visible area
1826     canvas->redraw_aborted.x0 = MAX (canvas->redraw_aborted.x0, canvas->x0);
1827     canvas->redraw_aborted.y0 = MAX (canvas->redraw_aborted.y0, canvas->y0);
1828     canvas->redraw_aborted.x1 = MIN (canvas->redraw_aborted.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1829     canvas->redraw_aborted.y1 = MIN (canvas->redraw_aborted.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1831     if (canvas->redraw_aborted.x0 < canvas->redraw_aborted.x1 && canvas->redraw_aborted.y0 < canvas->redraw_aborted.y1) {
1832         // There was an aborted redraw last time, now we need to redraw BOTH it and the new rect.
1834         // save the old aborted rect in case we decide to paint it separately (see below)
1835         NRRectL aborted = canvas->redraw_aborted;
1837         // calculate the rectangle union of the both rects (the smallest rectangle which covers both) 
1838         NRRectL nion;
1839         nr_rect_l_union (&nion, &rect, &aborted);
1841         // subtract one of the rects-to-draw from the other (the smallest rectangle which covers
1842         // all of the first not covered by the second)
1843         NRRectL rect_minus_aborted;
1844         nr_rect_l_subtract (&rect_minus_aborted, &rect, &aborted);
1846         // Initially, the rect to redraw later (in case we're aborted) is the same as the union of both rects
1847         canvas->redraw_aborted = nion;
1849         // calculate areas of the three rects
1850         if ((nr_rect_l_area(&rect_minus_aborted) + nr_rect_l_area(&aborted)) * 1.2 < nr_rect_l_area(&nion)) {
1851             // If the summary area of the two rects is significantly (at least by 20%) less than
1852             // the area of their rectangular union, it makes sense to paint the two rects
1853             // separately instead of painting their union. This gives a significant speedup when,
1854             // for example, your current canvas is almost painted, with only a strip at bottom
1855             // left, and at that moment you abort it by scrolling down which reveals a new strip at
1856             // the top. Straightforward painting of the union of the aborted rect and the new rect
1857             // will have to repaint the entire canvas! By contrast, the optimized approach below
1858             // paints the two narrow strips in order which is much faster.
1860             // find out which rect to draw first - compare them first by y then by x of the top left corners
1861             NRRectL *first;
1862             NRRectL *second;
1863             if (rect.y0 == aborted.y0) {
1864                 if (rect.x0 < aborted.x0) {
1865                     first = &rect;
1866                     second = &aborted;
1867                 } else {
1868                     second = &rect;
1869                     first = &aborted;
1870                 }
1871             } else if (rect.y0 < aborted.y0) {
1872                 first = &rect;
1873                 second = &aborted;
1874             } else {
1875                 second = &rect;
1876                 first = &aborted;
1877             }
1879             NRRectL second_minus_first;
1880             nr_rect_l_subtract (&second_minus_first, second, first);
1882             // paint the first rect;
1883             if (sp_canvas_paint_rect_internal (canvas, first, &(second_minus_first.x0), &(second_minus_first.y0))) {
1884                 // aborted!
1885                 return;
1886             }
1888             // if not aborted, assign (second rect minus first) as the new redraw_aborted and paint the same
1889             canvas->redraw_aborted = second_minus_first;
1890             if (sp_canvas_paint_rect_internal (canvas, &second_minus_first, NULL, NULL)) {
1891                 return; // aborted
1892             }
1894         } else {
1895             // no need for separate drawing, just draw the union as one rect
1896             if (sp_canvas_paint_rect_internal (canvas, &nion, NULL, NULL)) {
1897                 return; // aborted
1898             }
1899         }
1900     } else {
1901         // Nothing was aborted last time, just draw the rect we're given
1903         // Initially, the rect to redraw later (in case we're aborted) is the same as the one we're going to draw now.
1904         canvas->redraw_aborted = rect;
1906         if (sp_canvas_paint_rect_internal (canvas, &rect, NULL, NULL)) {
1907             return; // aborted
1908         }
1909     }
1911     // we've had a full unaborted redraw, reset the full redraw counter
1912     if (canvas->forced_redraw_limit != -1) {
1913         canvas->forced_redraw_count = 0;
1914     }
1917 /**
1918  * Force a full redraw after a specified number of interrupted redraws
1919  */
1920 void
1921 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1922   g_return_if_fail(canvas != NULL);
1923   
1924   canvas->forced_redraw_limit = count;
1925   canvas->forced_redraw_count = 0;
1928 /**
1929  * End forced full redraw requests
1930  */
1931 void
1932 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1933   g_return_if_fail(canvas != NULL);
1935   canvas->forced_redraw_limit = -1;
1938 /**
1939  * The canvas widget's expose callback.
1940  */
1941 static gint
1942 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1944     SPCanvas *canvas = SP_CANVAS (widget);
1946     if (!GTK_WIDGET_DRAWABLE (widget) || 
1947         (event->window != SP_CANVAS_WINDOW (canvas)))
1948         return FALSE;
1950     int n_rects;
1951     GdkRectangle *rects;
1952     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1954     for (int i = 0; i < n_rects; i++) {
1955         NRRectL rect;
1956                 
1957         rect.x0 = rects[i].x + canvas->x0;
1958         rect.y0 = rects[i].y + canvas->y0;
1959         rect.x1 = rect.x0 + rects[i].width;
1960         rect.y1 = rect.y0 + rects[i].height;
1962         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1963     }
1965     if (n_rects > 0)
1966         g_free (rects);
1968     return FALSE;
1971 /**
1972  * The canvas widget's keypress callback.
1973  */
1974 static gint
1975 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1977     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1980 /**
1981  * Crossing event handler for the canvas.
1982  */
1983 static gint
1984 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1986     SPCanvas *canvas = SP_CANVAS (widget);
1988     if (event->window != SP_CANVAS_WINDOW (canvas))
1989         return FALSE;
1991     canvas->state = event->state;
1992     return pick_current_item (canvas, (GdkEvent *) event);
1995 /**
1996  * Focus in handler for the canvas.
1997  */
1998 static gint
1999 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2001     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2003     SPCanvas *canvas = SP_CANVAS (widget);
2005     if (canvas->focused_item) {
2006         return emit_event (canvas, (GdkEvent *) event);
2007     } else {
2008         return FALSE;
2009     }
2012 /**
2013  * Focus out handler for the canvas.
2014  */
2015 static gint
2016 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2018     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2020     SPCanvas *canvas = SP_CANVAS (widget);
2022     if (canvas->focused_item)
2023         return emit_event (canvas, (GdkEvent *) event);
2024     else
2025         return FALSE;
2028 /**
2029  * Helper that repaints the areas in the canvas that need it.
2030  */
2031 static int
2032 paint (SPCanvas *canvas)
2034     if (canvas->need_update) {
2035         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2036         canvas->need_update = FALSE;
2037     }
2039     if (!canvas->need_redraw)
2040         return TRUE;
2042     GtkWidget const *widget = GTK_WIDGET(canvas);
2043     int const canvas_x1 = canvas->x0 + widget->allocation.width;
2044     int const canvas_y1 = canvas->y0 + widget->allocation.height;
2046     bool dirty = false;
2048     int pl = canvas->tRight, pr = canvas->tLeft, pt = canvas->tBottom, pb = canvas->tTop; // start with "inverted" tile rect
2050     for (int j=canvas->tTop; j<canvas->tBottom; j++) { 
2051         for (int i=canvas->tLeft; i<canvas->tRight; i++) { 
2053             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2055             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2056                 dirty = true;
2057                 // make (pl..pr)x(pt..pb) the minimal rect covering all dirtied tiles
2058                 if ( i < pl ) pl = i;
2059                 if ( i+1 > pr ) pr = i+1;
2060                 if ( j < pt ) pt = j;
2061                 if ( j+1 > pb ) pb = j+1;
2062             }
2064             canvas->tiles[tile_index] = 0; // undirty this tile
2065         }
2066     }
2068     canvas->need_redraw = FALSE;
2069       
2070     if ( dirty ) {
2071         NRRectL topaint;
2072         topaint.x0 = MAX (pl*TILE_SIZE, canvas->x0);
2073         topaint.y0 = MAX (pt*TILE_SIZE, canvas->y0);
2074         topaint.x1 = MIN (pr*TILE_SIZE, canvas_x1);
2075         topaint.y1 = MIN (pb*TILE_SIZE, canvas_y1);
2076         if ((topaint.x0 < topaint.x1) && (topaint.y0 < topaint.y1)) {
2077             sp_canvas_paint_rect (canvas, topaint.x0, topaint.y0, topaint.x1, topaint.y1);
2078         }
2079     }
2081     return TRUE;
2084 /**
2085  * Helper that invokes update, paint, and repick on canvas.
2086  */
2087 static int
2088 do_update (SPCanvas *canvas)
2090     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2091         return TRUE;
2093     /* Cause the update if necessary */
2094     if (canvas->need_update) {
2095         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2096         canvas->need_update = FALSE;
2097     }
2099     /* Paint if able to */
2100     if (GTK_WIDGET_DRAWABLE (canvas)) {
2101             return paint (canvas);
2102     }
2104     /* Pick new current item */
2105     while (canvas->need_repick) {
2106         canvas->need_repick = FALSE;
2107         pick_current_item (canvas, &canvas->pick_event);
2108     }
2110     return TRUE;
2113 /**
2114  * Idle handler for the canvas that deals with pending updates and redraws.
2115  */
2116 static gint
2117 idle_handler (gpointer data)
2119     GDK_THREADS_ENTER ();
2121     SPCanvas *canvas = SP_CANVAS (data);
2123     const int ret = do_update (canvas);
2125     if (ret) {
2126         /* Reset idle id */
2127         canvas->idle_id = 0;
2128     }
2130     GDK_THREADS_LEAVE ();
2132     return !ret;
2135 /**
2136  * Convenience function to add an idle handler to a canvas.
2137  */
2138 static void
2139 add_idle (SPCanvas *canvas)
2141     if (canvas->idle_id != 0)
2142         return;
2144     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2147 /**
2148  * Returns the root group of the specified canvas.
2149  */
2150 SPCanvasGroup *
2151 sp_canvas_root (SPCanvas *canvas)
2153     g_return_val_if_fail (canvas != NULL, NULL);
2154     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2156     return SP_CANVAS_GROUP (canvas->root);
2159 /**
2160  * Scrolls canvas to specific position.
2161  */
2162 void
2163 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2165     g_return_if_fail (canvas != NULL);
2166     g_return_if_fail (SP_IS_CANVAS (canvas));
2168     int ix = (int) (cx + 0.5);
2169     int iy = (int) (cy + 0.5);
2170     int dx = ix - canvas->x0;
2171     int dy = iy - canvas->y0;
2173     canvas->dx0 = cx;
2174     canvas->dy0 = cy;
2175     canvas->x0 = ix;
2176     canvas->y0 = iy;
2178     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2180     if (!clear) {
2181         // scrolling without zoom; redraw only the newly exposed areas
2182         if ((dx != 0) || (dy != 0)) {
2183             canvas->is_scrolling = is_scrolling;
2184             if (GTK_WIDGET_REALIZED (canvas)) {
2185                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2186             }
2187         }
2188     } else {
2189         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2190     }
2193 /** 
2194  * Updates canvas if necessary.
2195  */
2196 void
2197 sp_canvas_update_now (SPCanvas *canvas)
2199     g_return_if_fail (canvas != NULL);
2200     g_return_if_fail (SP_IS_CANVAS (canvas));
2202     if (!(canvas->need_update ||
2203           canvas->need_redraw))
2204         return;
2206     remove_idle (canvas);
2207     do_update (canvas);
2210 /**
2211  * Update callback for canvas widget.
2212  */
2213 static void
2214 sp_canvas_request_update (SPCanvas *canvas)
2216     canvas->need_update = TRUE;
2217     add_idle (canvas);
2220 /**
2221  * Forces redraw of rectangular canvas area.
2222  */
2223 void
2224 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2226     NRRectL bbox;
2227     NRRectL visible;
2228     NRRectL clip;
2230     g_return_if_fail (canvas != NULL);
2231     g_return_if_fail (SP_IS_CANVAS (canvas));
2233     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2234     if ((x0 >= x1) || (y0 >= y1)) return;
2236     bbox.x0 = x0;
2237     bbox.y0 = y0;
2238     bbox.x1 = x1;
2239     bbox.y1 = y1;
2241     visible.x0 = canvas->x0;
2242     visible.y0 = canvas->y0;
2243     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2244     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2246     nr_rect_l_intersect (&clip, &bbox, &visible);
2248     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2249     add_idle (canvas);
2252 /**
2253  * Sets world coordinates from win and canvas.
2254  */
2255 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2257     g_return_if_fail (canvas != NULL);
2258     g_return_if_fail (SP_IS_CANVAS (canvas));
2260     if (worldx) *worldx = canvas->x0 + winx;
2261     if (worldy) *worldy = canvas->y0 + winy;
2264 /**
2265  * Sets win coordinates from world and canvas.
2266  */
2267 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2269     g_return_if_fail (canvas != NULL);
2270     g_return_if_fail (SP_IS_CANVAS (canvas));
2272     if (winx) *winx = worldx - canvas->x0;
2273     if (winy) *winy = worldy - canvas->y0;
2276 /**
2277  * Converts point from win to world coordinates.
2278  */
2279 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2281     g_assert (canvas != NULL);
2282     g_assert (SP_IS_CANVAS (canvas));
2284     return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2287 /**
2288  * Converts point from world to win coordinates.
2289  */
2290 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2292     g_assert (canvas != NULL);
2293     g_assert (SP_IS_CANVAS (canvas));
2295     return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2298 /**
2299  * Returns true if point given in world coordinates is inside window.
2300  */
2301 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2303     g_assert( canvas != NULL );
2304     g_assert(SP_IS_CANVAS(canvas));
2306     using NR::X;
2307     using NR::Y;
2308     GtkWidget const &w = *GTK_WIDGET(canvas);
2309     return ( ( canvas->x0 <= world[X] )  &&
2310              ( canvas->y0 <= world[Y] )  &&
2311              ( world[X] < canvas->x0 + w.allocation.width )  &&
2312              ( world[Y] < canvas->y0 + w.allocation.height ) );
2315 /**
2316  * Return canvas window coordinates as NRRect.
2317  */
2318 NR::Rect SPCanvas::getViewbox() const
2320     GtkWidget const *w = GTK_WIDGET(this);
2322     return NR::Rect(NR::Point(dx0, dy0),
2323                     NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2326 inline int sp_canvas_tile_floor(int x)
2328     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2331 inline int sp_canvas_tile_ceil(int x)
2333     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2336 /**
2337  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2338  */
2339 void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2341     if ( nl >= nr || nt >= nb ) {
2342         if ( canvas->tiles ) g_free(canvas->tiles);
2343         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2344         canvas->tileH=canvas->tileV=0;
2345         canvas->tiles=NULL;
2346         return;
2347     }
2348     int tl=sp_canvas_tile_floor(nl);
2349     int tt=sp_canvas_tile_floor(nt);
2350     int tr=sp_canvas_tile_ceil(nr);
2351     int tb=sp_canvas_tile_ceil(nb);
2353     int nh = tr-tl, nv = tb-tt;
2354     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2355     for (int i=tl; i<tr; i++) {
2356         for (int j=tt; j<tb; j++) {
2357             int ind = (i-tl) + (j-tt)*nh;
2358             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2359                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2360             } else {
2361                 ntiles[ind]=0; // newly exposed areas get 0
2362             }
2363         }
2364     }
2365     if ( canvas->tiles ) g_free(canvas->tiles);
2366     canvas->tiles=ntiles;
2367     canvas->tLeft=tl;
2368     canvas->tTop=tt;
2369     canvas->tRight=tr;
2370     canvas->tBottom=tb;
2371     canvas->tileH=nh;
2372     canvas->tileV=nv;
2375 /**
2376  * Helper that marks specific canvas rectangle for redraw by dirtying its tiles
2377  */
2378 void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2380     if ( nl >= nr || nt >= nb ) {
2381         return;
2382     }
2383     int tl=sp_canvas_tile_floor(nl);
2384     int tt=sp_canvas_tile_floor(nt);
2385     int tr=sp_canvas_tile_ceil(nr);
2386     int tb=sp_canvas_tile_ceil(nb);
2387     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2388     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2389     if ( tr > canvas->tRight ) tr=canvas->tRight;
2390     if ( tt < canvas->tTop ) tt=canvas->tTop;
2391     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2393     canvas->need_redraw = TRUE;
2395     for (int i=tl; i<tr; i++) {
2396         for (int j=tt; j<tb; j++) {
2397             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = 1;
2398         }
2399     }
2403 /*
2404   Local Variables:
2405   mode:c++
2406   c-file-style:"stroustrup"
2407   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2408   indent-tabs-mode:nil
2409   fill-column:99
2410   End:
2411 */
2412 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :