Code

a hack to prevent the redraw of the entire area when a no-fill ctrlrect is deleted
[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"
39 #include "sodipodi-ctrlrect.h"
41 // Define this to visualize the regions to be redrawn
42 //#define DEBUG_REDRAW 1;
44 // Tiles are a way to minimize the number of redraws, eliminating too small redraws. 
45 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
46 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
47 #define TILE_SIZE 32
49 enum {
50         RENDERMODE_NORMAL,
51         RENDERMODE_NOAA,
52         RENDERMODE_OUTLINE
53 };
55 const gint sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
57 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
59 enum {
60     SP_CANVAS_ITEM_VISIBLE = 1 << 7,
61     SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
62     SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
63 };
65 /**
66  * A group of Items.
67  */
68 struct SPCanvasGroup {
69     SPCanvasItem item;
71     GList *items, *last;
72 };
74 /**
75  * The SPCanvasGroup vtable.
76  */
77 struct SPCanvasGroupClass {
78     SPCanvasItemClass parent_class;
79 };
81 /**
82  * The SPCanvas vtable.
83  */
84 struct SPCanvasClass {
85     GtkWidgetClass parent_class;
86 };
88 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
89 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
91 /* SPCanvasItem */
93 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
96 static void sp_canvas_request_update (SPCanvas *canvas);
98 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
99 static void sp_canvas_item_init (SPCanvasItem *item);
100 static void sp_canvas_item_dispose (GObject *object);
101 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args);
103 static int emit_event (SPCanvas *canvas, GdkEvent *event);
105 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
107 static GtkObjectClass *item_parent_class;
109 /**
110  * Registers the SPCanvasItem class with Glib and returns its type number.
111  */
112 GType
113 sp_canvas_item_get_type (void)
115     static GType type = 0;
116     if (!type) {
117         static const GTypeInfo info = {
118             sizeof (SPCanvasItemClass),
119             NULL, NULL,
120             (GClassInitFunc) sp_canvas_item_class_init,
121             NULL, NULL,
122             sizeof (SPCanvasItem),
123             0,
124             (GInstanceInitFunc) sp_canvas_item_init,
125             NULL
126         };
127         type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
128     }
130     return type;
133 /**
134  * Initializes the SPCanvasItem vtable and the "event" signal.
135  */
136 static void
137 sp_canvas_item_class_init (SPCanvasItemClass *klass)
139     GObjectClass *object_class = (GObjectClass *) klass;
141     /* fixme: Derive from GObject */
142     item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
144     item_signals[ITEM_EVENT] = g_signal_new ("event",
145                                              G_TYPE_FROM_CLASS (klass),
146                                              G_SIGNAL_RUN_LAST,
147                                              G_STRUCT_OFFSET (SPCanvasItemClass, event),
148                                              NULL, NULL,
149                                              sp_marshal_BOOLEAN__POINTER,
150                                              G_TYPE_BOOLEAN, 1,
151                                              GDK_TYPE_EVENT);
153     object_class->dispose = sp_canvas_item_dispose;
156 /**
157  * Callback for initialization of SPCanvasItem.
158  */
159 static void
160 sp_canvas_item_init (SPCanvasItem *item)
162     item->flags |= SP_CANVAS_ITEM_VISIBLE;
163     item->xform = NR::Matrix(NR::identity());
166 /**
167  * Constructs new SPCanvasItem on SPCanvasGroup.
168  */
169 SPCanvasItem *
170 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, const gchar *first_arg_name, ...)
172     va_list args;
174     g_return_val_if_fail (parent != NULL, NULL);
175     g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
176     g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
178     SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
180     va_start (args, first_arg_name);
181     sp_canvas_item_construct (item, parent, first_arg_name, args);
182     va_end (args);
184     return item;
187 /**
188  * Sets up the newly created SPCanvasItem.
189  *
190  * We make it static for encapsulation reasons since it was nowhere used.
191  */
192 static void
193 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args)
195     g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
196     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
198     item->parent = SP_CANVAS_ITEM (parent);
199     item->canvas = item->parent->canvas;
201     g_object_set_valist (G_OBJECT (item), first_arg_name, args);
203     group_add (SP_CANVAS_GROUP (item->parent), item);
205     sp_canvas_item_request_update (item);
208 /**
209  * Helper function that requests redraw only if item's visible flag is set.
210  */
211 static void
212 redraw_if_visible (SPCanvasItem *item)
214     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
215         int x0 = (int)(item->x1);
216         int x1 = (int)(item->x2);
217         int y0 = (int)(item->y1);
218         int y1 = (int)(item->y2);
220         if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
221             sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
222         }
223     }
226 /**
227  * Callback that removes item from all referers and destroys it.
228  */
229 static void
230 sp_canvas_item_dispose (GObject *object)
232     SPCanvasItem *item = SP_CANVAS_ITEM (object);
234     // Hack: if this is a ctrlrect, move it to 0,0;
235     // this redraws only the stroke of the rect to be deleted,
236     // avoiding redraw of the entire area
237     if (SP_IS_CTRLRECT(item)) {
238         SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
239         SP_CTRLRECT(object)->update(item->xform, 0);
240     } else {
241         redraw_if_visible (item);
242     }
243     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
245     if (item == item->canvas->current_item) {
246         item->canvas->current_item = NULL;
247         item->canvas->need_repick = TRUE;
248     }
250     if (item == item->canvas->new_current_item) {
251         item->canvas->new_current_item = NULL;
252         item->canvas->need_repick = TRUE;
253     }
255     if (item == item->canvas->grabbed_item) {
256         item->canvas->grabbed_item = NULL;
257         gdk_pointer_ungrab (GDK_CURRENT_TIME);
258     }
260     if (item == item->canvas->focused_item)
261         item->canvas->focused_item = NULL;
263     if (item->parent) {
264         group_remove (SP_CANVAS_GROUP (item->parent), item);
265     }
267     G_OBJECT_CLASS (item_parent_class)->dispose (object);
270 /**
271  * Helper function to update item and its children.
272  *
273  * NB! affine is parent2canvas.
274  */
275 static void
276 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
278     /* Apply the child item's transform */
279     NR::Matrix child_affine = item->xform * affine;
281     /* apply object flags to child flags */
282     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
284     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
285         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
287     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
288         child_flags |= SP_CANVAS_UPDATE_AFFINE;
290     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
291         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
292             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
293     }
295     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
296     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
299 /** 
300  * Helper function to invoke the point method of the item.  
301  *
302  * The argument x, y should be in the parent's item-relative coordinate 
303  * system.  This routine applies the inverse of the item's transform, 
304  * maintaining the affine invariant.
305  */
306 static double
307 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
309     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
310         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
312     return NR_HUGE;
315 /**
316  * Makes the item's affine transformation matrix be equal to the specified
317  * matrix.
318  * 
319  * @item: A canvas item.
320  * @affine: An affine transformation matrix.
321  */
322 void
323 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const& affine)
325     item->xform = affine;
327     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
328         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
329         if (item->parent != NULL) {
330             sp_canvas_item_request_update (item->parent);
331         } else {
332             sp_canvas_request_update (item->canvas);
333         }
334     }
336     item->canvas->need_repick = TRUE;
339 /**
340  * Convenience function to reorder items in a group's child list.  
341  *
342  * This puts the specified link after the "before" link.
343  */
344 static void
345 put_item_after (GList *link, GList *before)
347     if (link == before)
348         return;
350     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
352     if (before == NULL) {
353         if (link == parent->items) return;
355         link->prev->next = link->next;
357         if (link->next) {
358             link->next->prev = link->prev;
359         } else {
360             parent->last = link->prev;
361         }
363         link->prev = before;
364         link->next = parent->items;
365         link->next->prev = link;
366         parent->items = link;
367     } else {
368         if ((link == parent->last) && (before == parent->last->prev))
369             return;
371         if (link->next)
372             link->next->prev = link->prev;
374         if (link->prev)
375             link->prev->next = link->next;
376         else {
377             parent->items = link->next;
378             parent->items->prev = NULL;
379         }
381         link->prev = before;
382         link->next = before->next;
384         link->prev->next = link;
386         if (link->next)
387             link->next->prev = link;
388         else
389             parent->last = link;
390     }
394 /**
395  * Raises the item in its parent's stack by the specified number of positions.
396  * 
397  * \param item A canvas item.
398  * \param positions Number of steps to raise the item.
399  *
400  * If the number of positions is greater than the distance to the top of the
401  * stack, then the item is put at the top.
402  */
403 void
404 sp_canvas_item_raise (SPCanvasItem *item, int positions)
406     g_return_if_fail (item != NULL);
407     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
408     g_return_if_fail (positions >= 0);
410     if (!item->parent || positions == 0)
411         return;
413     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
414     GList *link = g_list_find (parent->items, item);
415     g_assert (link != NULL);
417     GList *before;
418     for (before = link; positions && before; positions--)
419         before = before->next;
421     if (!before)
422         before = parent->last;
424     put_item_after (link, before);
426     redraw_if_visible (item);
427     item->canvas->need_repick = TRUE;
431 /**
432  * Lowers the item in its parent's stack by the specified number of positions.
433  *
434  * \param item A canvas item.
435  * \param positions Number of steps to lower the item.
436  *
437  * If the number of positions is greater than the distance to the bottom of the
438  * stack, then the item is put at the bottom.
439  **/
440 void
441 sp_canvas_item_lower (SPCanvasItem *item, int positions)
443     g_return_if_fail (item != NULL);
444     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
445     g_return_if_fail (positions >= 1);
447     if (!item->parent || positions == 0)
448         return;
450     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
451     GList *link = g_list_find (parent->items, item);
452     g_assert (link != NULL);
454     GList *before;
455     if (link->prev)
456         for (before = link->prev; positions && before; positions--)
457             before = before->prev;
458     else
459         before = NULL;
461     put_item_after (link, before);
463     redraw_if_visible (item);
464     item->canvas->need_repick = TRUE;
467 /**
468  * Sets visible flag on item and requests a redraw.
469  */
470 void
471 sp_canvas_item_show (SPCanvasItem *item)
473     g_return_if_fail (item != NULL);
474     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
476     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
477         return;
479     item->flags |= SP_CANVAS_ITEM_VISIBLE;
481     int x0 = (int)(item->x1);
482     int x1 = (int)(item->x2);
483     int y0 = (int)(item->y1);
484     int y1 = (int)(item->y2);
486     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
487         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
488         item->canvas->need_repick = TRUE;
489     }
492 /**
493  * Clears visible flag on item and requests a redraw.
494  */
495 void
496 sp_canvas_item_hide (SPCanvasItem *item)
498     g_return_if_fail (item != NULL);
499     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
501     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
502         return;
504     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
506     int x0 = (int)(item->x1);
507     int x1 = (int)(item->x2);
508     int y0 = (int)(item->y1);
509     int y1 = (int)(item->y2);
511     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
512         sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
513         item->canvas->need_repick = TRUE;
514     }
517 /**
518  * Grab item under cursor.
519  * 
520  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
521  */
522 int
523 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
525     g_return_val_if_fail (item != NULL, -1);
526     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
527     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
529     if (item->canvas->grabbed_item)
530         return -1;
532     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
533         return -1;
535     /* fixme: Top hack (Lauris) */
536     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
537     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
538     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
539                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
540                       NULL, cursor, etime);
542     item->canvas->grabbed_item = item;
543     item->canvas->grabbed_event_mask = event_mask;
544     item->canvas->current_item = item; /* So that events go to the grabbed item */
546     return 0;
549 /**
550  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
551  * mouse.
552  * 
553  * \param item A canvas item that holds a grab.
554  * \param etime The timestamp for ungrabbing the mouse.
555  */
556 void
557 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
559     g_return_if_fail (item != NULL);
560     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
562     if (item->canvas->grabbed_item != item)
563         return;
565     item->canvas->grabbed_item = NULL;
567     gdk_pointer_ungrab (etime);
570 /**
571  * Returns the product of all transformation matrices from the root item down
572  * to the item.
573  */
574 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
576     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
578     NR::Matrix affine = NR::identity();
580     while (item) {
581         affine *= item->xform;
582         item = item->parent;
583     }
584     return affine;
587 /**
588  * Helper that returns true iff item is descendant of parent.
589  */
590 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
592     while (item) {
593         if (item == parent)
594             return true;
595         item = item->parent;
596     }
598     return false;
601 /**
602  * Focus canvas, and item under cursor if it is not already focussed.
603  */
604 void
605 sp_canvas_item_grab_focus (SPCanvasItem *item)
607     g_return_if_fail (item != NULL);
608     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
609     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
611     SPCanvasItem *focused_item = item->canvas->focused_item;
613     if (focused_item) {
614         GdkEvent ev;
615         ev.focus_change.type = GDK_FOCUS_CHANGE;
616         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
617         ev.focus_change.send_event = FALSE;
618         ev.focus_change.in = FALSE;
620         emit_event (item->canvas, &ev);
621     }
623     item->canvas->focused_item = item;
624     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
626     if (focused_item) {
627         GdkEvent ev;
628         ev.focus_change.type = GDK_FOCUS_CHANGE;
629         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
630         ev.focus_change.send_event = FALSE;
631         ev.focus_change.in = TRUE;
633         emit_event (item->canvas, &ev);
634     }
637 /**
638  * Requests that the canvas queue an update for the specified item.
639  * 
640  * To be used only by item implementations.
641  */
642 void
643 sp_canvas_item_request_update (SPCanvasItem *item)
645     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
646         return;
648     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
650     if (item->parent != NULL) {
651         /* Recurse up the tree */
652         sp_canvas_item_request_update (item->parent);
653     } else {
654         /* Have reached the top of the tree, make sure the update call gets scheduled. */
655         sp_canvas_request_update (item->canvas);
656     }
659 /**
660  * Returns position of item in group.
661  */
662 gint sp_canvas_item_order (SPCanvasItem * item)
664     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
667 /* SPCanvasGroup */
669 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
670 static void sp_canvas_group_init (SPCanvasGroup *group);
671 static void sp_canvas_group_destroy (GtkObject *object);
673 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
674 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
675 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
677 static SPCanvasItemClass *group_parent_class;
679 /**
680  * Registers SPCanvasGroup class with Gtk and returns its type number.
681  */
682 GtkType
683 sp_canvas_group_get_type (void)
685     static GtkType group_type = 0;
687     if (!group_type) {
688         static const GtkTypeInfo group_info = {
689             "SPCanvasGroup",
690             sizeof (SPCanvasGroup),
691             sizeof (SPCanvasGroupClass),
692             (GtkClassInitFunc) sp_canvas_group_class_init,
693             (GtkObjectInitFunc) sp_canvas_group_init,
694             NULL, NULL, NULL
695         };
697         group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
698     }
700     return group_type;
703 /**
704  * Class initialization function for SPCanvasGroupClass
705  */
706 static void
707 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
709     GtkObjectClass *object_class = (GtkObjectClass *) klass;
710     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
712     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
714     object_class->destroy = sp_canvas_group_destroy;
716     item_class->update = sp_canvas_group_update;
717     item_class->render = sp_canvas_group_render;
718     item_class->point = sp_canvas_group_point;
721 /**
722  * Callback. Empty.
723  */
724 static void
725 sp_canvas_group_init (SPCanvasGroup */*group*/)
727     /* Nothing here */
730 /**
731  * Callback that destroys all items in group and calls group's virtual 
732  * destroy() function.
733  */
734 static void
735 sp_canvas_group_destroy (GtkObject *object)
737     g_return_if_fail (object != NULL);
738     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
740     const SPCanvasGroup *group = SP_CANVAS_GROUP (object);
742     GList *list = group->items;
743     while (list) {
744         SPCanvasItem *child = (SPCanvasItem *)list->data;
745         list = list->next;
747         gtk_object_destroy (GTK_OBJECT (child));
748     }
750     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
751         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
754 /**
755  * Update handler for canvas groups
756  */
757 static void
758 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
760     const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
761     NR::ConvexHull corners(NR::Point(0, 0));
762     bool empty=true;
764     for (GList *list = group->items; list; list = list->next) {
765         SPCanvasItem *i = (SPCanvasItem *)list->data;
767         sp_canvas_item_invoke_update (i, affine, flags);
769         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
770             if (empty) {
771                 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
772                 empty = false;
773             } else {
774                 corners.add(NR::Point(i->x1, i->y1));
775             }
776             corners.add(NR::Point(i->x2, i->y2));
777         }
778     }
780     NR::Maybe<NR::Rect> const bounds = corners.bounds();
781     if (bounds) {
782         item->x1 = bounds->min()[NR::X];
783         item->y1 = bounds->min()[NR::Y];
784         item->x2 = bounds->max()[NR::X];
785         item->y2 = bounds->max()[NR::Y];
786     } else {
787         // FIXME ?
788         item->x1 = item->x2 = item->y1 = item->y2 = 0;
789     }
792 /**
793  * Point handler for canvas groups.
794  */
795 static double
796 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
798     const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
799     const double x = p[NR::X];
800     const double y = p[NR::Y];
801     int x1 = (int)(x - item->canvas->close_enough);
802     int y1 = (int)(y - item->canvas->close_enough);
803     int x2 = (int)(x + item->canvas->close_enough);
804     int y2 = (int)(y + item->canvas->close_enough);
806     double best = 0.0;
807     *actual_item = NULL;
809     double dist = 0.0;
811     for (GList *list = group->items; list; list = list->next) {
812         SPCanvasItem *child = (SPCanvasItem *)list->data;
814         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
815             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
817             int has_point;
818             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
819                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
820                 has_point = TRUE;
821             } else
822                 has_point = FALSE;
824             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
825                 best = dist;
826                 *actual_item = point_item;
827             }
828         }
829     }
831     return best;
834 /**
835  * Renders all visible canvas group items in buf rectangle.
836  */
837 static void
838 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
840     const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
842     for (GList *list = group->items; list; list = list->next) {
843         SPCanvasItem *child = (SPCanvasItem *)list->data;
844         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
845             if ((child->x1 < buf->rect.x1) &&
846                 (child->y1 < buf->rect.y1) &&
847                 (child->x2 > buf->rect.x0) &&
848                 (child->y2 > buf->rect.y0)) {
849                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
850                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
851             }
852         }
853     }
856 /**
857  * Adds an item to a canvas group.
858  */
859 static void
860 group_add (SPCanvasGroup *group, SPCanvasItem *item)
862     gtk_object_ref (GTK_OBJECT (item));
863     gtk_object_sink (GTK_OBJECT (item));
865     if (!group->items) {
866         group->items = g_list_append (group->items, item);
867         group->last = group->items;
868     } else {
869         group->last = g_list_append (group->last, item)->next;
870     }
872     sp_canvas_item_request_update (item);
875 /** 
876  * Removes an item from a canvas group
877  */
878 static void
879 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
881     g_return_if_fail (group != NULL);
882     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
883     g_return_if_fail (item != NULL);
885     for (GList *children = group->items; children; children = children->next) {
886         if (children->data == item) {
888             /* Unparent the child */
890             item->parent = NULL;
891             gtk_object_unref (GTK_OBJECT (item));
893             /* Remove it from the list */
895             if (children == group->last) group->last = children->prev;
897             group->items = g_list_remove_link (group->items, children);
898             g_list_free (children);
899             break;
900         }
901     }
904 /* SPCanvas */
906 static void sp_canvas_class_init (SPCanvasClass *klass);
907 static void sp_canvas_init (SPCanvas *canvas);
908 static void sp_canvas_destroy (GtkObject *object);
910 static void sp_canvas_realize (GtkWidget *widget);
911 static void sp_canvas_unrealize (GtkWidget *widget);
913 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
914 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
916 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
917 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
918 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
919 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
920 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
921 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
922 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
923 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
925 static GtkWidgetClass *canvas_parent_class;
927 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
928 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
929 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
930 static int do_update (SPCanvas *canvas);
932 /**
933  * Registers the SPCanvas class if necessary, and returns the type ID
934  * associated to it.
935  *
936  * \return The type ID of the SPCanvas class.
937  **/
938 GtkType
939 sp_canvas_get_type (void)
941     static GtkType canvas_type = 0;
943     if (!canvas_type) {
944         static const GtkTypeInfo canvas_info = {
945             "SPCanvas",
946             sizeof (SPCanvas),
947             sizeof (SPCanvasClass),
948             (GtkClassInitFunc) sp_canvas_class_init,
949             (GtkObjectInitFunc) sp_canvas_init,
950             NULL, NULL, NULL
951         };
953         canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
954     }
956     return canvas_type;
959 /**
960  * Class initialization function for SPCanvasClass.
961  */
962 static void
963 sp_canvas_class_init (SPCanvasClass *klass)
965     GtkObjectClass *object_class = (GtkObjectClass *) klass;
966     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
968     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
970     object_class->destroy = sp_canvas_destroy;
972     widget_class->realize = sp_canvas_realize;
973     widget_class->unrealize = sp_canvas_unrealize;
974     widget_class->size_request = sp_canvas_size_request;
975     widget_class->size_allocate = sp_canvas_size_allocate;
976     widget_class->button_press_event = sp_canvas_button;
977     widget_class->button_release_event = sp_canvas_button;
978     widget_class->motion_notify_event = sp_canvas_motion;
979     widget_class->scroll_event = sp_canvas_scroll;
980     widget_class->expose_event = sp_canvas_expose;
981     widget_class->key_press_event = sp_canvas_key;
982     widget_class->key_release_event = sp_canvas_key;
983     widget_class->enter_notify_event = sp_canvas_crossing;
984     widget_class->leave_notify_event = sp_canvas_crossing;
985     widget_class->focus_in_event = sp_canvas_focus_in;
986     widget_class->focus_out_event = sp_canvas_focus_out;
989 /** 
990  * Callback: object initialization for SPCanvas.
991  */
992 static void
993 sp_canvas_init (SPCanvas *canvas)
995     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
996     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
997     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
999     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1000     canvas->pick_event.crossing.x = 0;
1001     canvas->pick_event.crossing.y = 0;
1003     /* Create the root item as a special case */
1004     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1005     canvas->root->canvas = canvas;
1007     gtk_object_ref (GTK_OBJECT (canvas->root));
1008     gtk_object_sink (GTK_OBJECT (canvas->root));
1010     canvas->need_repick = TRUE;
1012     // See comment at in sp-canvas.h.
1013     canvas->gen_all_enter_events = false;
1015     canvas->tiles=NULL;
1016     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1017     canvas->tileH=canvas->tileV=0;
1019     canvas->forced_redraw_count = 0;
1020     canvas->forced_redraw_limit = -1;
1022     canvas->is_scrolling = false;
1026 /**
1027  * Convenience function to remove the idle handler of a canvas.
1028  */
1029 static void
1030 remove_idle (SPCanvas *canvas)
1032     if (canvas->idle_id) {
1033         gtk_idle_remove (canvas->idle_id);
1034         canvas->idle_id = 0;
1035     }
1038 /*
1039  * Removes the transient state of the canvas (idle handler, grabs).
1040  */
1041 static void
1042 shutdown_transients (SPCanvas *canvas)
1044     /* We turn off the need_redraw flag, since if the canvas is mapped again
1045      * it will request a redraw anyways.  We do not turn off the need_update
1046      * flag, though, because updates are not queued when the canvas remaps
1047      * itself.
1048      */
1049     if (canvas->need_redraw) {
1050         canvas->need_redraw = FALSE;
1051     }
1052     if ( canvas->tiles ) g_free(canvas->tiles);
1053     canvas->tiles=NULL;
1054     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1055     canvas->tileH=canvas->tileV=0;
1057     if (canvas->grabbed_item) {
1058         canvas->grabbed_item = NULL;
1059         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1060     }
1062     remove_idle (canvas);
1065 /**
1066  * Destroy handler for SPCanvas.
1067  */
1068 static void
1069 sp_canvas_destroy (GtkObject *object)
1071     SPCanvas *canvas = SP_CANVAS (object);
1073     if (canvas->root) {
1074         gtk_object_unref (GTK_OBJECT (canvas->root));
1075         canvas->root = NULL;
1076     }
1078     shutdown_transients (canvas);
1080     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1081         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1084 /**
1085  * Returns new canvas as widget.
1086  */
1087 GtkWidget *
1088 sp_canvas_new_aa (void)
1090     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1092     return (GtkWidget *) canvas;
1095 /**
1096  * The canvas widget's realize callback.
1097  */
1098 static void
1099 sp_canvas_realize (GtkWidget *widget)
1101     SPCanvas *canvas = SP_CANVAS (widget);
1103     GdkWindowAttr attributes;
1104     attributes.window_type = GDK_WINDOW_CHILD;
1105     attributes.x = widget->allocation.x;
1106     attributes.y = widget->allocation.y;
1107     attributes.width = widget->allocation.width;
1108     attributes.height = widget->allocation.height;
1109     attributes.wclass = GDK_INPUT_OUTPUT;
1110     attributes.visual = gdk_rgb_get_visual ();
1111     attributes.colormap = gdk_rgb_get_cmap ();
1112     attributes.event_mask = (gtk_widget_get_events (widget) |
1113                              GDK_EXPOSURE_MASK |
1114                              GDK_BUTTON_PRESS_MASK |
1115                              GDK_BUTTON_RELEASE_MASK |
1116                              GDK_POINTER_MOTION_MASK |
1117                              GDK_PROXIMITY_IN_MASK |
1118                              GDK_PROXIMITY_OUT_MASK |
1119                              GDK_KEY_PRESS_MASK |
1120                              GDK_KEY_RELEASE_MASK |
1121                              GDK_ENTER_NOTIFY_MASK |
1122                              GDK_LEAVE_NOTIFY_MASK |
1123                              GDK_FOCUS_CHANGE_MASK);
1124     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1126     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1127     gdk_window_set_user_data (widget->window, widget);
1129     if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1130         gtk_widget_set_events(widget, attributes.event_mask);
1132     widget->style = gtk_style_attach (widget->style, widget->window);
1134     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1136     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1139 /**
1140  * The canvas widget's unrealize callback.
1141  */
1142 static void
1143 sp_canvas_unrealize (GtkWidget *widget)
1145     SPCanvas *canvas = SP_CANVAS (widget);
1147     shutdown_transients (canvas);
1149     gdk_gc_destroy (canvas->pixmap_gc);
1150     canvas->pixmap_gc = NULL;
1152     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1153         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1156 /**
1157  * The canvas widget's size_request callback.
1158  */
1159 static void
1160 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1162     static_cast<void>(SP_CANVAS (widget));
1164     req->width = 256;
1165     req->height = 256;
1168 /**
1169  * The canvas widget's size_allocate callback.
1170  */
1171 static void
1172 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1174     SPCanvas *canvas = SP_CANVAS (widget);
1176     /* Schedule redraw of new region */
1177     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1178     if (allocation->width > widget->allocation.width) {
1179         sp_canvas_request_redraw (canvas,
1180                                   canvas->x0 + widget->allocation.width,
1181                                   0,
1182                                   canvas->x0 + allocation->width,
1183                                   canvas->y0 + allocation->height);
1184     }
1185     if (allocation->height > widget->allocation.height) {
1186         sp_canvas_request_redraw (canvas,
1187                                   0,
1188                                   canvas->y0 + widget->allocation.height,
1189                                   canvas->x0 + allocation->width,
1190                                   canvas->y0 + allocation->height);
1191     }
1193     widget->allocation = *allocation;
1195     if (GTK_WIDGET_REALIZED (widget)) {
1196         gdk_window_move_resize (widget->window,
1197                                 widget->allocation.x, widget->allocation.y,
1198                                 widget->allocation.width, widget->allocation.height);
1199     }
1202 /**
1203  * Helper that emits an event for an item in the canvas, be it the current 
1204  * item, grabbed item, or focused item, as appropriate.
1205  */
1206 static int
1207 emit_event (SPCanvas *canvas, GdkEvent *event)
1209     guint mask;
1211     if (canvas->grabbed_item) {
1212         switch (event->type) {
1213         case GDK_ENTER_NOTIFY:
1214             mask = GDK_ENTER_NOTIFY_MASK;
1215             break;
1216         case GDK_LEAVE_NOTIFY:
1217             mask = GDK_LEAVE_NOTIFY_MASK;
1218             break;
1219         case GDK_MOTION_NOTIFY:
1220             mask = GDK_POINTER_MOTION_MASK;
1221             break;
1222         case GDK_BUTTON_PRESS:
1223         case GDK_2BUTTON_PRESS:
1224         case GDK_3BUTTON_PRESS:
1225             mask = GDK_BUTTON_PRESS_MASK;
1226             break;
1227         case GDK_BUTTON_RELEASE:
1228             mask = GDK_BUTTON_RELEASE_MASK;
1229             break;
1230         case GDK_KEY_PRESS:
1231             mask = GDK_KEY_PRESS_MASK;
1232             break;
1233         case GDK_KEY_RELEASE:
1234             mask = GDK_KEY_RELEASE_MASK;
1235             break;
1236         case GDK_SCROLL:
1237             mask = GDK_SCROLL;
1238             break;
1239         default:
1240             mask = 0;
1241             break;
1242         }
1244         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1245     }
1247     /* Convert to world coordinates -- we have two cases because of diferent
1248      * offsets of the fields in the event structures.
1249      */
1251     GdkEvent ev = *event;
1253     switch (ev.type) {
1254     case GDK_ENTER_NOTIFY:
1255     case GDK_LEAVE_NOTIFY:
1256         ev.crossing.x += canvas->x0;
1257         ev.crossing.y += canvas->y0;
1258         break;
1259     case GDK_MOTION_NOTIFY:
1260     case GDK_BUTTON_PRESS:
1261     case GDK_2BUTTON_PRESS:
1262     case GDK_3BUTTON_PRESS:
1263     case GDK_BUTTON_RELEASE:
1264         ev.motion.x += canvas->x0;
1265         ev.motion.y += canvas->y0;
1266         break;
1267     default:
1268         break;
1269     }
1271     /* Choose where we send the event */
1273     /* canvas->current_item becomes NULL in some cases under Win32
1274     ** (e.g. if the pointer leaves the window).  So this is a hack that
1275     ** Lauris applied to SP to get around the problem.
1276     */
1277     SPCanvasItem* item = NULL;
1278     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1279         item = canvas->grabbed_item;
1280     } else {
1281         item = canvas->current_item;
1282     }
1284     if (canvas->focused_item &&
1285         ((event->type == GDK_KEY_PRESS) ||
1286          (event->type == GDK_KEY_RELEASE) ||
1287          (event->type == GDK_FOCUS_CHANGE))) {
1288         item = canvas->focused_item;
1289     }
1291     /* The event is propagated up the hierarchy (for if someone connected to
1292      * a group instead of a leaf event), and emission is stopped if a
1293      * handler returns TRUE, just like for GtkWidget events.
1294      */
1296     gint finished = FALSE;
1298     while (item && !finished) {
1299         gtk_object_ref (GTK_OBJECT (item));
1300         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1301         SPCanvasItem *parent = item->parent;
1302         gtk_object_unref (GTK_OBJECT (item));
1303         item = parent;
1304     }
1306     return finished;
1309 /**
1310  * Helper that re-picks the current item in the canvas, based on the event's 
1311  * coordinates and emits enter/leave events for items as appropriate.
1312  */
1313 static int
1314 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1316     int button_down = 0;
1317     double x, y;
1319     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1320         return FALSE;
1322     int retval = FALSE;
1324     if (canvas->gen_all_enter_events == false) {
1325         // If a button is down, we'll perform enter and leave events on the
1326         // current item, but not enter on any other item.  This is more or
1327         // less like X pointer grabbing for canvas items.
1328         //
1329         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1330                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1332         if (!button_down) canvas->left_grabbed_item = FALSE;
1333     }
1335     /* Save the event in the canvas.  This is used to synthesize enter and
1336      * leave events in case the current item changes.  It is also used to
1337      * re-pick the current item if the current one gets deleted.  Also,
1338      * synthesize an enter event.
1339      */
1340     if (event != &canvas->pick_event) {
1341         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1342             /* these fields have the same offsets in both types of events */
1344             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1345             canvas->pick_event.crossing.window     = event->motion.window;
1346             canvas->pick_event.crossing.send_event = event->motion.send_event;
1347             canvas->pick_event.crossing.subwindow  = NULL;
1348             canvas->pick_event.crossing.x          = event->motion.x;
1349             canvas->pick_event.crossing.y          = event->motion.y;
1350             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1351             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1352             canvas->pick_event.crossing.focus      = FALSE;
1353             canvas->pick_event.crossing.state      = event->motion.state;
1355             /* these fields don't have the same offsets in both types of events */
1357             if (event->type == GDK_MOTION_NOTIFY) {
1358                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1359                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1360             } else {
1361                 canvas->pick_event.crossing.x_root = event->button.x_root;
1362                 canvas->pick_event.crossing.y_root = event->button.y_root;
1363             }
1364         } else {
1365             canvas->pick_event = *event;
1366         }
1367     }
1369     /* Don't do anything else if this is a recursive call */
1370     if (canvas->in_repick) return retval;
1372     /* LeaveNotify means that there is no current item, so we don't look for one */
1373     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1374         /* these fields don't have the same offsets in both types of events */
1376         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1377             x = canvas->pick_event.crossing.x;
1378             y = canvas->pick_event.crossing.y;
1379         } else {
1380             x = canvas->pick_event.motion.x;
1381             y = canvas->pick_event.motion.y;
1382         }
1384         /* world coords */
1385         x += canvas->x0;
1386         y += canvas->y0;
1388         /* find the closest item */
1389         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1390             sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1391         } else {
1392             canvas->new_current_item = NULL;
1393         }
1394     } else {
1395         canvas->new_current_item = NULL;
1396     }
1398     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1399         return retval; /* current item did not change */
1400     }
1402     /* Synthesize events for old and new current items */
1404     if ((canvas->new_current_item != canvas->current_item)
1405         && (canvas->current_item != NULL)
1406         && !canvas->left_grabbed_item) {
1407         GdkEvent new_event;
1408         SPCanvasItem *item;
1410         item = canvas->current_item;
1412         new_event = canvas->pick_event;
1413         new_event.type = GDK_LEAVE_NOTIFY;
1415         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1416         new_event.crossing.subwindow = NULL;
1417         canvas->in_repick = TRUE;
1418         retval = emit_event (canvas, &new_event);
1419         canvas->in_repick = FALSE;
1420     }
1422     if (canvas->gen_all_enter_events == false) {
1423         // new_current_item may have been set to NULL during the call to
1424         // emit_event() above
1425         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1426             canvas->left_grabbed_item = TRUE;
1427             return retval;
1428         }
1429     }
1431     /* Handle the rest of cases */
1433     canvas->left_grabbed_item = FALSE;
1434     canvas->current_item = canvas->new_current_item;
1436     if (canvas->current_item != NULL) {
1437         GdkEvent new_event;
1439         new_event = canvas->pick_event;
1440         new_event.type = GDK_ENTER_NOTIFY;
1441         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1442         new_event.crossing.subwindow = NULL;
1443         retval = emit_event (canvas, &new_event);
1444     }
1446     return retval;
1449 /**
1450  * Button event handler for the canvas.
1451  */
1452 static gint
1453 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1455     SPCanvas *canvas = SP_CANVAS (widget);
1457     int retval = FALSE;
1459     /* dispatch normally regardless of the event's window if an item has
1460        has a pointer grab in effect */
1461     if (!canvas->grabbed_item && 
1462         event->window != SP_CANVAS_WINDOW (canvas))
1463         return retval;
1465     int mask;
1466     switch (event->button) {
1467     case 1:
1468         mask = GDK_BUTTON1_MASK;
1469         break;
1470     case 2:
1471         mask = GDK_BUTTON2_MASK;
1472         break;
1473     case 3:
1474         mask = GDK_BUTTON3_MASK;
1475         break;
1476     case 4:
1477         mask = GDK_BUTTON4_MASK;
1478         break;
1479     case 5:
1480         mask = GDK_BUTTON5_MASK;
1481         break;
1482     default:
1483         mask = 0;
1484     }
1486     switch (event->type) {
1487     case GDK_BUTTON_PRESS:
1488     case GDK_2BUTTON_PRESS:
1489     case GDK_3BUTTON_PRESS:
1490         /* Pick the current item as if the button were not pressed, and
1491          * then process the event.
1492          */
1493         canvas->state = event->state;
1494         pick_current_item (canvas, (GdkEvent *) event);
1495         canvas->state ^= mask;
1496         retval = emit_event (canvas, (GdkEvent *) event);
1497         break;
1499     case GDK_BUTTON_RELEASE:
1500         /* Process the event as if the button were pressed, then repick
1501          * after the button has been released
1502          */
1503         canvas->state = event->state;
1504         retval = emit_event (canvas, (GdkEvent *) event);
1505         event->state ^= mask;
1506         canvas->state = event->state;
1507         pick_current_item (canvas, (GdkEvent *) event);
1508         event->state ^= mask;
1509         break;
1511     default:
1512         g_assert_not_reached ();
1513     }
1515     return retval;
1518 /**
1519  * Scroll event handler for the canvas.
1520  *
1521  * \todo FIXME: generate motion events to re-select items.
1522  */
1523 static gint
1524 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1526     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1529 /**
1530  * Motion event handler for the canvas.
1531  */
1532 static int
1533 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1535     SPCanvas *canvas = SP_CANVAS (widget);
1537     if (event->window != SP_CANVAS_WINDOW (canvas))
1538         return FALSE;
1540     if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1541         gint x, y;
1542         gdk_window_get_pointer (widget->window, &x, &y, NULL);
1543         event->x = x;
1544         event->y = y;
1545     }
1547     canvas->state = event->state;
1548     pick_current_item (canvas, (GdkEvent *) event);
1550     return emit_event (canvas, (GdkEvent *) event);
1553 static void
1554 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)
1556     GtkWidget *widget = GTK_WIDGET (canvas);
1558     SPCanvasBuf buf;
1559     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1560         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1561     } else {
1562         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1563     }
1565     // Mark the region clean
1566     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1568     buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1569     buf.rect.x0 = x0;
1570     buf.rect.y0 = y0;
1571     buf.rect.x1 = x1;
1572     buf.rect.y1 = y1;
1573     buf.visible_rect.x0 = draw_x1;
1574     buf.visible_rect.y0 = draw_y1;
1575     buf.visible_rect.x1 = draw_x2;
1576     buf.visible_rect.y1 = draw_y2;
1577     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1578     buf.bg_color = (((color->red & 0xff00) << 8)
1579                     | (color->green & 0xff00)
1580                     | (color->blue >> 8));
1581     buf.is_empty = true;
1582       
1583     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1584         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1585     }
1586       
1587     if (buf.is_empty) {
1588         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1589         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1590                             canvas->pixmap_gc,
1591                             TRUE,
1592                             x0 - canvas->x0, y0 - canvas->y0,
1593                             x1 - x0, y1 - y0);
1594     } else {
1595 /*
1596 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1597 // Why this must not be done currently:
1598 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1599 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)  
1600 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels, 
1601 // we need more bufs to paint a given area and as a result it's even a bit slower
1603     cairo_surface_t* cst = cairo_image_surface_create_for_data (
1604         buf.buf,
1605         CAIRO_FORMAT_RGB24,  // unpacked, i.e. 32 bits! one byte is unused
1606         x1 - x0, y1 - y0,
1607         buf.buf_rowstride
1608         );
1609         cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1610         cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1611         cairo_paint (ct);
1612     cairo_destroy (ct);
1613     cairo_surface_finish (cst);
1614     cairo_surface_destroy (cst);
1615 */
1617         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1618                                       canvas->pixmap_gc,
1619                                       x0 - canvas->x0, y0 - canvas->y0,
1620                                       x1 - x0, y1 - y0,
1621                                       GDK_RGB_DITHER_MAX,
1622                                       buf.buf,
1623                                       sw * 3,
1624                                       x0 - canvas->x0, y0 - canvas->y0);
1625     }
1627     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1628         nr_pixelstore_256K_free (buf.buf);
1629     } else {
1630         nr_pixelstore_1M_free (buf.buf);
1631     }
1634 struct PaintRectSetup {
1635     SPCanvas* canvas;
1636     NRRectL big_rect;
1637     GTimeVal start_time;
1638     int max_pixels;
1639     NR::Point mouse_loc;
1640 };
1642 /**
1643  * Paint the given rect, recursively subdividing the region until it is the size of a single
1644  * buffer.
1645  *
1646  * @return true if the drawing completes
1647  */
1648 static int
1649 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1651     GTimeVal now;
1652     g_get_current_time (&now);
1654     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1655         + (now.tv_usec - setup->start_time.tv_usec);
1657     // Allow only very fast buffers to be run together;
1658     // as soon as the total redraw time exceeds 1ms, cancel;
1659     // this returns control to the idle loop and allows Inkscape to process user input
1660     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1661     // it will get back and finish painting what remains to paint.
1662     if (elapsed > 1000) {
1664         // Interrupting redraw isn't always good.
1665         // For example, when you drag one node of a big path, only the buffer containing
1666         // the mouse cursor will be redrawn again and again, and the rest of the path
1667         // will remain stale because Inkscape never has enough idle time to redraw all 
1668         // of the screen. To work around this, such operations set a forced_redraw_limit > 0. 
1669         // If this limit is set, and if we have aborted redraw more times than is allowed,
1670         // interrupting is blocked and we're forced to redraw full screen once 
1671         // (after which we can again interrupt forced_redraw_limit times).
1672         if (setup->canvas->forced_redraw_limit < 0 || 
1673             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1675             if (setup->canvas->forced_redraw_limit != -1) {
1676                 setup->canvas->forced_redraw_count++;
1677             }
1679             return false;
1680         }
1681     }
1683     // Find the optimal buffer dimensions
1684     int bw = this_rect.x1 - this_rect.x0;
1685     int bh = this_rect.y1 - this_rect.y0;
1686     if ((bw < 1) || (bh < 1))
1687         return 0;
1689     if (bw * bh < setup->max_pixels) {
1690         // We are small enough
1691         sp_canvas_paint_single_buffer (setup->canvas,
1692                                        this_rect.x0, this_rect.y0,
1693                                        this_rect.x1, this_rect.y1,
1694                                        setup->big_rect.x0, setup->big_rect.y0,
1695                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1696         return 1;
1697     }
1699     NRRectL lo = this_rect;
1700     NRRectL hi = this_rect;
1702 /* 
1703 This test determines the redraw strategy: 
1705 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1706 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1707 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1708 and seems to be faster for drawings with many smaller objects at zoom-out.
1710 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1711 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1712 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1713 faster.
1715 The default for now is the strips mode.
1716 */
1717     if (bw < bh) {
1718         int mid = (this_rect.x0 + this_rect.x1) / 2;
1719         // Make sure that mid lies on a tile boundary
1720         mid = (mid / TILE_SIZE) * TILE_SIZE;
1722         lo.x1 = mid;
1723         hi.x0 = mid;
1725         if (setup->mouse_loc[NR::X] < mid) {
1726             // Always paint towards the mouse first
1727             return sp_canvas_paint_rect_internal(setup, lo)
1728                 && sp_canvas_paint_rect_internal(setup, hi);
1729         } else {
1730             return sp_canvas_paint_rect_internal(setup, hi)
1731                 && sp_canvas_paint_rect_internal(setup, lo);
1732         }
1733     } else {
1734         int mid = (this_rect.y0 + this_rect.y1) / 2;
1735         // Make sure that mid lies on a tile boundary
1736         mid = (mid / TILE_SIZE) * TILE_SIZE;
1738         lo.y1 = mid;
1739         hi.y0 = mid;
1741         if (setup->mouse_loc[NR::Y] < mid) {
1742             // Always paint towards the mouse first
1743             return sp_canvas_paint_rect_internal(setup, lo)
1744                 && sp_canvas_paint_rect_internal(setup, hi);
1745         } else {
1746             return sp_canvas_paint_rect_internal(setup, hi)
1747                 && sp_canvas_paint_rect_internal(setup, lo);
1748         }
1749     }
1753 /**
1754  * Helper that draws a specific rectangular part of the canvas.
1755  *
1756  * @return true if the rectangle painting succeeds.
1757  */
1758 static bool
1759 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1761     g_return_val_if_fail (!canvas->need_update, false);
1762  
1763     NRRectL rect;
1764     rect.x0 = xx0;
1765     rect.x1 = xx1;
1766     rect.y0 = yy0;
1767     rect.y1 = yy1;
1769     // Clip rect-to-draw by the current visible area
1770     rect.x0 = MAX (rect.x0, canvas->x0);
1771     rect.y0 = MAX (rect.y0, canvas->y0);
1772     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1773     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1775 #ifdef DEBUG_REDRAW
1776     // paint the area to redraw yellow
1777     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1778     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1779                             canvas->pixmap_gc,
1780                             TRUE,
1781                             rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1782                                               rect.x1 - rect.x0, rect.y1 - rect.y0);
1783 #endif                      
1785     PaintRectSetup setup;
1787     setup.canvas = canvas;
1788     setup.big_rect = rect;
1790     // Save the mouse location
1791     gint x, y;
1792     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1793     setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1795     // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1796     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1797         // use 256K as a compromise to not slow down gradients
1798         // 256K is the cached buffer and we need 3 channels
1799         setup.max_pixels = 87381; // 256K/3
1800     } else {
1801         // paths only, so 1M works faster
1802         // 1M is the cached buffer and we need 3 channels
1803         setup.max_pixels = 349525;
1804     }
1806     // Start the clock
1807     g_get_current_time(&(setup.start_time));
1809     // Go
1810     return sp_canvas_paint_rect_internal(&setup, rect);
1813 /**
1814  * Force a full redraw after a specified number of interrupted redraws
1815  */
1816 void
1817 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1818   g_return_if_fail(canvas != NULL);
1819   
1820   canvas->forced_redraw_limit = count;
1821   canvas->forced_redraw_count = 0;
1824 /**
1825  * End forced full redraw requests
1826  */
1827 void
1828 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1829   g_return_if_fail(canvas != NULL);
1831   canvas->forced_redraw_limit = -1;
1834 /**
1835  * The canvas widget's expose callback.
1836  */
1837 static gint
1838 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1840     SPCanvas *canvas = SP_CANVAS (widget);
1842     if (!GTK_WIDGET_DRAWABLE (widget) || 
1843         (event->window != SP_CANVAS_WINDOW (canvas)))
1844         return FALSE;
1846     int n_rects;
1847     GdkRectangle *rects;
1848     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1850     for (int i = 0; i < n_rects; i++) {
1851         NRRectL rect;
1852                 
1853         rect.x0 = rects[i].x + canvas->x0;
1854         rect.y0 = rects[i].y + canvas->y0;
1855         rect.x1 = rect.x0 + rects[i].width;
1856         rect.y1 = rect.y0 + rects[i].height;
1858         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1859     }
1861     if (n_rects > 0)
1862         g_free (rects);
1864     return FALSE;
1867 /**
1868  * The canvas widget's keypress callback.
1869  */
1870 static gint
1871 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1873     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1876 /**
1877  * Crossing event handler for the canvas.
1878  */
1879 static gint
1880 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1882     SPCanvas *canvas = SP_CANVAS (widget);
1884     if (event->window != SP_CANVAS_WINDOW (canvas))
1885         return FALSE;
1887     canvas->state = event->state;
1888     return pick_current_item (canvas, (GdkEvent *) event);
1891 /**
1892  * Focus in handler for the canvas.
1893  */
1894 static gint
1895 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1897     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1899     SPCanvas *canvas = SP_CANVAS (widget);
1901     if (canvas->focused_item) {
1902         return emit_event (canvas, (GdkEvent *) event);
1903     } else {
1904         return FALSE;
1905     }
1908 /**
1909  * Focus out handler for the canvas.
1910  */
1911 static gint
1912 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1914     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1916     SPCanvas *canvas = SP_CANVAS (widget);
1918     if (canvas->focused_item)
1919         return emit_event (canvas, (GdkEvent *) event);
1920     else
1921         return FALSE;
1924 /**
1925  * Helper that repaints the areas in the canvas that need it.
1926  *
1927  * @return true if all the dirty parts have been redrawn
1928  */
1929 static int
1930 paint (SPCanvas *canvas)
1932     if (canvas->need_update) {
1933         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1934         canvas->need_update = FALSE;
1935     }
1937     if (!canvas->need_redraw)
1938         return TRUE;
1940     Gdk::Region to_paint;
1942     for (int j=canvas->tTop; j<canvas->tBottom; j++) { 
1943         for (int i=canvas->tLeft; i<canvas->tRight; i++) { 
1944             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
1946             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
1947                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
1948                                    TILE_SIZE, TILE_SIZE));
1949             }
1951         }
1952     }
1954     if (!to_paint.empty()) {
1955         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
1956         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
1957         for (Iter i=rect.begin(); i != rect.end(); ++i) {
1958             int x0 = (*i).get_x();
1959             int y0 = (*i).get_y();
1960             int x1 = x0 + (*i).get_width();
1961             int y1 = y0 + (*i).get_height();
1962             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
1963                 // Aborted
1964                 return FALSE;
1965             };
1966         }
1967     }
1969     canvas->need_redraw = FALSE;
1971     // we've had a full unaborted redraw, reset the full redraw counter
1972     if (canvas->forced_redraw_limit != -1) {
1973         canvas->forced_redraw_count = 0;
1974     }
1976     return TRUE;
1979 /**
1980  * Helper that invokes update, paint, and repick on canvas.
1981  */
1982 static int
1983 do_update (SPCanvas *canvas)
1985     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1986         return TRUE;
1988     /* Cause the update if necessary */
1989     if (canvas->need_update) {
1990         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1991         canvas->need_update = FALSE;
1992     }
1994     /* Paint if able to */
1995     if (GTK_WIDGET_DRAWABLE (canvas)) {
1996             return paint (canvas);
1997     }
1999     /* Pick new current item */
2000     while (canvas->need_repick) {
2001         canvas->need_repick = FALSE;
2002         pick_current_item (canvas, &canvas->pick_event);
2003     }
2005     return TRUE;
2008 /**
2009  * Idle handler for the canvas that deals with pending updates and redraws.
2010  */
2011 static gint
2012 idle_handler (gpointer data)
2014     GDK_THREADS_ENTER ();
2016     SPCanvas *canvas = SP_CANVAS (data);
2018     const int ret = do_update (canvas);
2020     if (ret) {
2021         /* Reset idle id */
2022         canvas->idle_id = 0;
2023     }
2025     GDK_THREADS_LEAVE ();
2027     return !ret;
2030 /**
2031  * Convenience function to add an idle handler to a canvas.
2032  */
2033 static void
2034 add_idle (SPCanvas *canvas)
2036     if (canvas->idle_id != 0)
2037         return;
2039     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2042 /**
2043  * Returns the root group of the specified canvas.
2044  */
2045 SPCanvasGroup *
2046 sp_canvas_root (SPCanvas *canvas)
2048     g_return_val_if_fail (canvas != NULL, NULL);
2049     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2051     return SP_CANVAS_GROUP (canvas->root);
2054 /**
2055  * Scrolls canvas to specific position.
2056  */
2057 void
2058 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2060     g_return_if_fail (canvas != NULL);
2061     g_return_if_fail (SP_IS_CANVAS (canvas));
2063     int ix = (int) (cx + 0.5);
2064     int iy = (int) (cy + 0.5);
2065     int dx = ix - canvas->x0;
2066     int dy = iy - canvas->y0;
2068     canvas->dx0 = cx;
2069     canvas->dy0 = cy;
2070     canvas->x0 = ix;
2071     canvas->y0 = iy;
2073     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2075     if (!clear) {
2076         // scrolling without zoom; redraw only the newly exposed areas
2077         if ((dx != 0) || (dy != 0)) {
2078             canvas->is_scrolling = is_scrolling;
2079             if (GTK_WIDGET_REALIZED (canvas)) {
2080                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2081             }
2082         }
2083     } else {
2084         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2085     }
2087     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
2088     SPEventContext *ec = inkscape_active_event_context();
2089     if (SP_IS_3DBOX_CONTEXT (ec)) {
2090         // We could avoid redraw during panning by checking the status of is_scrolling, but this is
2091         // neither faster nor does it get rid of artefacts, so we update PLs unconditionally
2092         SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec);
2093         bc->_vpdrag->updateLines();
2094     }
2097 /** 
2098  * Updates canvas if necessary.
2099  */
2100 void
2101 sp_canvas_update_now (SPCanvas *canvas)
2103     g_return_if_fail (canvas != NULL);
2104     g_return_if_fail (SP_IS_CANVAS (canvas));
2106     if (!(canvas->need_update ||
2107           canvas->need_redraw))
2108         return;
2110     do_update (canvas);
2113 /**
2114  * Update callback for canvas widget.
2115  */
2116 static void
2117 sp_canvas_request_update (SPCanvas *canvas)
2119     canvas->need_update = TRUE;
2120     add_idle (canvas);
2123 /**
2124  * Forces redraw of rectangular canvas area.
2125  */
2126 void
2127 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2129     NRRectL bbox;
2130     NRRectL visible;
2131     NRRectL clip;
2133     g_return_if_fail (canvas != NULL);
2134     g_return_if_fail (SP_IS_CANVAS (canvas));
2136     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2137     if ((x0 >= x1) || (y0 >= y1)) return;
2139     bbox.x0 = x0;
2140     bbox.y0 = y0;
2141     bbox.x1 = x1;
2142     bbox.y1 = y1;
2144     visible.x0 = canvas->x0;
2145     visible.y0 = canvas->y0;
2146     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2147     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2149     nr_rect_l_intersect (&clip, &bbox, &visible);
2151     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2152     add_idle (canvas);
2155 /**
2156  * Sets world coordinates from win and canvas.
2157  */
2158 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2160     g_return_if_fail (canvas != NULL);
2161     g_return_if_fail (SP_IS_CANVAS (canvas));
2163     if (worldx) *worldx = canvas->x0 + winx;
2164     if (worldy) *worldy = canvas->y0 + winy;
2167 /**
2168  * Sets win coordinates from world and canvas.
2169  */
2170 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2172     g_return_if_fail (canvas != NULL);
2173     g_return_if_fail (SP_IS_CANVAS (canvas));
2175     if (winx) *winx = worldx - canvas->x0;
2176     if (winy) *winy = worldy - canvas->y0;
2179 /**
2180  * Converts point from win to world coordinates.
2181  */
2182 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2184     g_assert (canvas != NULL);
2185     g_assert (SP_IS_CANVAS (canvas));
2187     return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2190 /**
2191  * Converts point from world to win coordinates.
2192  */
2193 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2195     g_assert (canvas != NULL);
2196     g_assert (SP_IS_CANVAS (canvas));
2198     return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2201 /**
2202  * Returns true if point given in world coordinates is inside window.
2203  */
2204 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2206     g_assert( canvas != NULL );
2207     g_assert(SP_IS_CANVAS(canvas));
2209     using NR::X;
2210     using NR::Y;
2211     GtkWidget const &w = *GTK_WIDGET(canvas);
2212     return ( ( canvas->x0 <= world[X] )  &&
2213              ( canvas->y0 <= world[Y] )  &&
2214              ( world[X] < canvas->x0 + w.allocation.width )  &&
2215              ( world[Y] < canvas->y0 + w.allocation.height ) );
2218 /**
2219  * Return canvas window coordinates as NR::Rect.
2220  */
2221 NR::Rect SPCanvas::getViewbox() const
2223     GtkWidget const *w = GTK_WIDGET(this);
2225     return NR::Rect(NR::Point(dx0, dy0),
2226                     NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2229 inline int sp_canvas_tile_floor(int x)
2231     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2234 inline int sp_canvas_tile_ceil(int x)
2236     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2239 /**
2240  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2241  */
2242 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2244     if ( nl >= nr || nt >= nb ) {
2245         if ( canvas->tiles ) g_free(canvas->tiles);
2246         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2247         canvas->tileH=canvas->tileV=0;
2248         canvas->tiles=NULL;
2249         return;
2250     }
2251     int tl=sp_canvas_tile_floor(nl);
2252     int tt=sp_canvas_tile_floor(nt);
2253     int tr=sp_canvas_tile_ceil(nr);
2254     int tb=sp_canvas_tile_ceil(nb);
2256     int nh = tr-tl, nv = tb-tt;
2257     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2258     for (int i=tl; i<tr; i++) {
2259         for (int j=tt; j<tb; j++) {
2260             int ind = (i-tl) + (j-tt)*nh;
2261             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2262                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2263             } else {
2264                 ntiles[ind]=0; // newly exposed areas get 0
2265             }
2266         }
2267     }
2268     if ( canvas->tiles ) g_free(canvas->tiles);
2269     canvas->tiles=ntiles;
2270     canvas->tLeft=tl;
2271     canvas->tTop=tt;
2272     canvas->tRight=tr;
2273     canvas->tBottom=tb;
2274     canvas->tileH=nh;
2275     canvas->tileV=nv;
2278 /*
2279  * Helper that queues a canvas rectangle for redraw
2280  */
2281 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2282     canvas->need_redraw = TRUE;
2284     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2287 /**
2288  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2289  */
2290 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2292     if ( nl >= nr || nt >= nb ) {
2293         return;
2294     }
2295     int tl=sp_canvas_tile_floor(nl);
2296     int tt=sp_canvas_tile_floor(nt);
2297     int tr=sp_canvas_tile_ceil(nr);
2298     int tb=sp_canvas_tile_ceil(nb);
2299     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2300     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2301     if ( tr > canvas->tRight ) tr=canvas->tRight;
2302     if ( tt < canvas->tTop ) tt=canvas->tTop;
2303     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2305     for (int i=tl; i<tr; i++) {
2306         for (int j=tt; j<tb; j++) {
2307             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2308         }
2309     }
2313 /*
2314   Local Variables:
2315   mode:c++
2316   c-file-style:"stroustrup"
2317   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2318   indent-tabs-mode:nil
2319   fill-column:99
2320   End:
2321 */
2322 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :