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)
108 {
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;
125 }
127 /**
128 * Initializes the SPCanvasItem vtable and the "event" signal.
129 */
130 static void
131 sp_canvas_item_class_init (SPCanvasItemClass *klass)
132 {
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;
148 }
150 /**
151 * Callback for initialization of SPCanvasItem.
152 */
153 static void
154 sp_canvas_item_init (SPCanvasItem *item)
155 {
156 item->flags |= SP_CANVAS_ITEM_VISIBLE;
157 item->xform = NR::Matrix(NR::identity());
158 }
160 /**
161 * Constructs new SPCanvasItem on SPCanvasGroup.
162 */
163 SPCanvasItem *
164 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, const gchar *first_arg_name, ...)
165 {
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;
179 }
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)
188 {
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;
202 }
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)
209 {
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 }
213 }
215 /**
216 * Callback that removes item from all referers and destroys it.
217 */
218 static void
219 sp_canvas_item_dispose (GObject *object)
220 {
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);
249 }
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)
258 {
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);
278 }
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)
289 {
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;
294 }
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)
305 {
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;
318 }
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)
327 {
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 }
372 }
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)
386 {
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;
409 }
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)
423 {
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;
446 }
448 /**
449 * Sets visible flag on item and requests a redraw.
450 */
451 void
452 sp_canvas_item_show (SPCanvasItem *item)
453 {
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;
464 }
466 /**
467 * Clears visible flag on item and requests a redraw.
468 */
469 void
470 sp_canvas_item_hide (SPCanvasItem *item)
471 {
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;
482 }
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)
491 {
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;
514 }
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)
525 {
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);
535 }
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)
542 {
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;
552 }
554 /**
555 * Helper that returns true iff item is descendant of parent.
556 */
557 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
558 {
559 while (item) {
560 if (item == parent)
561 return true;
562 item = item->parent;
563 }
565 return false;
566 }
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)
573 {
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 }
602 }
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)
611 {
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 }
624 }
626 /**
627 * Returns position of item in group.
628 */
629 gint sp_canvas_item_order (SPCanvasItem * item)
630 {
631 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
632 }
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)
651 {
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;
668 }
670 /**
671 * Class initialization function for SPCanvasGroupClass
672 */
673 static void
674 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
675 {
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;
686 }
688 /**
689 * Callback. Empty.
690 */
691 static void
692 sp_canvas_group_init (SPCanvasGroup */*group*/)
693 {
694 /* Nothing here */
695 }
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)
703 {
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);
719 }
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)
726 {
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::Maybe<NR::Rect> const bounds = corners.bounds();
748 if (bounds) {
749 item->x1 = bounds->min()[NR::X];
750 item->y1 = bounds->min()[NR::Y];
751 item->x2 = bounds->max()[NR::X];
752 item->y2 = bounds->max()[NR::Y];
753 } else {
754 // FIXME ?
755 item->x1 = item->x2 = item->y1 = item->y2 = 0;
756 }
757 }
759 /**
760 * Point handler for canvas groups.
761 */
762 static double
763 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
764 {
765 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
766 const double x = p[NR::X];
767 const double y = p[NR::Y];
768 int x1 = (int)(x - item->canvas->close_enough);
769 int y1 = (int)(y - item->canvas->close_enough);
770 int x2 = (int)(x + item->canvas->close_enough);
771 int y2 = (int)(y + item->canvas->close_enough);
773 double best = 0.0;
774 *actual_item = NULL;
776 double dist = 0.0;
778 for (GList *list = group->items; list; list = list->next) {
779 SPCanvasItem *child = (SPCanvasItem *)list->data;
781 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
782 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
784 int has_point;
785 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
786 dist = sp_canvas_item_invoke_point (child, p, &point_item);
787 has_point = TRUE;
788 } else
789 has_point = FALSE;
791 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
792 best = dist;
793 *actual_item = point_item;
794 }
795 }
796 }
798 return best;
799 }
801 /**
802 * Renders all visible canvas group items in buf rectangle.
803 */
804 static void
805 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
806 {
807 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
809 for (GList *list = group->items; list; list = list->next) {
810 SPCanvasItem *child = (SPCanvasItem *)list->data;
811 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
812 if ((child->x1 < buf->rect.x1) &&
813 (child->y1 < buf->rect.y1) &&
814 (child->x2 > buf->rect.x0) &&
815 (child->y2 > buf->rect.y0)) {
816 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
817 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
818 }
819 }
820 }
821 }
823 /**
824 * Adds an item to a canvas group.
825 */
826 static void
827 group_add (SPCanvasGroup *group, SPCanvasItem *item)
828 {
829 gtk_object_ref (GTK_OBJECT (item));
830 gtk_object_sink (GTK_OBJECT (item));
832 if (!group->items) {
833 group->items = g_list_append (group->items, item);
834 group->last = group->items;
835 } else {
836 group->last = g_list_append (group->last, item)->next;
837 }
839 sp_canvas_item_request_update (item);
840 }
842 /**
843 * Removes an item from a canvas group
844 */
845 static void
846 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
847 {
848 g_return_if_fail (group != NULL);
849 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
850 g_return_if_fail (item != NULL);
852 for (GList *children = group->items; children; children = children->next) {
853 if (children->data == item) {
855 /* Unparent the child */
857 item->parent = NULL;
858 gtk_object_unref (GTK_OBJECT (item));
860 /* Remove it from the list */
862 if (children == group->last) group->last = children->prev;
864 group->items = g_list_remove_link (group->items, children);
865 g_list_free (children);
866 break;
867 }
868 }
869 }
871 /* SPCanvas */
873 static void sp_canvas_class_init (SPCanvasClass *klass);
874 static void sp_canvas_init (SPCanvas *canvas);
875 static void sp_canvas_destroy (GtkObject *object);
877 static void sp_canvas_realize (GtkWidget *widget);
878 static void sp_canvas_unrealize (GtkWidget *widget);
880 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
881 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
883 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
884 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
885 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
886 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
887 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
888 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
889 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
890 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
892 static GtkWidgetClass *canvas_parent_class;
894 void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb);
895 void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb);
896 static int do_update (SPCanvas *canvas);
898 /**
899 * Registers the SPCanvas class if necessary, and returns the type ID
900 * associated to it.
901 *
902 * \return The type ID of the SPCanvas class.
903 **/
904 GtkType
905 sp_canvas_get_type (void)
906 {
907 static GtkType canvas_type = 0;
909 if (!canvas_type) {
910 static const GtkTypeInfo canvas_info = {
911 "SPCanvas",
912 sizeof (SPCanvas),
913 sizeof (SPCanvasClass),
914 (GtkClassInitFunc) sp_canvas_class_init,
915 (GtkObjectInitFunc) sp_canvas_init,
916 NULL, NULL, NULL
917 };
919 canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
920 }
922 return canvas_type;
923 }
925 /**
926 * Class initialization function for SPCanvasClass.
927 */
928 static void
929 sp_canvas_class_init (SPCanvasClass *klass)
930 {
931 GtkObjectClass *object_class = (GtkObjectClass *) klass;
932 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
934 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
936 object_class->destroy = sp_canvas_destroy;
938 widget_class->realize = sp_canvas_realize;
939 widget_class->unrealize = sp_canvas_unrealize;
940 widget_class->size_request = sp_canvas_size_request;
941 widget_class->size_allocate = sp_canvas_size_allocate;
942 widget_class->button_press_event = sp_canvas_button;
943 widget_class->button_release_event = sp_canvas_button;
944 widget_class->motion_notify_event = sp_canvas_motion;
945 widget_class->scroll_event = sp_canvas_scroll;
946 widget_class->expose_event = sp_canvas_expose;
947 widget_class->key_press_event = sp_canvas_key;
948 widget_class->key_release_event = sp_canvas_key;
949 widget_class->enter_notify_event = sp_canvas_crossing;
950 widget_class->leave_notify_event = sp_canvas_crossing;
951 widget_class->focus_in_event = sp_canvas_focus_in;
952 widget_class->focus_out_event = sp_canvas_focus_out;
953 }
955 /**
956 * Callback: object initialization for SPCanvas.
957 */
958 static void
959 sp_canvas_init (SPCanvas *canvas)
960 {
961 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
962 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
963 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
965 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
966 canvas->pick_event.crossing.x = 0;
967 canvas->pick_event.crossing.y = 0;
969 /* Create the root item as a special case */
970 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
971 canvas->root->canvas = canvas;
973 gtk_object_ref (GTK_OBJECT (canvas->root));
974 gtk_object_sink (GTK_OBJECT (canvas->root));
976 canvas->need_repick = TRUE;
978 // See comment at in sp-canvas.h.
979 canvas->gen_all_enter_events = false;
981 canvas->tiles=NULL;
982 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
983 canvas->tileH=canvas->tileV=0;
985 canvas->redraw_aborted.x0 = NR_HUGE_L;
986 canvas->redraw_aborted.x1 = -NR_HUGE_L;
987 canvas->redraw_aborted.y0 = NR_HUGE_L;
988 canvas->redraw_aborted.y1 = -NR_HUGE_L;
990 canvas->redraw_count = 0;
992 canvas->forced_redraw_count = 0;
993 canvas->forced_redraw_limit = -1;
995 canvas->slowest_buffer = 0;
997 canvas->is_scrolling = false;
999 }
1001 /**
1002 * Convenience function to remove the idle handler of a canvas.
1003 */
1004 static void
1005 remove_idle (SPCanvas *canvas)
1006 {
1007 if (canvas->idle_id) {
1008 gtk_idle_remove (canvas->idle_id);
1009 canvas->idle_id = 0;
1010 }
1011 }
1013 /*
1014 * Removes the transient state of the canvas (idle handler, grabs).
1015 */
1016 static void
1017 shutdown_transients (SPCanvas *canvas)
1018 {
1019 /* We turn off the need_redraw flag, since if the canvas is mapped again
1020 * it will request a redraw anyways. We do not turn off the need_update
1021 * flag, though, because updates are not queued when the canvas remaps
1022 * itself.
1023 */
1024 if (canvas->need_redraw) {
1025 canvas->need_redraw = FALSE;
1026 }
1027 if ( canvas->tiles ) g_free(canvas->tiles);
1028 canvas->tiles=NULL;
1029 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1030 canvas->tileH=canvas->tileV=0;
1032 if (canvas->grabbed_item) {
1033 canvas->grabbed_item = NULL;
1034 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1035 }
1037 remove_idle (canvas);
1038 }
1040 /**
1041 * Destroy handler for SPCanvas.
1042 */
1043 static void
1044 sp_canvas_destroy (GtkObject *object)
1045 {
1046 SPCanvas *canvas = SP_CANVAS (object);
1048 if (canvas->root) {
1049 gtk_object_unref (GTK_OBJECT (canvas->root));
1050 canvas->root = NULL;
1051 }
1053 shutdown_transients (canvas);
1055 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1056 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1057 }
1059 /**
1060 * Returns new canvas as widget.
1061 */
1062 GtkWidget *
1063 sp_canvas_new_aa (void)
1064 {
1065 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1067 return (GtkWidget *) canvas;
1068 }
1070 /**
1071 * The canvas widget's realize callback.
1072 */
1073 static void
1074 sp_canvas_realize (GtkWidget *widget)
1075 {
1076 SPCanvas *canvas = SP_CANVAS (widget);
1078 GdkWindowAttr attributes;
1079 attributes.window_type = GDK_WINDOW_CHILD;
1080 attributes.x = widget->allocation.x;
1081 attributes.y = widget->allocation.y;
1082 attributes.width = widget->allocation.width;
1083 attributes.height = widget->allocation.height;
1084 attributes.wclass = GDK_INPUT_OUTPUT;
1085 attributes.visual = gdk_rgb_get_visual ();
1086 attributes.colormap = gdk_rgb_get_cmap ();
1087 attributes.event_mask = (gtk_widget_get_events (widget) |
1088 GDK_EXPOSURE_MASK |
1089 GDK_BUTTON_PRESS_MASK |
1090 GDK_BUTTON_RELEASE_MASK |
1091 GDK_POINTER_MOTION_MASK |
1092 GDK_PROXIMITY_IN_MASK |
1093 GDK_PROXIMITY_OUT_MASK |
1094 GDK_KEY_PRESS_MASK |
1095 GDK_KEY_RELEASE_MASK |
1096 GDK_ENTER_NOTIFY_MASK |
1097 GDK_LEAVE_NOTIFY_MASK |
1098 GDK_FOCUS_CHANGE_MASK);
1099 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1101 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1102 gdk_window_set_user_data (widget->window, widget);
1104 if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1105 gtk_widget_set_events(widget, attributes.event_mask);
1107 widget->style = gtk_style_attach (widget->style, widget->window);
1109 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1111 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1112 }
1114 /**
1115 * The canvas widget's unrealize callback.
1116 */
1117 static void
1118 sp_canvas_unrealize (GtkWidget *widget)
1119 {
1120 SPCanvas *canvas = SP_CANVAS (widget);
1122 shutdown_transients (canvas);
1124 gdk_gc_destroy (canvas->pixmap_gc);
1125 canvas->pixmap_gc = NULL;
1127 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1128 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1129 }
1131 /**
1132 * The canvas widget's size_request callback.
1133 */
1134 static void
1135 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1136 {
1137 static_cast<void>(SP_CANVAS (widget));
1139 req->width = 256;
1140 req->height = 256;
1141 }
1143 /**
1144 * The canvas widget's size_allocate callback.
1145 */
1146 static void
1147 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1148 {
1149 SPCanvas *canvas = SP_CANVAS (widget);
1151 /* Schedule redraw of new region */
1152 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1153 if (allocation->width > widget->allocation.width) {
1154 sp_canvas_request_redraw (canvas,
1155 canvas->x0 + widget->allocation.width,
1156 0,
1157 canvas->x0 + allocation->width,
1158 canvas->y0 + allocation->height);
1159 }
1160 if (allocation->height > widget->allocation.height) {
1161 sp_canvas_request_redraw (canvas,
1162 0,
1163 canvas->y0 + widget->allocation.height,
1164 canvas->x0 + allocation->width,
1165 canvas->y0 + allocation->height);
1166 }
1168 widget->allocation = *allocation;
1170 if (GTK_WIDGET_REALIZED (widget)) {
1171 gdk_window_move_resize (widget->window,
1172 widget->allocation.x, widget->allocation.y,
1173 widget->allocation.width, widget->allocation.height);
1174 }
1175 }
1177 /**
1178 * Helper that emits an event for an item in the canvas, be it the current
1179 * item, grabbed item, or focused item, as appropriate.
1180 */
1181 static int
1182 emit_event (SPCanvas *canvas, GdkEvent *event)
1183 {
1184 guint mask;
1186 if (canvas->grabbed_item) {
1187 switch (event->type) {
1188 case GDK_ENTER_NOTIFY:
1189 mask = GDK_ENTER_NOTIFY_MASK;
1190 break;
1191 case GDK_LEAVE_NOTIFY:
1192 mask = GDK_LEAVE_NOTIFY_MASK;
1193 break;
1194 case GDK_MOTION_NOTIFY:
1195 mask = GDK_POINTER_MOTION_MASK;
1196 break;
1197 case GDK_BUTTON_PRESS:
1198 case GDK_2BUTTON_PRESS:
1199 case GDK_3BUTTON_PRESS:
1200 mask = GDK_BUTTON_PRESS_MASK;
1201 break;
1202 case GDK_BUTTON_RELEASE:
1203 mask = GDK_BUTTON_RELEASE_MASK;
1204 break;
1205 case GDK_KEY_PRESS:
1206 mask = GDK_KEY_PRESS_MASK;
1207 break;
1208 case GDK_KEY_RELEASE:
1209 mask = GDK_KEY_RELEASE_MASK;
1210 break;
1211 case GDK_SCROLL:
1212 mask = GDK_SCROLL;
1213 break;
1214 default:
1215 mask = 0;
1216 break;
1217 }
1219 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1220 }
1222 /* Convert to world coordinates -- we have two cases because of diferent
1223 * offsets of the fields in the event structures.
1224 */
1226 GdkEvent ev = *event;
1228 switch (ev.type) {
1229 case GDK_ENTER_NOTIFY:
1230 case GDK_LEAVE_NOTIFY:
1231 ev.crossing.x += canvas->x0;
1232 ev.crossing.y += canvas->y0;
1233 break;
1234 case GDK_MOTION_NOTIFY:
1235 case GDK_BUTTON_PRESS:
1236 case GDK_2BUTTON_PRESS:
1237 case GDK_3BUTTON_PRESS:
1238 case GDK_BUTTON_RELEASE:
1239 ev.motion.x += canvas->x0;
1240 ev.motion.y += canvas->y0;
1241 break;
1242 default:
1243 break;
1244 }
1246 /* Choose where we send the event */
1248 /* canvas->current_item becomes NULL in some cases under Win32
1249 ** (e.g. if the pointer leaves the window). So this is a hack that
1250 ** Lauris applied to SP to get around the problem.
1251 */
1252 SPCanvasItem* item = NULL;
1253 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1254 item = canvas->grabbed_item;
1255 } else {
1256 item = canvas->current_item;
1257 }
1259 if (canvas->focused_item &&
1260 ((event->type == GDK_KEY_PRESS) ||
1261 (event->type == GDK_KEY_RELEASE) ||
1262 (event->type == GDK_FOCUS_CHANGE))) {
1263 item = canvas->focused_item;
1264 }
1266 /* The event is propagated up the hierarchy (for if someone connected to
1267 * a group instead of a leaf event), and emission is stopped if a
1268 * handler returns TRUE, just like for GtkWidget events.
1269 */
1271 gint finished = FALSE;
1273 while (item && !finished) {
1274 gtk_object_ref (GTK_OBJECT (item));
1275 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1276 SPCanvasItem *parent = item->parent;
1277 gtk_object_unref (GTK_OBJECT (item));
1278 item = parent;
1279 }
1281 return finished;
1282 }
1284 /**
1285 * Helper that re-picks the current item in the canvas, based on the event's
1286 * coordinates and emits enter/leave events for items as appropriate.
1287 */
1288 static int
1289 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1290 {
1291 int button_down = 0;
1292 double x, y;
1294 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1295 return FALSE;
1297 int retval = FALSE;
1299 if (canvas->gen_all_enter_events == false) {
1300 // If a button is down, we'll perform enter and leave events on the
1301 // current item, but not enter on any other item. This is more or
1302 // less like X pointer grabbing for canvas items.
1303 //
1304 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1305 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1307 if (!button_down) canvas->left_grabbed_item = FALSE;
1308 }
1310 /* Save the event in the canvas. This is used to synthesize enter and
1311 * leave events in case the current item changes. It is also used to
1312 * re-pick the current item if the current one gets deleted. Also,
1313 * synthesize an enter event.
1314 */
1315 if (event != &canvas->pick_event) {
1316 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1317 /* these fields have the same offsets in both types of events */
1319 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1320 canvas->pick_event.crossing.window = event->motion.window;
1321 canvas->pick_event.crossing.send_event = event->motion.send_event;
1322 canvas->pick_event.crossing.subwindow = NULL;
1323 canvas->pick_event.crossing.x = event->motion.x;
1324 canvas->pick_event.crossing.y = event->motion.y;
1325 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1326 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1327 canvas->pick_event.crossing.focus = FALSE;
1328 canvas->pick_event.crossing.state = event->motion.state;
1330 /* these fields don't have the same offsets in both types of events */
1332 if (event->type == GDK_MOTION_NOTIFY) {
1333 canvas->pick_event.crossing.x_root = event->motion.x_root;
1334 canvas->pick_event.crossing.y_root = event->motion.y_root;
1335 } else {
1336 canvas->pick_event.crossing.x_root = event->button.x_root;
1337 canvas->pick_event.crossing.y_root = event->button.y_root;
1338 }
1339 } else {
1340 canvas->pick_event = *event;
1341 }
1342 }
1344 /* Don't do anything else if this is a recursive call */
1345 if (canvas->in_repick) return retval;
1347 /* LeaveNotify means that there is no current item, so we don't look for one */
1348 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1349 /* these fields don't have the same offsets in both types of events */
1351 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1352 x = canvas->pick_event.crossing.x;
1353 y = canvas->pick_event.crossing.y;
1354 } else {
1355 x = canvas->pick_event.motion.x;
1356 y = canvas->pick_event.motion.y;
1357 }
1359 /* world coords */
1360 x += canvas->x0;
1361 y += canvas->y0;
1363 /* find the closest item */
1364 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1365 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1366 } else {
1367 canvas->new_current_item = NULL;
1368 }
1369 } else {
1370 canvas->new_current_item = NULL;
1371 }
1373 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1374 return retval; /* current item did not change */
1375 }
1377 /* Synthesize events for old and new current items */
1379 if ((canvas->new_current_item != canvas->current_item)
1380 && (canvas->current_item != NULL)
1381 && !canvas->left_grabbed_item) {
1382 GdkEvent new_event;
1383 SPCanvasItem *item;
1385 item = canvas->current_item;
1387 new_event = canvas->pick_event;
1388 new_event.type = GDK_LEAVE_NOTIFY;
1390 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1391 new_event.crossing.subwindow = NULL;
1392 canvas->in_repick = TRUE;
1393 retval = emit_event (canvas, &new_event);
1394 canvas->in_repick = FALSE;
1395 }
1397 if (canvas->gen_all_enter_events == false) {
1398 // new_current_item may have been set to NULL during the call to
1399 // emit_event() above
1400 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1401 canvas->left_grabbed_item = TRUE;
1402 return retval;
1403 }
1404 }
1406 /* Handle the rest of cases */
1408 canvas->left_grabbed_item = FALSE;
1409 canvas->current_item = canvas->new_current_item;
1411 if (canvas->current_item != NULL) {
1412 GdkEvent new_event;
1414 new_event = canvas->pick_event;
1415 new_event.type = GDK_ENTER_NOTIFY;
1416 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1417 new_event.crossing.subwindow = NULL;
1418 retval = emit_event (canvas, &new_event);
1419 }
1421 return retval;
1422 }
1424 /**
1425 * Button event handler for the canvas.
1426 */
1427 static gint
1428 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1429 {
1430 SPCanvas *canvas = SP_CANVAS (widget);
1432 int retval = FALSE;
1434 /* dispatch normally regardless of the event's window if an item has
1435 has a pointer grab in effect */
1436 if (!canvas->grabbed_item &&
1437 event->window != SP_CANVAS_WINDOW (canvas))
1438 return retval;
1440 int mask;
1441 switch (event->button) {
1442 case 1:
1443 mask = GDK_BUTTON1_MASK;
1444 break;
1445 case 2:
1446 mask = GDK_BUTTON2_MASK;
1447 break;
1448 case 3:
1449 mask = GDK_BUTTON3_MASK;
1450 break;
1451 case 4:
1452 mask = GDK_BUTTON4_MASK;
1453 break;
1454 case 5:
1455 mask = GDK_BUTTON5_MASK;
1456 break;
1457 default:
1458 mask = 0;
1459 }
1461 switch (event->type) {
1462 case GDK_BUTTON_PRESS:
1463 case GDK_2BUTTON_PRESS:
1464 case GDK_3BUTTON_PRESS:
1465 /* Pick the current item as if the button were not pressed, and
1466 * then process the event.
1467 */
1468 canvas->state = event->state;
1469 pick_current_item (canvas, (GdkEvent *) event);
1470 canvas->state ^= mask;
1471 retval = emit_event (canvas, (GdkEvent *) event);
1472 break;
1474 case GDK_BUTTON_RELEASE:
1475 /* Process the event as if the button were pressed, then repick
1476 * after the button has been released
1477 */
1478 canvas->state = event->state;
1479 retval = emit_event (canvas, (GdkEvent *) event);
1480 event->state ^= mask;
1481 canvas->state = event->state;
1482 pick_current_item (canvas, (GdkEvent *) event);
1483 event->state ^= mask;
1484 break;
1486 default:
1487 g_assert_not_reached ();
1488 }
1490 return retval;
1491 }
1493 /**
1494 * Scroll event handler for the canvas.
1495 *
1496 * \todo FIXME: generate motion events to re-select items.
1497 */
1498 static gint
1499 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1500 {
1501 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1502 }
1504 /**
1505 * Motion event handler for the canvas.
1506 */
1507 static int
1508 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1509 {
1510 SPCanvas *canvas = SP_CANVAS (widget);
1512 if (event->window != SP_CANVAS_WINDOW (canvas))
1513 return FALSE;
1515 if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1516 gint x, y;
1517 gdk_window_get_pointer (widget->window, &x, &y, NULL);
1518 event->x = x;
1519 event->y = y;
1520 }
1522 canvas->state = event->state;
1523 pick_current_item (canvas, (GdkEvent *) event);
1525 return emit_event (canvas, (GdkEvent *) event);
1526 }
1528 static void
1529 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)
1530 {
1531 GtkWidget *widget = GTK_WIDGET (canvas);
1533 SPCanvasBuf buf;
1534 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1535 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1536 } else {
1537 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1538 }
1540 buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1541 buf.rect.x0 = x0;
1542 buf.rect.y0 = y0;
1543 buf.rect.x1 = x1;
1544 buf.rect.y1 = y1;
1545 buf.visible_rect.x0 = draw_x1;
1546 buf.visible_rect.y0 = draw_y1;
1547 buf.visible_rect.x1 = draw_x2;
1548 buf.visible_rect.y1 = draw_y2;
1549 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1550 buf.bg_color = (((color->red & 0xff00) << 8)
1551 | (color->green & 0xff00)
1552 | (color->blue >> 8));
1553 buf.is_empty = true;
1555 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1556 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1557 }
1559 if (buf.is_empty) {
1560 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1561 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1562 canvas->pixmap_gc,
1563 TRUE,
1564 x0 - canvas->x0, y0 - canvas->y0,
1565 x1 - x0, y1 - y0);
1566 } else {
1567 /*
1568 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1569 // Why this must not be done currently:
1570 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1571 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)
1572 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels,
1573 // we need more bufs to paint a given area and as a result it's even a bit slower
1575 cairo_surface_t* cst = cairo_image_surface_create_for_data (
1576 buf.buf,
1577 CAIRO_FORMAT_RGB24, // unpacked, i.e. 32 bits! one byte is unused
1578 x1 - x0, y1 - y0,
1579 buf.buf_rowstride
1580 );
1581 cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1582 cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1583 cairo_paint (ct);
1584 cairo_destroy (ct);
1585 cairo_surface_finish (cst);
1586 cairo_surface_destroy (cst);
1587 */
1589 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1590 canvas->pixmap_gc,
1591 x0 - canvas->x0, y0 - canvas->y0,
1592 x1 - x0, y1 - y0,
1593 GDK_RGB_DITHER_MAX,
1594 buf.buf,
1595 sw * 3,
1596 x0 - canvas->x0, y0 - canvas->y0);
1597 }
1599 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1600 nr_pixelstore_256K_free (buf.buf);
1601 } else {
1602 nr_pixelstore_1M_free (buf.buf);
1603 }
1604 }
1606 /* Paint the given rect, while updating canvas->redraw_aborted and running iterations after each
1607 * buffer; make sure canvas->redraw_aborted never goes past aborted_limit (used for 2-rect
1608 * optimized repaint)
1609 */
1610 static int
1611 sp_canvas_paint_rect_internal (SPCanvas *canvas, NRRectL *rect, NR::ICoord *x_aborted_limit, NR::ICoord *y_aborted_limit)
1612 {
1613 int draw_x1 = rect->x0;
1614 int draw_x2 = rect->x1;
1615 int draw_y1 = rect->y0;
1616 int draw_y2 = rect->y1;
1618 // Here we'll store the time it took to draw the slowest buffer of this paint.
1619 glong slowest_buffer = 0;
1621 // Find the optimal buffer dimensions
1622 int bw = draw_x2 - draw_x1;
1623 int bh = draw_y2 - draw_y1;
1624 if ((bw < 1) || (bh < 1))
1625 return 0;
1627 int sw, sh; // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1628 if (canvas->rendermode != RENDERMODE_OUTLINE) { // use 256K as a compromise to not slow down gradients
1629 /* 256K is the cached buffer and we need 3 channels */
1630 if (bw * bh < 87381) { // 256K/3
1631 // We can go with single buffer
1632 sw = bw;
1633 sh = bh;
1634 } else if (bw <= (16 * 341)) {
1635 // Go with row buffer
1636 sw = bw;
1637 sh = 87381 / bw;
1638 } else if (bh <= (16 * 256)) {
1639 // Go with column buffer
1640 sw = 87381 / bh;
1641 sh = bh;
1642 } else {
1643 sw = 341;
1644 sh = 256;
1645 }
1646 } else { // paths only, so 1M works faster
1647 /* 1M is the cached buffer and we need 3 channels */
1648 if (bw * bh < 349525) { // 1M/3
1649 // We can go with single buffer
1650 sw = bw;
1651 sh = bh;
1652 } else if (bw <= (16 * 682)) {
1653 // Go with row buffer
1654 sw = bw;
1655 sh = 349525 / bw;
1656 } else if (bh <= (16 * 512)) {
1657 // Go with column buffer
1658 sw = 349525 / bh;
1659 sh = bh;
1660 } else {
1661 sw = 682;
1662 sh = 512;
1663 }
1664 }
1666 // Will this paint require more than one buffer?
1667 bool multiple_buffers = (((draw_y2 - draw_y1) > sh) || ((draw_x2 - draw_x1) > sw)); // or two_rects
1669 // remember the counter during this paint
1670 long this_count = canvas->redraw_count;
1672 // Time values to measure each buffer's paint time
1673 GTimeVal tstart, tfinish;
1675 // paint from the corner nearest the mouse pointer
1677 gint x, y;
1678 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1679 NR::Point pw = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1681 bool reverse_x = (pw[NR::X] > ((draw_x2 + draw_x1) / 2));
1682 bool reverse_y = (pw[NR::Y] > ((draw_y2 + draw_y1) / 2));
1684 if ((bw > bh) && (sh > sw)) {
1685 int t = sw; sw = sh; sh = t;
1686 }
1688 // This is the main loop which corresponds to the visible left-to-right, top-to-bottom drawing
1689 // of screen blocks (buffers).
1690 for (int y0 = draw_y1; y0 < draw_y2; y0 += sh) {
1691 int y1 = MIN (y0 + sh, draw_y2);
1692 for (int x0 = draw_x1; x0 < draw_x2; x0 += sw) {
1693 int x1 = MIN (x0 + sw, draw_x2);
1695 int dx0 = x0;
1696 int dx1 = x1;
1697 int dy0 = y0;
1698 int dy1 = y1;
1700 if (reverse_x) {
1701 dx0 = (draw_x2 - (x0 + sw)) + draw_x1;
1702 dx0 = MAX (dx0, draw_x1);
1703 dx1 = (draw_x2 - x0) + draw_x1;
1704 dx1 = MIN (dx1, draw_x2);
1705 }
1706 if (reverse_y) {
1707 dy0 = (draw_y2 - (y0 + sh)) + draw_y1;
1708 dy0 = MAX (dy0, draw_y1);
1709 dy1 = (draw_y2 - y0) + draw_y1;
1710 dy1 = MIN (dy1, draw_y2);
1711 }
1713 // SMOOTH SCROLLING: if we are scrolling, process pending events even before doing any rendering.
1714 // This allows for scrolling smoothly without hiccups. Any accumulated redraws will be made
1715 // when scrolling stops. The scrolling flag is set by sp_canvas_scroll_to for each scroll and zeroed
1716 // here for each redraw, to ensure it never gets stuck.
1718 // OPTIMIZATION IDEA: if drawing is really slow (as measured by canvas->slowest
1719 // buffer), do the same - process some events even before we paint any buffers
1721 if (canvas->is_scrolling) {
1722 while (Gtk::Main::events_pending()) { // process any events
1723 Gtk::Main::iteration(false);
1724 }
1725 canvas->is_scrolling = false;
1726 if (this_count != canvas->redraw_count) { // if there was redraw,
1727 return 1; // interrupt this one
1728 }
1729 }
1731 // Paint one buffer; measure how long it takes.
1732 g_get_current_time (&tstart);
1733 sp_canvas_paint_single_buffer (canvas, dx0, dy0, dx1, dy1, draw_x1, draw_y1, draw_x2, draw_y2, sw);
1734 g_get_current_time (&tfinish);
1736 // Remember the slowest_buffer of this paint.
1737 glong this_buffer = (tfinish.tv_sec - tstart.tv_sec) * 1000000 + (tfinish.tv_usec - tstart.tv_usec);
1738 if (this_buffer > slowest_buffer)
1739 slowest_buffer = this_buffer;
1741 // After each successful buffer, reduce the rect remaining to redraw by what is already redrawn
1742 if (x1 >= draw_x2) {
1743 if (reverse_y) {
1744 if (canvas->redraw_aborted.y1 > dy0) { canvas->redraw_aborted.y1 = dy0; }
1745 } else {
1746 if (canvas->redraw_aborted.y0 < y1) { canvas->redraw_aborted.y0 = y1; }
1747 }
1748 }
1750 if (y1 >= draw_y2) {
1751 if (reverse_x) {
1752 if (canvas->redraw_aborted.x1 > dx0) { canvas->redraw_aborted.x1 = dx0; }
1753 } else {
1754 if (canvas->redraw_aborted.x0 < x1) { canvas->redraw_aborted.x0 = x1; }
1755 }
1756 }
1758 // INTERRUPTIBLE DISPLAY:
1759 // Process events that may have arrived while we were busy drawing;
1760 // only if we're drawing multiple buffers, and only if this one was not very fast,
1761 // and only if we're allowed to interrupt this redraw
1762 bool ok_to_interrupt = (multiple_buffers && this_buffer > 25000);
1763 if (ok_to_interrupt && (canvas->forced_redraw_limit != -1)) {
1764 ok_to_interrupt = (canvas->forced_redraw_count < canvas->forced_redraw_limit);
1765 }
1767 if (ok_to_interrupt) {
1768 // Run at most max_iterations of the main loop; we cannot process ALL events
1769 // here because some things (e.g. rubberband) flood with dirtying events but will
1770 // not redraw themselves
1771 int max_iterations = 10;
1772 int iterations = 0;
1773 while (Gtk::Main::events_pending() && iterations++ < max_iterations) {
1774 Gtk::Main::iteration(false);
1775 // If one of the iterations has redrawn by itself, abort
1776 if (this_count != canvas->redraw_count) {
1777 canvas->slowest_buffer = slowest_buffer;
1778 if (canvas->forced_redraw_limit != -1) {
1779 canvas->forced_redraw_count++;
1780 }
1781 return 1; // interrupted
1782 }
1783 }
1785 // If not aborted so far, check if the events set redraw or update flags;
1786 // if so, force update and abort
1787 if (canvas->need_redraw || canvas->need_update) {
1788 canvas->slowest_buffer = slowest_buffer;
1789 if (canvas->forced_redraw_limit != -1) {
1790 canvas->forced_redraw_count++;
1791 }
1792 do_update (canvas);
1793 return 1; // interrupted
1794 }
1795 }
1796 }
1797 }
1799 // Remember the slowest buffer of this paint in canvas
1800 canvas->slowest_buffer = slowest_buffer;
1802 return 0; // finished
1803 }
1806 /**
1807 * Helper that draws a specific rectangular part of the canvas.
1808 */
1809 static void
1810 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1811 {
1812 g_return_if_fail (!canvas->need_update);
1814 // Monotonously increment the canvas-global counter on each paint. This will let us find out
1815 // when a new paint happened in event processing during this paint, so we can abort it.
1816 canvas->redraw_count++;
1818 NRRectL rect;
1819 rect.x0 = xx0;
1820 rect.x1 = xx1;
1821 rect.y0 = yy0;
1822 rect.y1 = yy1;
1824 // Clip rect-to-draw by the current visible area
1825 rect.x0 = MAX (rect.x0, canvas->x0);
1826 rect.y0 = MAX (rect.y0, canvas->y0);
1827 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1828 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1830 // Clip rect-aborted-last-time by the current visible area
1831 canvas->redraw_aborted.x0 = MAX (canvas->redraw_aborted.x0, canvas->x0);
1832 canvas->redraw_aborted.y0 = MAX (canvas->redraw_aborted.y0, canvas->y0);
1833 canvas->redraw_aborted.x1 = MIN (canvas->redraw_aborted.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1834 canvas->redraw_aborted.y1 = MIN (canvas->redraw_aborted.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1836 if (canvas->redraw_aborted.x0 < canvas->redraw_aborted.x1 && canvas->redraw_aborted.y0 < canvas->redraw_aborted.y1) {
1837 // There was an aborted redraw last time, now we need to redraw BOTH it and the new rect.
1839 // save the old aborted rect in case we decide to paint it separately (see below)
1840 NRRectL aborted = canvas->redraw_aborted;
1842 // calculate the rectangle union of the both rects (the smallest rectangle which covers both)
1843 NRRectL nion;
1844 nr_rect_l_union (&nion, &rect, &aborted);
1846 // subtract one of the rects-to-draw from the other (the smallest rectangle which covers
1847 // all of the first not covered by the second)
1848 NRRectL rect_minus_aborted;
1849 nr_rect_l_subtract (&rect_minus_aborted, &rect, &aborted);
1851 // Initially, the rect to redraw later (in case we're aborted) is the same as the union of both rects
1852 canvas->redraw_aborted = nion;
1854 // calculate areas of the three rects
1855 if ((nr_rect_l_area(&rect_minus_aborted) + nr_rect_l_area(&aborted)) * 1.2 < nr_rect_l_area(&nion)) {
1856 // If the summary area of the two rects is significantly (at least by 20%) less than
1857 // the area of their rectangular union, it makes sense to paint the two rects
1858 // separately instead of painting their union. This gives a significant speedup when,
1859 // for example, your current canvas is almost painted, with only a strip at bottom
1860 // left, and at that moment you abort it by scrolling down which reveals a new strip at
1861 // the top. Straightforward painting of the union of the aborted rect and the new rect
1862 // will have to repaint the entire canvas! By contrast, the optimized approach below
1863 // paints the two narrow strips in order which is much faster.
1865 // find out which rect to draw first - compare them first by y then by x of the top left corners
1866 NRRectL *first;
1867 NRRectL *second;
1868 if (rect.y0 == aborted.y0) {
1869 if (rect.x0 < aborted.x0) {
1870 first = ▭
1871 second = &aborted;
1872 } else {
1873 second = ▭
1874 first = &aborted;
1875 }
1876 } else if (rect.y0 < aborted.y0) {
1877 first = ▭
1878 second = &aborted;
1879 } else {
1880 second = ▭
1881 first = &aborted;
1882 }
1884 NRRectL second_minus_first;
1885 nr_rect_l_subtract (&second_minus_first, second, first);
1887 // paint the first rect;
1888 if (sp_canvas_paint_rect_internal (canvas, first, &(second_minus_first.x0), &(second_minus_first.y0))) {
1889 // aborted!
1890 return;
1891 }
1893 // if not aborted, assign (second rect minus first) as the new redraw_aborted and paint the same
1894 canvas->redraw_aborted = second_minus_first;
1895 if (sp_canvas_paint_rect_internal (canvas, &second_minus_first, NULL, NULL)) {
1896 return; // aborted
1897 }
1899 } else {
1900 // no need for separate drawing, just draw the union as one rect
1901 if (sp_canvas_paint_rect_internal (canvas, &nion, NULL, NULL)) {
1902 return; // aborted
1903 }
1904 }
1905 } else {
1906 // Nothing was aborted last time, just draw the rect we're given
1908 // Initially, the rect to redraw later (in case we're aborted) is the same as the one we're going to draw now.
1909 canvas->redraw_aborted = rect;
1911 if (sp_canvas_paint_rect_internal (canvas, &rect, NULL, NULL)) {
1912 return; // aborted
1913 }
1914 }
1916 // we've had a full unaborted redraw, reset the full redraw counter
1917 if (canvas->forced_redraw_limit != -1) {
1918 canvas->forced_redraw_count = 0;
1919 }
1920 }
1922 /**
1923 * Force a full redraw after a specified number of interrupted redraws
1924 */
1925 void
1926 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1927 g_return_if_fail(canvas != NULL);
1929 canvas->forced_redraw_limit = count;
1930 canvas->forced_redraw_count = 0;
1931 }
1933 /**
1934 * End forced full redraw requests
1935 */
1936 void
1937 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1938 g_return_if_fail(canvas != NULL);
1940 canvas->forced_redraw_limit = -1;
1941 }
1943 /**
1944 * The canvas widget's expose callback.
1945 */
1946 static gint
1947 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1948 {
1949 SPCanvas *canvas = SP_CANVAS (widget);
1951 if (!GTK_WIDGET_DRAWABLE (widget) ||
1952 (event->window != SP_CANVAS_WINDOW (canvas)))
1953 return FALSE;
1955 int n_rects;
1956 GdkRectangle *rects;
1957 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1959 for (int i = 0; i < n_rects; i++) {
1960 NRRectL rect;
1962 rect.x0 = rects[i].x + canvas->x0;
1963 rect.y0 = rects[i].y + canvas->y0;
1964 rect.x1 = rect.x0 + rects[i].width;
1965 rect.y1 = rect.y0 + rects[i].height;
1967 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1968 }
1970 if (n_rects > 0)
1971 g_free (rects);
1973 return FALSE;
1974 }
1976 /**
1977 * The canvas widget's keypress callback.
1978 */
1979 static gint
1980 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1981 {
1982 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1983 }
1985 /**
1986 * Crossing event handler for the canvas.
1987 */
1988 static gint
1989 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1990 {
1991 SPCanvas *canvas = SP_CANVAS (widget);
1993 if (event->window != SP_CANVAS_WINDOW (canvas))
1994 return FALSE;
1996 canvas->state = event->state;
1997 return pick_current_item (canvas, (GdkEvent *) event);
1998 }
2000 /**
2001 * Focus in handler for the canvas.
2002 */
2003 static gint
2004 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2005 {
2006 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2008 SPCanvas *canvas = SP_CANVAS (widget);
2010 if (canvas->focused_item) {
2011 return emit_event (canvas, (GdkEvent *) event);
2012 } else {
2013 return FALSE;
2014 }
2015 }
2017 /**
2018 * Focus out handler for the canvas.
2019 */
2020 static gint
2021 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2022 {
2023 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2025 SPCanvas *canvas = SP_CANVAS (widget);
2027 if (canvas->focused_item)
2028 return emit_event (canvas, (GdkEvent *) event);
2029 else
2030 return FALSE;
2031 }
2033 /**
2034 * Helper that repaints the areas in the canvas that need it.
2035 */
2036 static int
2037 paint (SPCanvas *canvas)
2038 {
2039 if (canvas->need_update) {
2040 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2041 canvas->need_update = FALSE;
2042 }
2044 if (!canvas->need_redraw)
2045 return TRUE;
2047 GtkWidget const *widget = GTK_WIDGET(canvas);
2048 int const canvas_x1 = canvas->x0 + widget->allocation.width;
2049 int const canvas_y1 = canvas->y0 + widget->allocation.height;
2051 bool dirty = false;
2053 int pl = canvas->tRight, pr = canvas->tLeft, pt = canvas->tBottom, pb = canvas->tTop; // start with "inverted" tile rect
2055 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2056 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2058 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2060 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2061 dirty = true;
2062 // make (pl..pr)x(pt..pb) the minimal rect covering all dirtied tiles
2063 if ( i < pl ) pl = i;
2064 if ( i+1 > pr ) pr = i+1;
2065 if ( j < pt ) pt = j;
2066 if ( j+1 > pb ) pb = j+1;
2067 }
2069 canvas->tiles[tile_index] = 0; // undirty this tile
2070 }
2071 }
2073 canvas->need_redraw = FALSE;
2075 if ( dirty ) {
2076 NRRectL topaint;
2077 topaint.x0 = MAX (pl*TILE_SIZE, canvas->x0);
2078 topaint.y0 = MAX (pt*TILE_SIZE, canvas->y0);
2079 topaint.x1 = MIN (pr*TILE_SIZE, canvas_x1);
2080 topaint.y1 = MIN (pb*TILE_SIZE, canvas_y1);
2081 if ((topaint.x0 < topaint.x1) && (topaint.y0 < topaint.y1)) {
2082 sp_canvas_paint_rect (canvas, topaint.x0, topaint.y0, topaint.x1, topaint.y1);
2083 }
2084 }
2086 return TRUE;
2087 }
2089 /**
2090 * Helper that invokes update, paint, and repick on canvas.
2091 */
2092 static int
2093 do_update (SPCanvas *canvas)
2094 {
2095 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2096 return TRUE;
2098 /* Cause the update if necessary */
2099 if (canvas->need_update) {
2100 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2101 canvas->need_update = FALSE;
2102 }
2104 /* Paint if able to */
2105 if (GTK_WIDGET_DRAWABLE (canvas)) {
2106 return paint (canvas);
2107 }
2109 /* Pick new current item */
2110 while (canvas->need_repick) {
2111 canvas->need_repick = FALSE;
2112 pick_current_item (canvas, &canvas->pick_event);
2113 }
2115 return TRUE;
2116 }
2118 /**
2119 * Idle handler for the canvas that deals with pending updates and redraws.
2120 */
2121 static gint
2122 idle_handler (gpointer data)
2123 {
2124 GDK_THREADS_ENTER ();
2126 SPCanvas *canvas = SP_CANVAS (data);
2128 const int ret = do_update (canvas);
2130 if (ret) {
2131 /* Reset idle id */
2132 canvas->idle_id = 0;
2133 }
2135 GDK_THREADS_LEAVE ();
2137 return !ret;
2138 }
2140 /**
2141 * Convenience function to add an idle handler to a canvas.
2142 */
2143 static void
2144 add_idle (SPCanvas *canvas)
2145 {
2146 if (canvas->idle_id != 0)
2147 return;
2149 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2150 }
2152 /**
2153 * Returns the root group of the specified canvas.
2154 */
2155 SPCanvasGroup *
2156 sp_canvas_root (SPCanvas *canvas)
2157 {
2158 g_return_val_if_fail (canvas != NULL, NULL);
2159 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2161 return SP_CANVAS_GROUP (canvas->root);
2162 }
2164 /**
2165 * Scrolls canvas to specific position.
2166 */
2167 void
2168 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2169 {
2170 g_return_if_fail (canvas != NULL);
2171 g_return_if_fail (SP_IS_CANVAS (canvas));
2173 int ix = (int) (cx + 0.5);
2174 int iy = (int) (cy + 0.5);
2175 int dx = ix - canvas->x0;
2176 int dy = iy - canvas->y0;
2178 canvas->dx0 = cx;
2179 canvas->dy0 = cy;
2180 canvas->x0 = ix;
2181 canvas->y0 = iy;
2183 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2185 if (!clear) {
2186 // scrolling without zoom; redraw only the newly exposed areas
2187 if ((dx != 0) || (dy != 0)) {
2188 canvas->is_scrolling = is_scrolling;
2189 if (GTK_WIDGET_REALIZED (canvas)) {
2190 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2191 }
2192 }
2193 } else {
2194 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2195 }
2196 }
2198 /**
2199 * Updates canvas if necessary.
2200 */
2201 void
2202 sp_canvas_update_now (SPCanvas *canvas)
2203 {
2204 g_return_if_fail (canvas != NULL);
2205 g_return_if_fail (SP_IS_CANVAS (canvas));
2207 if (!(canvas->need_update ||
2208 canvas->need_redraw))
2209 return;
2211 remove_idle (canvas);
2212 do_update (canvas);
2213 }
2215 /**
2216 * Update callback for canvas widget.
2217 */
2218 static void
2219 sp_canvas_request_update (SPCanvas *canvas)
2220 {
2221 canvas->need_update = TRUE;
2222 add_idle (canvas);
2223 }
2225 /**
2226 * Forces redraw of rectangular canvas area.
2227 */
2228 void
2229 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2230 {
2231 NRRectL bbox;
2232 NRRectL visible;
2233 NRRectL clip;
2235 g_return_if_fail (canvas != NULL);
2236 g_return_if_fail (SP_IS_CANVAS (canvas));
2238 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2239 if ((x0 >= x1) || (y0 >= y1)) return;
2241 bbox.x0 = x0;
2242 bbox.y0 = y0;
2243 bbox.x1 = x1;
2244 bbox.y1 = y1;
2246 visible.x0 = canvas->x0;
2247 visible.y0 = canvas->y0;
2248 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2249 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2251 nr_rect_l_intersect (&clip, &bbox, &visible);
2253 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2254 add_idle (canvas);
2255 }
2257 /**
2258 * Sets world coordinates from win and canvas.
2259 */
2260 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2261 {
2262 g_return_if_fail (canvas != NULL);
2263 g_return_if_fail (SP_IS_CANVAS (canvas));
2265 if (worldx) *worldx = canvas->x0 + winx;
2266 if (worldy) *worldy = canvas->y0 + winy;
2267 }
2269 /**
2270 * Sets win coordinates from world and canvas.
2271 */
2272 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2273 {
2274 g_return_if_fail (canvas != NULL);
2275 g_return_if_fail (SP_IS_CANVAS (canvas));
2277 if (winx) *winx = worldx - canvas->x0;
2278 if (winy) *winy = worldy - canvas->y0;
2279 }
2281 /**
2282 * Converts point from win to world coordinates.
2283 */
2284 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2285 {
2286 g_assert (canvas != NULL);
2287 g_assert (SP_IS_CANVAS (canvas));
2289 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2290 }
2292 /**
2293 * Converts point from world to win coordinates.
2294 */
2295 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2296 {
2297 g_assert (canvas != NULL);
2298 g_assert (SP_IS_CANVAS (canvas));
2300 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2301 }
2303 /**
2304 * Returns true if point given in world coordinates is inside window.
2305 */
2306 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2307 {
2308 g_assert( canvas != NULL );
2309 g_assert(SP_IS_CANVAS(canvas));
2311 using NR::X;
2312 using NR::Y;
2313 GtkWidget const &w = *GTK_WIDGET(canvas);
2314 return ( ( canvas->x0 <= world[X] ) &&
2315 ( canvas->y0 <= world[Y] ) &&
2316 ( world[X] < canvas->x0 + w.allocation.width ) &&
2317 ( world[Y] < canvas->y0 + w.allocation.height ) );
2318 }
2320 /**
2321 * Return canvas window coordinates as NR::Rect.
2322 */
2323 NR::Rect SPCanvas::getViewbox() const
2324 {
2325 GtkWidget const *w = GTK_WIDGET(this);
2327 return NR::Rect(NR::Point(dx0, dy0),
2328 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2329 }
2331 inline int sp_canvas_tile_floor(int x)
2332 {
2333 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2334 }
2336 inline int sp_canvas_tile_ceil(int x)
2337 {
2338 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2339 }
2341 /**
2342 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2343 */
2344 void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2345 {
2346 if ( nl >= nr || nt >= nb ) {
2347 if ( canvas->tiles ) g_free(canvas->tiles);
2348 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2349 canvas->tileH=canvas->tileV=0;
2350 canvas->tiles=NULL;
2351 return;
2352 }
2353 int tl=sp_canvas_tile_floor(nl);
2354 int tt=sp_canvas_tile_floor(nt);
2355 int tr=sp_canvas_tile_ceil(nr);
2356 int tb=sp_canvas_tile_ceil(nb);
2358 int nh = tr-tl, nv = tb-tt;
2359 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2360 for (int i=tl; i<tr; i++) {
2361 for (int j=tt; j<tb; j++) {
2362 int ind = (i-tl) + (j-tt)*nh;
2363 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2364 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2365 } else {
2366 ntiles[ind]=0; // newly exposed areas get 0
2367 }
2368 }
2369 }
2370 if ( canvas->tiles ) g_free(canvas->tiles);
2371 canvas->tiles=ntiles;
2372 canvas->tLeft=tl;
2373 canvas->tTop=tt;
2374 canvas->tRight=tr;
2375 canvas->tBottom=tb;
2376 canvas->tileH=nh;
2377 canvas->tileV=nv;
2378 }
2380 /**
2381 * Helper that marks specific canvas rectangle for redraw by dirtying its tiles
2382 */
2383 void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2384 {
2385 if ( nl >= nr || nt >= nb ) {
2386 return;
2387 }
2388 int tl=sp_canvas_tile_floor(nl);
2389 int tt=sp_canvas_tile_floor(nt);
2390 int tr=sp_canvas_tile_ceil(nr);
2391 int tb=sp_canvas_tile_ceil(nb);
2392 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2393 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2394 if ( tr > canvas->tRight ) tr=canvas->tRight;
2395 if ( tt < canvas->tTop ) tt=canvas->tTop;
2396 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2398 canvas->need_redraw = TRUE;
2400 for (int i=tl; i<tr; i++) {
2401 for (int j=tt; j<tb; j++) {
2402 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = 1;
2403 }
2404 }
2405 }
2408 /*
2409 Local Variables:
2410 mode:c++
2411 c-file-style:"stroustrup"
2412 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2413 indent-tabs-mode:nil
2414 fill-column:99
2415 End:
2416 */
2417 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :