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 enum {
39 RENDERMODE_NORMAL,
40 RENDERMODE_NOAA,
41 RENDERMODE_OUTLINE
42 };
44 const gint sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
46 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
48 enum {
49 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
50 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
51 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
52 };
54 /**
55 * A group of Items.
56 */
57 struct SPCanvasGroup {
58 SPCanvasItem item;
60 GList *items, *last;
61 };
63 /**
64 * The SPCanvasGroup vtable.
65 */
66 struct SPCanvasGroupClass {
67 SPCanvasItemClass parent_class;
68 };
70 /**
71 * The SPCanvas vtable.
72 */
73 struct SPCanvasClass {
74 GtkWidgetClass parent_class;
75 };
77 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
78 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
80 /* SPCanvasItem */
82 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
85 static void sp_canvas_request_update (SPCanvas *canvas);
87 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
88 static void sp_canvas_item_init (SPCanvasItem *item);
89 static void sp_canvas_item_dispose (GObject *object);
90 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args);
92 static int emit_event (SPCanvas *canvas, GdkEvent *event);
94 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
96 static GtkObjectClass *item_parent_class;
98 /**
99 * Registers the SPCanvasItem class with Glib and returns its type number.
100 */
101 GType
102 sp_canvas_item_get_type (void)
103 {
104 static GType type = 0;
105 if (!type) {
106 static const GTypeInfo info = {
107 sizeof (SPCanvasItemClass),
108 NULL, NULL,
109 (GClassInitFunc) sp_canvas_item_class_init,
110 NULL, NULL,
111 sizeof (SPCanvasItem),
112 0,
113 (GInstanceInitFunc) sp_canvas_item_init,
114 NULL
115 };
116 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
117 }
119 return type;
120 }
122 /**
123 * Initializes the SPCanvasItem vtable and the "event" signal.
124 */
125 static void
126 sp_canvas_item_class_init (SPCanvasItemClass *klass)
127 {
128 GObjectClass *object_class = (GObjectClass *) klass;
130 /* fixme: Derive from GObject */
131 item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
133 item_signals[ITEM_EVENT] = g_signal_new ("event",
134 G_TYPE_FROM_CLASS (klass),
135 G_SIGNAL_RUN_LAST,
136 G_STRUCT_OFFSET (SPCanvasItemClass, event),
137 NULL, NULL,
138 sp_marshal_BOOLEAN__POINTER,
139 G_TYPE_BOOLEAN, 1,
140 GDK_TYPE_EVENT);
142 object_class->dispose = sp_canvas_item_dispose;
143 }
145 /**
146 * Callback for initialization of SPCanvasItem.
147 */
148 static void
149 sp_canvas_item_init (SPCanvasItem *item)
150 {
151 item->flags |= SP_CANVAS_ITEM_VISIBLE;
152 item->xform = NR::Matrix(NR::identity());
153 }
155 /**
156 * Constructs new SPCanvasItem on SPCanvasGroup.
157 */
158 SPCanvasItem *
159 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, const gchar *first_arg_name, ...)
160 {
161 va_list args;
163 g_return_val_if_fail (parent != NULL, NULL);
164 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
165 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
167 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
169 va_start (args, first_arg_name);
170 sp_canvas_item_construct (item, parent, first_arg_name, args);
171 va_end (args);
173 return item;
174 }
176 /**
177 * Sets up the newly created SPCanvasItem.
178 *
179 * We make it static for encapsulation reasons since it was nowhere used.
180 */
181 static void
182 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args)
183 {
184 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
185 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
187 item->parent = SP_CANVAS_ITEM (parent);
188 item->canvas = item->parent->canvas;
190 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
192 group_add (SP_CANVAS_GROUP (item->parent), item);
194 sp_canvas_item_request_update (item);
195 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
196 item->canvas->need_repick = TRUE;
197 }
199 /**
200 * Helper function that requests redraw only if item's visible flag is set.
201 */
202 static void
203 redraw_if_visible (SPCanvasItem *item)
204 {
205 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
206 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
207 }
208 }
210 /**
211 * Callback that removes item from all referers and destroys it.
212 */
213 static void
214 sp_canvas_item_dispose (GObject *object)
215 {
216 SPCanvasItem *item = SP_CANVAS_ITEM (object);
218 redraw_if_visible (item);
219 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
221 if (item == item->canvas->current_item) {
222 item->canvas->current_item = NULL;
223 item->canvas->need_repick = TRUE;
224 }
226 if (item == item->canvas->new_current_item) {
227 item->canvas->new_current_item = NULL;
228 item->canvas->need_repick = TRUE;
229 }
231 if (item == item->canvas->grabbed_item) {
232 item->canvas->grabbed_item = NULL;
233 gdk_pointer_ungrab (GDK_CURRENT_TIME);
234 }
236 if (item == item->canvas->focused_item)
237 item->canvas->focused_item = NULL;
239 if (item->parent) {
240 group_remove (SP_CANVAS_GROUP (item->parent), item);
241 }
243 G_OBJECT_CLASS (item_parent_class)->dispose (object);
244 }
246 /**
247 * Helper function to update item and its children.
248 *
249 * NB! affine is parent2canvas.
250 */
251 static void
252 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
253 {
254 /* Apply the child item's transform */
255 NR::Matrix child_affine = item->xform * affine;
257 /* apply object flags to child flags */
258 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
260 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
261 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
263 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
264 child_flags |= SP_CANVAS_UPDATE_AFFINE;
266 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
267 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
268 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
269 }
271 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
272 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
273 }
275 /**
276 * Helper function to invoke the point method of the item.
277 *
278 * The argument x, y should be in the parent's item-relative coordinate
279 * system. This routine applies the inverse of the item's transform,
280 * maintaining the affine invariant.
281 */
282 static double
283 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
284 {
285 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
286 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
288 return NR_HUGE;
289 }
291 /**
292 * Makes the item's affine transformation matrix be equal to the specified
293 * matrix.
294 *
295 * @item: A canvas item.
296 * @affine: An affine transformation matrix.
297 */
298 void
299 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const& affine)
300 {
301 item->xform = affine;
303 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
304 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
305 if (item->parent != NULL) {
306 sp_canvas_item_request_update (item->parent);
307 } else {
308 sp_canvas_request_update (item->canvas);
309 }
310 }
312 item->canvas->need_repick = TRUE;
313 }
315 /**
316 * Convenience function to reorder items in a group's child list.
317 *
318 * This puts the specified link after the "before" link.
319 */
320 static void
321 put_item_after (GList *link, GList *before)
322 {
323 if (link == before)
324 return;
326 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
328 if (before == NULL) {
329 if (link == parent->items) return;
331 link->prev->next = link->next;
333 if (link->next) {
334 link->next->prev = link->prev;
335 } else {
336 parent->last = link->prev;
337 }
339 link->prev = before;
340 link->next = parent->items;
341 link->next->prev = link;
342 parent->items = link;
343 } else {
344 if ((link == parent->last) && (before == parent->last->prev))
345 return;
347 if (link->next)
348 link->next->prev = link->prev;
350 if (link->prev)
351 link->prev->next = link->next;
352 else {
353 parent->items = link->next;
354 parent->items->prev = NULL;
355 }
357 link->prev = before;
358 link->next = before->next;
360 link->prev->next = link;
362 if (link->next)
363 link->next->prev = link;
364 else
365 parent->last = link;
366 }
367 }
370 /**
371 * Raises the item in its parent's stack by the specified number of positions.
372 *
373 * \param item A canvas item.
374 * \param positions Number of steps to raise the item.
375 *
376 * If the number of positions is greater than the distance to the top of the
377 * stack, then the item is put at the top.
378 */
379 void
380 sp_canvas_item_raise (SPCanvasItem *item, int positions)
381 {
382 g_return_if_fail (item != NULL);
383 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
384 g_return_if_fail (positions >= 0);
386 if (!item->parent || positions == 0)
387 return;
389 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
390 GList *link = g_list_find (parent->items, item);
391 g_assert (link != NULL);
393 GList *before;
394 for (before = link; positions && before; positions--)
395 before = before->next;
397 if (!before)
398 before = parent->last;
400 put_item_after (link, before);
402 redraw_if_visible (item);
403 item->canvas->need_repick = TRUE;
404 }
407 /**
408 * Lowers the item in its parent's stack by the specified number of positions.
409 *
410 * \param item A canvas item.
411 * \param positions Number of steps to lower the item.
412 *
413 * If the number of positions is greater than the distance to the bottom of the
414 * stack, then the item is put at the bottom.
415 **/
416 void
417 sp_canvas_item_lower (SPCanvasItem *item, int positions)
418 {
419 g_return_if_fail (item != NULL);
420 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
421 g_return_if_fail (positions >= 1);
423 if (!item->parent || positions == 0)
424 return;
426 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
427 GList *link = g_list_find (parent->items, item);
428 g_assert (link != NULL);
430 GList *before;
431 if (link->prev)
432 for (before = link->prev; positions && before; positions--)
433 before = before->prev;
434 else
435 before = NULL;
437 put_item_after (link, before);
439 redraw_if_visible (item);
440 item->canvas->need_repick = TRUE;
441 }
443 /**
444 * Sets visible flag on item and requests a redraw.
445 */
446 void
447 sp_canvas_item_show (SPCanvasItem *item)
448 {
449 g_return_if_fail (item != NULL);
450 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
452 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
453 return;
455 item->flags |= SP_CANVAS_ITEM_VISIBLE;
457 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
458 item->canvas->need_repick = TRUE;
459 }
461 /**
462 * Clears visible flag on item and requests a redraw.
463 */
464 void
465 sp_canvas_item_hide (SPCanvasItem *item)
466 {
467 g_return_if_fail (item != NULL);
468 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
470 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
471 return;
473 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
475 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
476 item->canvas->need_repick = TRUE;
477 }
479 /**
480 * Grab item under cursor.
481 *
482 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
483 */
484 int
485 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
486 {
487 g_return_val_if_fail (item != NULL, -1);
488 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
489 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
491 if (item->canvas->grabbed_item)
492 return -1;
494 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
495 return -1;
497 /* fixme: Top hack (Lauris) */
498 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
499 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
500 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
501 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
502 NULL, cursor, etime);
504 item->canvas->grabbed_item = item;
505 item->canvas->grabbed_event_mask = event_mask;
506 item->canvas->current_item = item; /* So that events go to the grabbed item */
508 return 0;
509 }
511 /**
512 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
513 * mouse.
514 *
515 * \param item A canvas item that holds a grab.
516 * \param etime The timestamp for ungrabbing the mouse.
517 */
518 void
519 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
520 {
521 g_return_if_fail (item != NULL);
522 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
524 if (item->canvas->grabbed_item != item)
525 return;
527 item->canvas->grabbed_item = NULL;
529 gdk_pointer_ungrab (etime);
530 }
532 /**
533 * Returns the product of all transformation matrices from the root item down
534 * to the item.
535 */
536 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
537 {
538 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
540 NR::Matrix affine = NR::identity();
542 while (item) {
543 affine *= item->xform;
544 item = item->parent;
545 }
546 return affine;
547 }
549 /**
550 * Helper that returns true iff item is descendant of parent.
551 */
552 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
553 {
554 while (item) {
555 if (item == parent)
556 return true;
557 item = item->parent;
558 }
560 return false;
561 }
563 /**
564 * Focus canvas, and item under cursor if it is not already focussed.
565 */
566 void
567 sp_canvas_item_grab_focus (SPCanvasItem *item)
568 {
569 g_return_if_fail (item != NULL);
570 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
571 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
573 SPCanvasItem *focused_item = item->canvas->focused_item;
575 if (focused_item) {
576 GdkEvent ev;
577 ev.focus_change.type = GDK_FOCUS_CHANGE;
578 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
579 ev.focus_change.send_event = FALSE;
580 ev.focus_change.in = FALSE;
582 emit_event (item->canvas, &ev);
583 }
585 item->canvas->focused_item = item;
586 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
588 if (focused_item) {
589 GdkEvent ev;
590 ev.focus_change.type = GDK_FOCUS_CHANGE;
591 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
592 ev.focus_change.send_event = FALSE;
593 ev.focus_change.in = TRUE;
595 emit_event (item->canvas, &ev);
596 }
597 }
599 /**
600 * Requests that the canvas queue an update for the specified item.
601 *
602 * To be used only by item implementations.
603 */
604 void
605 sp_canvas_item_request_update (SPCanvasItem *item)
606 {
607 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
608 return;
610 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
612 if (item->parent != NULL) {
613 /* Recurse up the tree */
614 sp_canvas_item_request_update (item->parent);
615 } else {
616 /* Have reached the top of the tree, make sure the update call gets scheduled. */
617 sp_canvas_request_update (item->canvas);
618 }
619 }
621 /**
622 * Returns position of item in group.
623 */
624 gint sp_canvas_item_order (SPCanvasItem * item)
625 {
626 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
627 }
629 /* SPCanvasGroup */
631 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
632 static void sp_canvas_group_init (SPCanvasGroup *group);
633 static void sp_canvas_group_destroy (GtkObject *object);
635 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
636 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
637 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
639 static SPCanvasItemClass *group_parent_class;
641 /**
642 * Registers SPCanvasGroup class with Gtk and returns its type number.
643 */
644 GtkType
645 sp_canvas_group_get_type (void)
646 {
647 static GtkType group_type = 0;
649 if (!group_type) {
650 static const GtkTypeInfo group_info = {
651 "SPCanvasGroup",
652 sizeof (SPCanvasGroup),
653 sizeof (SPCanvasGroupClass),
654 (GtkClassInitFunc) sp_canvas_group_class_init,
655 (GtkObjectInitFunc) sp_canvas_group_init,
656 NULL, NULL, NULL
657 };
659 group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
660 }
662 return group_type;
663 }
665 /**
666 * Class initialization function for SPCanvasGroupClass
667 */
668 static void
669 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
670 {
671 GtkObjectClass *object_class = (GtkObjectClass *) klass;
672 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
674 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
676 object_class->destroy = sp_canvas_group_destroy;
678 item_class->update = sp_canvas_group_update;
679 item_class->render = sp_canvas_group_render;
680 item_class->point = sp_canvas_group_point;
681 }
683 /**
684 * Callback. Empty.
685 */
686 static void
687 sp_canvas_group_init (SPCanvasGroup */*group*/)
688 {
689 /* Nothing here */
690 }
692 /**
693 * Callback that destroys all items in group and calls group's virtual
694 * destroy() function.
695 */
696 static void
697 sp_canvas_group_destroy (GtkObject *object)
698 {
699 g_return_if_fail (object != NULL);
700 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
702 const SPCanvasGroup *group = SP_CANVAS_GROUP (object);
704 GList *list = group->items;
705 while (list) {
706 SPCanvasItem *child = (SPCanvasItem *)list->data;
707 list = list->next;
709 gtk_object_destroy (GTK_OBJECT (child));
710 }
712 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
713 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
714 }
716 /**
717 * Update handler for canvas groups
718 */
719 static void
720 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
721 {
722 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
723 NR::ConvexHull corners(NR::Point(0, 0));
724 bool empty=true;
726 for (GList *list = group->items; list; list = list->next) {
727 SPCanvasItem *i = (SPCanvasItem *)list->data;
729 sp_canvas_item_invoke_update (i, affine, flags);
731 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
732 if (empty) {
733 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
734 empty = false;
735 } else {
736 corners.add(NR::Point(i->x1, i->y1));
737 }
738 corners.add(NR::Point(i->x2, i->y2));
739 }
740 }
742 NR::Rect const &bounds = corners.bounds();
743 item->x1 = bounds.min()[NR::X];
744 item->y1 = bounds.min()[NR::Y];
745 item->x2 = bounds.max()[NR::X];
746 item->y2 = bounds.max()[NR::Y];
747 }
749 /**
750 * Point handler for canvas groups.
751 */
752 static double
753 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
754 {
755 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
756 const double x = p[NR::X];
757 const double y = p[NR::Y];
758 int x1 = (int)(x - item->canvas->close_enough);
759 int y1 = (int)(y - item->canvas->close_enough);
760 int x2 = (int)(x + item->canvas->close_enough);
761 int y2 = (int)(y + item->canvas->close_enough);
763 double best = 0.0;
764 *actual_item = NULL;
766 double dist = 0.0;
768 for (GList *list = group->items; list; list = list->next) {
769 SPCanvasItem *child = (SPCanvasItem *)list->data;
771 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
772 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
774 int has_point;
775 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
776 dist = sp_canvas_item_invoke_point (child, p, &point_item);
777 has_point = TRUE;
778 } else
779 has_point = FALSE;
781 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
782 best = dist;
783 *actual_item = point_item;
784 }
785 }
786 }
788 return best;
789 }
791 /**
792 * Renders all visible canvas group items in buf rectangle.
793 */
794 static void
795 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
796 {
797 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
799 for (GList *list = group->items; list; list = list->next) {
800 SPCanvasItem *child = (SPCanvasItem *)list->data;
801 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
802 if ((child->x1 < buf->rect.x1) &&
803 (child->y1 < buf->rect.y1) &&
804 (child->x2 > buf->rect.x0) &&
805 (child->y2 > buf->rect.y0)) {
806 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
807 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
808 }
809 }
810 }
811 }
813 /**
814 * Adds an item to a canvas group.
815 */
816 static void
817 group_add (SPCanvasGroup *group, SPCanvasItem *item)
818 {
819 gtk_object_ref (GTK_OBJECT (item));
820 gtk_object_sink (GTK_OBJECT (item));
822 if (!group->items) {
823 group->items = g_list_append (group->items, item);
824 group->last = group->items;
825 } else {
826 group->last = g_list_append (group->last, item)->next;
827 }
829 sp_canvas_item_request_update (item);
830 }
832 /**
833 * Removes an item from a canvas group
834 */
835 static void
836 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
837 {
838 g_return_if_fail (group != NULL);
839 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
840 g_return_if_fail (item != NULL);
842 for (GList *children = group->items; children; children = children->next) {
843 if (children->data == item) {
845 /* Unparent the child */
847 item->parent = NULL;
848 gtk_object_unref (GTK_OBJECT (item));
850 /* Remove it from the list */
852 if (children == group->last) group->last = children->prev;
854 group->items = g_list_remove_link (group->items, children);
855 g_list_free (children);
856 break;
857 }
858 }
859 }
861 /* SPCanvas */
863 static void sp_canvas_class_init (SPCanvasClass *klass);
864 static void sp_canvas_init (SPCanvas *canvas);
865 static void sp_canvas_destroy (GtkObject *object);
867 static void sp_canvas_realize (GtkWidget *widget);
868 static void sp_canvas_unrealize (GtkWidget *widget);
870 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
871 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
873 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
874 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
875 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
876 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
877 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
878 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
879 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
880 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
882 static GtkWidgetClass *canvas_parent_class;
884 void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb);
885 void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb);
886 static int do_update (SPCanvas *canvas);
888 /**
889 * Registers the SPCanvas class if necessary, and returns the type ID
890 * associated to it.
891 *
892 * \return The type ID of the SPCanvas class.
893 **/
894 GtkType
895 sp_canvas_get_type (void)
896 {
897 static GtkType canvas_type = 0;
899 if (!canvas_type) {
900 static const GtkTypeInfo canvas_info = {
901 "SPCanvas",
902 sizeof (SPCanvas),
903 sizeof (SPCanvasClass),
904 (GtkClassInitFunc) sp_canvas_class_init,
905 (GtkObjectInitFunc) sp_canvas_init,
906 NULL, NULL, NULL
907 };
909 canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
910 }
912 return canvas_type;
913 }
915 /**
916 * Class initialization function for SPCanvasClass.
917 */
918 static void
919 sp_canvas_class_init (SPCanvasClass *klass)
920 {
921 GtkObjectClass *object_class = (GtkObjectClass *) klass;
922 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
924 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
926 object_class->destroy = sp_canvas_destroy;
928 widget_class->realize = sp_canvas_realize;
929 widget_class->unrealize = sp_canvas_unrealize;
930 widget_class->size_request = sp_canvas_size_request;
931 widget_class->size_allocate = sp_canvas_size_allocate;
932 widget_class->button_press_event = sp_canvas_button;
933 widget_class->button_release_event = sp_canvas_button;
934 widget_class->motion_notify_event = sp_canvas_motion;
935 widget_class->scroll_event = sp_canvas_scroll;
936 widget_class->expose_event = sp_canvas_expose;
937 widget_class->key_press_event = sp_canvas_key;
938 widget_class->key_release_event = sp_canvas_key;
939 widget_class->enter_notify_event = sp_canvas_crossing;
940 widget_class->leave_notify_event = sp_canvas_crossing;
941 widget_class->focus_in_event = sp_canvas_focus_in;
942 widget_class->focus_out_event = sp_canvas_focus_out;
943 }
945 /**
946 * Callback: object initialization for SPCanvas.
947 */
948 static void
949 sp_canvas_init (SPCanvas *canvas)
950 {
951 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
952 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
953 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
955 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
956 canvas->pick_event.crossing.x = 0;
957 canvas->pick_event.crossing.y = 0;
959 /* Create the root item as a special case */
960 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
961 canvas->root->canvas = canvas;
963 gtk_object_ref (GTK_OBJECT (canvas->root));
964 gtk_object_sink (GTK_OBJECT (canvas->root));
966 canvas->need_repick = TRUE;
968 // See comment at in sp-canvas.h.
969 canvas->gen_all_enter_events = false;
971 canvas->tiles=NULL;
972 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
973 canvas->tileH=canvas->tileV=0;
975 canvas->redraw_aborted.x0 = NR_HUGE_L;
976 canvas->redraw_aborted.x1 = -NR_HUGE_L;
977 canvas->redraw_aborted.y0 = NR_HUGE_L;
978 canvas->redraw_aborted.y1 = -NR_HUGE_L;
980 canvas->redraw_count = 0;
982 canvas->forced_redraw_count = 0;
983 canvas->forced_redraw_limit = -1;
985 canvas->slowest_buffer = 0;
986 }
988 /**
989 * Convenience function to remove the idle handler of a canvas.
990 */
991 static void
992 remove_idle (SPCanvas *canvas)
993 {
994 if (canvas->idle_id) {
995 gtk_idle_remove (canvas->idle_id);
996 canvas->idle_id = 0;
997 }
998 }
1000 /*
1001 * Removes the transient state of the canvas (idle handler, grabs).
1002 */
1003 static void
1004 shutdown_transients (SPCanvas *canvas)
1005 {
1006 /* We turn off the need_redraw flag, since if the canvas is mapped again
1007 * it will request a redraw anyways. We do not turn off the need_update
1008 * flag, though, because updates are not queued when the canvas remaps
1009 * itself.
1010 */
1011 if (canvas->need_redraw) {
1012 canvas->need_redraw = FALSE;
1013 }
1014 if ( canvas->tiles ) g_free(canvas->tiles);
1015 canvas->tiles=NULL;
1016 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1017 canvas->tileH=canvas->tileV=0;
1019 if (canvas->grabbed_item) {
1020 canvas->grabbed_item = NULL;
1021 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1022 }
1024 remove_idle (canvas);
1025 }
1027 /**
1028 * Destroy handler for SPCanvas.
1029 */
1030 static void
1031 sp_canvas_destroy (GtkObject *object)
1032 {
1033 SPCanvas *canvas = SP_CANVAS (object);
1035 if (canvas->root) {
1036 gtk_object_unref (GTK_OBJECT (canvas->root));
1037 canvas->root = NULL;
1038 }
1040 shutdown_transients (canvas);
1042 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1043 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1044 }
1046 /**
1047 * Returns new canvas as widget.
1048 */
1049 GtkWidget *
1050 sp_canvas_new_aa (void)
1051 {
1052 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1054 return (GtkWidget *) canvas;
1055 }
1057 /**
1058 * The canvas widget's realize callback.
1059 */
1060 static void
1061 sp_canvas_realize (GtkWidget *widget)
1062 {
1063 SPCanvas *canvas = SP_CANVAS (widget);
1065 GdkWindowAttr attributes;
1066 attributes.window_type = GDK_WINDOW_CHILD;
1067 attributes.x = widget->allocation.x;
1068 attributes.y = widget->allocation.y;
1069 attributes.width = widget->allocation.width;
1070 attributes.height = widget->allocation.height;
1071 attributes.wclass = GDK_INPUT_OUTPUT;
1072 attributes.visual = gdk_rgb_get_visual ();
1073 attributes.colormap = gdk_rgb_get_cmap ();
1074 attributes.event_mask = (gtk_widget_get_events (widget) |
1075 GDK_EXPOSURE_MASK |
1076 GDK_BUTTON_PRESS_MASK |
1077 GDK_BUTTON_RELEASE_MASK |
1078 GDK_POINTER_MOTION_MASK |
1079 GDK_PROXIMITY_IN_MASK |
1080 GDK_PROXIMITY_OUT_MASK |
1081 GDK_KEY_PRESS_MASK |
1082 GDK_KEY_RELEASE_MASK |
1083 GDK_ENTER_NOTIFY_MASK |
1084 GDK_LEAVE_NOTIFY_MASK |
1085 GDK_FOCUS_CHANGE_MASK);
1086 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1088 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1089 gdk_window_set_user_data (widget->window, widget);
1091 if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1092 gtk_widget_set_events(widget, attributes.event_mask);
1094 widget->style = gtk_style_attach (widget->style, widget->window);
1096 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1098 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1099 }
1101 /**
1102 * The canvas widget's unrealize callback.
1103 */
1104 static void
1105 sp_canvas_unrealize (GtkWidget *widget)
1106 {
1107 SPCanvas *canvas = SP_CANVAS (widget);
1109 shutdown_transients (canvas);
1111 gdk_gc_destroy (canvas->pixmap_gc);
1112 canvas->pixmap_gc = NULL;
1114 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1115 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1116 }
1118 /**
1119 * The canvas widget's size_request callback.
1120 */
1121 static void
1122 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1123 {
1124 static_cast<void>(SP_CANVAS (widget));
1126 req->width = 256;
1127 req->height = 256;
1128 }
1130 /**
1131 * The canvas widget's size_allocate callback.
1132 */
1133 static void
1134 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1135 {
1136 SPCanvas *canvas = SP_CANVAS (widget);
1138 /* Schedule redraw of new region */
1139 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1140 if (allocation->width > widget->allocation.width) {
1141 sp_canvas_request_redraw (canvas,
1142 canvas->x0 + widget->allocation.width,
1143 0,
1144 canvas->x0 + allocation->width,
1145 canvas->y0 + allocation->height);
1146 }
1147 if (allocation->height > widget->allocation.height) {
1148 sp_canvas_request_redraw (canvas,
1149 0,
1150 canvas->y0 + widget->allocation.height,
1151 canvas->x0 + allocation->width,
1152 canvas->y0 + allocation->height);
1153 }
1155 widget->allocation = *allocation;
1157 if (GTK_WIDGET_REALIZED (widget)) {
1158 gdk_window_move_resize (widget->window,
1159 widget->allocation.x, widget->allocation.y,
1160 widget->allocation.width, widget->allocation.height);
1161 }
1162 }
1164 /**
1165 * Helper that emits an event for an item in the canvas, be it the current
1166 * item, grabbed item, or focused item, as appropriate.
1167 */
1168 static int
1169 emit_event (SPCanvas *canvas, GdkEvent *event)
1170 {
1171 guint mask;
1173 if (canvas->grabbed_item) {
1174 switch (event->type) {
1175 case GDK_ENTER_NOTIFY:
1176 mask = GDK_ENTER_NOTIFY_MASK;
1177 break;
1178 case GDK_LEAVE_NOTIFY:
1179 mask = GDK_LEAVE_NOTIFY_MASK;
1180 break;
1181 case GDK_MOTION_NOTIFY:
1182 mask = GDK_POINTER_MOTION_MASK;
1183 break;
1184 case GDK_BUTTON_PRESS:
1185 case GDK_2BUTTON_PRESS:
1186 case GDK_3BUTTON_PRESS:
1187 mask = GDK_BUTTON_PRESS_MASK;
1188 break;
1189 case GDK_BUTTON_RELEASE:
1190 mask = GDK_BUTTON_RELEASE_MASK;
1191 break;
1192 case GDK_KEY_PRESS:
1193 mask = GDK_KEY_PRESS_MASK;
1194 break;
1195 case GDK_KEY_RELEASE:
1196 mask = GDK_KEY_RELEASE_MASK;
1197 break;
1198 case GDK_SCROLL:
1199 mask = GDK_SCROLL;
1200 break;
1201 default:
1202 mask = 0;
1203 break;
1204 }
1206 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1207 }
1209 /* Convert to world coordinates -- we have two cases because of diferent
1210 * offsets of the fields in the event structures.
1211 */
1213 GdkEvent ev = *event;
1215 switch (ev.type) {
1216 case GDK_ENTER_NOTIFY:
1217 case GDK_LEAVE_NOTIFY:
1218 ev.crossing.x += canvas->x0;
1219 ev.crossing.y += canvas->y0;
1220 break;
1221 case GDK_MOTION_NOTIFY:
1222 case GDK_BUTTON_PRESS:
1223 case GDK_2BUTTON_PRESS:
1224 case GDK_3BUTTON_PRESS:
1225 case GDK_BUTTON_RELEASE:
1226 ev.motion.x += canvas->x0;
1227 ev.motion.y += canvas->y0;
1228 break;
1229 default:
1230 break;
1231 }
1233 /* Choose where we send the event */
1235 /* canvas->current_item becomes NULL in some cases under Win32
1236 ** (e.g. if the pointer leaves the window). So this is a hack that
1237 ** Lauris applied to SP to get around the problem.
1238 */
1239 SPCanvasItem* item = NULL;
1240 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1241 item = canvas->grabbed_item;
1242 } else {
1243 item = canvas->current_item;
1244 }
1246 if (canvas->focused_item &&
1247 ((event->type == GDK_KEY_PRESS) ||
1248 (event->type == GDK_KEY_RELEASE) ||
1249 (event->type == GDK_FOCUS_CHANGE))) {
1250 item = canvas->focused_item;
1251 }
1253 /* The event is propagated up the hierarchy (for if someone connected to
1254 * a group instead of a leaf event), and emission is stopped if a
1255 * handler returns TRUE, just like for GtkWidget events.
1256 */
1258 gint finished = FALSE;
1260 while (item && !finished) {
1261 gtk_object_ref (GTK_OBJECT (item));
1262 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1263 SPCanvasItem *parent = item->parent;
1264 gtk_object_unref (GTK_OBJECT (item));
1265 item = parent;
1266 }
1268 return finished;
1269 }
1271 /**
1272 * Helper that re-picks the current item in the canvas, based on the event's
1273 * coordinates and emits enter/leave events for items as appropriate.
1274 */
1275 static int
1276 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1277 {
1278 int button_down = 0;
1279 double x, y;
1281 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1282 return FALSE;
1284 int retval = FALSE;
1286 if (canvas->gen_all_enter_events == false) {
1287 // If a button is down, we'll perform enter and leave events on the
1288 // current item, but not enter on any other item. This is more or
1289 // less like X pointer grabbing for canvas items.
1290 //
1291 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1292 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1294 if (!button_down) canvas->left_grabbed_item = FALSE;
1295 }
1297 /* Save the event in the canvas. This is used to synthesize enter and
1298 * leave events in case the current item changes. It is also used to
1299 * re-pick the current item if the current one gets deleted. Also,
1300 * synthesize an enter event.
1301 */
1302 if (event != &canvas->pick_event) {
1303 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1304 /* these fields have the same offsets in both types of events */
1306 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1307 canvas->pick_event.crossing.window = event->motion.window;
1308 canvas->pick_event.crossing.send_event = event->motion.send_event;
1309 canvas->pick_event.crossing.subwindow = NULL;
1310 canvas->pick_event.crossing.x = event->motion.x;
1311 canvas->pick_event.crossing.y = event->motion.y;
1312 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1313 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1314 canvas->pick_event.crossing.focus = FALSE;
1315 canvas->pick_event.crossing.state = event->motion.state;
1317 /* these fields don't have the same offsets in both types of events */
1319 if (event->type == GDK_MOTION_NOTIFY) {
1320 canvas->pick_event.crossing.x_root = event->motion.x_root;
1321 canvas->pick_event.crossing.y_root = event->motion.y_root;
1322 } else {
1323 canvas->pick_event.crossing.x_root = event->button.x_root;
1324 canvas->pick_event.crossing.y_root = event->button.y_root;
1325 }
1326 } else {
1327 canvas->pick_event = *event;
1328 }
1329 }
1331 /* Don't do anything else if this is a recursive call */
1332 if (canvas->in_repick) return retval;
1334 /* LeaveNotify means that there is no current item, so we don't look for one */
1335 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1336 /* these fields don't have the same offsets in both types of events */
1338 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1339 x = canvas->pick_event.crossing.x;
1340 y = canvas->pick_event.crossing.y;
1341 } else {
1342 x = canvas->pick_event.motion.x;
1343 y = canvas->pick_event.motion.y;
1344 }
1346 /* world coords */
1347 x += canvas->x0;
1348 y += canvas->y0;
1350 /* find the closest item */
1351 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1352 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1353 } else {
1354 canvas->new_current_item = NULL;
1355 }
1356 } else {
1357 canvas->new_current_item = NULL;
1358 }
1360 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1361 return retval; /* current item did not change */
1362 }
1364 /* Synthesize events for old and new current items */
1366 if ((canvas->new_current_item != canvas->current_item)
1367 && (canvas->current_item != NULL)
1368 && !canvas->left_grabbed_item) {
1369 GdkEvent new_event;
1370 SPCanvasItem *item;
1372 item = canvas->current_item;
1374 new_event = canvas->pick_event;
1375 new_event.type = GDK_LEAVE_NOTIFY;
1377 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1378 new_event.crossing.subwindow = NULL;
1379 canvas->in_repick = TRUE;
1380 retval = emit_event (canvas, &new_event);
1381 canvas->in_repick = FALSE;
1382 }
1384 if (canvas->gen_all_enter_events == false) {
1385 // new_current_item may have been set to NULL during the call to
1386 // emit_event() above
1387 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1388 canvas->left_grabbed_item = TRUE;
1389 return retval;
1390 }
1391 }
1393 /* Handle the rest of cases */
1395 canvas->left_grabbed_item = FALSE;
1396 canvas->current_item = canvas->new_current_item;
1398 if (canvas->current_item != NULL) {
1399 GdkEvent new_event;
1401 new_event = canvas->pick_event;
1402 new_event.type = GDK_ENTER_NOTIFY;
1403 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1404 new_event.crossing.subwindow = NULL;
1405 retval = emit_event (canvas, &new_event);
1406 }
1408 return retval;
1409 }
1411 /**
1412 * Button event handler for the canvas.
1413 */
1414 static gint
1415 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1416 {
1417 SPCanvas *canvas = SP_CANVAS (widget);
1419 int retval = FALSE;
1421 /* dispatch normally regardless of the event's window if an item has
1422 has a pointer grab in effect */
1423 if (!canvas->grabbed_item &&
1424 event->window != SP_CANVAS_WINDOW (canvas))
1425 return retval;
1427 int mask;
1428 switch (event->button) {
1429 case 1:
1430 mask = GDK_BUTTON1_MASK;
1431 break;
1432 case 2:
1433 mask = GDK_BUTTON2_MASK;
1434 break;
1435 case 3:
1436 mask = GDK_BUTTON3_MASK;
1437 break;
1438 case 4:
1439 mask = GDK_BUTTON4_MASK;
1440 break;
1441 case 5:
1442 mask = GDK_BUTTON5_MASK;
1443 break;
1444 default:
1445 mask = 0;
1446 }
1448 switch (event->type) {
1449 case GDK_BUTTON_PRESS:
1450 case GDK_2BUTTON_PRESS:
1451 case GDK_3BUTTON_PRESS:
1452 /* Pick the current item as if the button were not pressed, and
1453 * then process the event.
1454 */
1455 canvas->state = event->state;
1456 pick_current_item (canvas, (GdkEvent *) event);
1457 canvas->state ^= mask;
1458 retval = emit_event (canvas, (GdkEvent *) event);
1459 break;
1461 case GDK_BUTTON_RELEASE:
1462 /* Process the event as if the button were pressed, then repick
1463 * after the button has been released
1464 */
1465 canvas->state = event->state;
1466 retval = emit_event (canvas, (GdkEvent *) event);
1467 event->state ^= mask;
1468 canvas->state = event->state;
1469 pick_current_item (canvas, (GdkEvent *) event);
1470 event->state ^= mask;
1471 break;
1473 default:
1474 g_assert_not_reached ();
1475 }
1477 return retval;
1478 }
1480 /**
1481 * Scroll event handler for the canvas.
1482 *
1483 * \todo FIXME: generate motion events to re-select items.
1484 */
1485 static gint
1486 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1487 {
1488 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1489 }
1491 /**
1492 * Motion event handler for the canvas.
1493 */
1494 static int
1495 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1496 {
1497 SPCanvas *canvas = SP_CANVAS (widget);
1499 if (event->window != SP_CANVAS_WINDOW (canvas))
1500 return FALSE;
1502 if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1503 gint x, y;
1504 gdk_window_get_pointer (widget->window, &x, &y, NULL);
1505 event->x = x;
1506 event->y = y;
1507 }
1509 canvas->state = event->state;
1510 pick_current_item (canvas, (GdkEvent *) event);
1512 return emit_event (canvas, (GdkEvent *) event);
1513 }
1515 static void
1516 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)
1517 {
1518 GtkWidget *widget = GTK_WIDGET (canvas);
1520 SPCanvasBuf buf;
1521 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1522 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1523 } else {
1524 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1525 }
1527 buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1528 buf.rect.x0 = x0;
1529 buf.rect.y0 = y0;
1530 buf.rect.x1 = x1;
1531 buf.rect.y1 = y1;
1532 buf.visible_rect.x0 = draw_x1;
1533 buf.visible_rect.y0 = draw_y1;
1534 buf.visible_rect.x1 = draw_x2;
1535 buf.visible_rect.y1 = draw_y2;
1536 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1537 buf.bg_color = (((color->red & 0xff00) << 8)
1538 | (color->green & 0xff00)
1539 | (color->blue >> 8));
1540 buf.is_empty = true;
1542 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1543 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1544 }
1546 if (buf.is_empty) {
1547 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1548 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1549 canvas->pixmap_gc,
1550 TRUE,
1551 x0 - canvas->x0, y0 - canvas->y0,
1552 x1 - x0, y1 - y0);
1553 } else {
1554 /*
1555 CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with:
1556 cairo_surface_t* cst = cairo_image_surface_create_for_data (
1557 buf.buf,
1558 CAIRO_FORMAT_RGB24, // unpacked, i.e. 32 bits! one byte is unused
1559 x1 - x0, y1 - y0,
1560 buf.buf_rowstride
1561 );
1562 cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1563 cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1564 cairo_paint (ct);
1566 cairo_destroy (ct);
1567 cairo_surface_finish (cst);
1568 cairo_surface_destroy (cst);
1569 */
1571 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1572 canvas->pixmap_gc,
1573 x0 - canvas->x0, y0 - canvas->y0,
1574 x1 - x0, y1 - y0,
1575 GDK_RGB_DITHER_MAX,
1576 buf.buf,
1577 sw * 3,
1578 x0 - canvas->x0, y0 - canvas->y0);
1579 }
1581 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1582 nr_pixelstore_256K_free (buf.buf);
1583 } else {
1584 nr_pixelstore_1M_free (buf.buf);
1585 }
1586 }
1588 /* Paint the given rect, while updating canvas->redraw_aborted and running iterations after each
1589 * buffer; make sure canvas->redraw_aborted never goes past aborted_limit (used for 2-rect
1590 * optimized repaint)
1591 */
1592 static int
1593 sp_canvas_paint_rect_internal (SPCanvas *canvas, NRRectL *rect, NR::ICoord *x_aborted_limit, NR::ICoord *y_aborted_limit)
1594 {
1595 int draw_x1 = rect->x0;
1596 int draw_x2 = rect->x1;
1597 int draw_y1 = rect->y0;
1598 int draw_y2 = rect->y1;
1600 // Here we'll store the time it took to draw the slowest buffer of this paint.
1601 glong slowest_buffer = 0;
1603 // Find the optimal buffer dimensions
1604 int bw = draw_x2 - draw_x1;
1605 int bh = draw_y2 - draw_y1;
1606 if ((bw < 1) || (bh < 1))
1607 return 0;
1609 int sw, sh; // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1610 if (canvas->rendermode != RENDERMODE_OUTLINE) { // use 256K as a compromise to not slow down gradients
1611 /* 256K is the cached buffer and we need 3 channels */
1612 if (bw * bh < 87381) { // 256K/3
1613 // We can go with single buffer
1614 sw = bw;
1615 sh = bh;
1616 } else if (bw <= (16 * 341)) {
1617 // Go with row buffer
1618 sw = bw;
1619 sh = 87381 / bw;
1620 } else if (bh <= (16 * 256)) {
1621 // Go with column buffer
1622 sw = 87381 / bh;
1623 sh = bh;
1624 } else {
1625 sw = 341;
1626 sh = 256;
1627 }
1628 } else { // paths only, so 1M works faster
1629 /* 1M is the cached buffer and we need 3 channels */
1630 if (bw * bh < 349525) { // 1M/3
1631 // We can go with single buffer
1632 sw = bw;
1633 sh = bh;
1634 } else if (bw <= (16 * 682)) {
1635 // Go with row buffer
1636 sw = bw;
1637 sh = 349525 / bw;
1638 } else if (bh <= (16 * 512)) {
1639 // Go with column buffer
1640 sw = 349525 / bh;
1641 sh = bh;
1642 } else {
1643 sw = 682;
1644 sh = 512;
1645 }
1646 }
1648 // Will this paint require more than one buffer?
1649 bool multiple_buffers = (((draw_y2 - draw_y1) > sh) || ((draw_x2 - draw_x1) > sw)); // or two_rects
1651 // remember the counter during this paint
1652 long this_count = canvas->redraw_count;
1654 // Time values to measure each buffer's paint time
1655 GTimeVal tstart, tfinish;
1657 // paint from the corner nearest the mouse pointer
1659 gint x, y;
1660 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1661 NR::Point pw = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1663 bool reverse_x = (pw[NR::X] > ((draw_x2 + draw_x1) / 2));
1664 bool reverse_y = (pw[NR::Y] > ((draw_y2 + draw_y1) / 2));
1666 if ((bw > bh) && (sh > sw)) {
1667 int t = sw; sw = sh; sh = t;
1668 }
1670 // This is the main loop which corresponds to the visible left-to-right, top-to-bottom drawing
1671 // of screen blocks (buffers).
1672 for (int y0 = draw_y1; y0 < draw_y2; y0 += sh) {
1673 int y1 = MIN (y0 + sh, draw_y2);
1674 for (int x0 = draw_x1; x0 < draw_x2; x0 += sw) {
1675 int x1 = MIN (x0 + sw, draw_x2);
1677 int dx0 = x0;
1678 int dx1 = x1;
1679 int dy0 = y0;
1680 int dy1 = y1;
1682 if (reverse_x) {
1683 dx0 = (draw_x2 - (x0 + sw)) + draw_x1;
1684 dx0 = MAX (dx0, draw_x1);
1685 dx1 = (draw_x2 - x0) + draw_x1;
1686 dx1 = MIN (dx1, draw_x2);
1687 }
1688 if (reverse_y) {
1689 dy0 = (draw_y2 - (y0 + sh)) + draw_y1;
1690 dy0 = MAX (dy0, draw_y1);
1691 dy1 = (draw_y2 - y0) + draw_y1;
1692 dy1 = MIN (dy1, draw_y2);
1693 }
1695 // OPTIMIZATION IDEA: if drawing is really slow (as measured by canvas->slowest
1696 // buffer), process some events even BEFORE we do any buffers?
1698 // Paint one buffer; measure how long it takes.
1699 g_get_current_time (&tstart);
1700 sp_canvas_paint_single_buffer (canvas, dx0, dy0, dx1, dy1, draw_x1, draw_y1, draw_x2, draw_y2, sw);
1701 g_get_current_time (&tfinish);
1703 // Remember the slowest_buffer of this paint.
1704 glong this_buffer = (tfinish.tv_sec - tstart.tv_sec) * 1000000 + (tfinish.tv_usec - tstart.tv_usec);
1705 if (this_buffer > slowest_buffer)
1706 slowest_buffer = this_buffer;
1708 // After each successful buffer, reduce the rect remaining to redraw by what is already redrawn
1709 if (x1 >= draw_x2) {
1710 if (reverse_y) {
1711 if (canvas->redraw_aborted.y1 > dy0) { canvas->redraw_aborted.y1 = dy0; }
1712 } else {
1713 if (canvas->redraw_aborted.y0 < y1) { canvas->redraw_aborted.y0 = y1; }
1714 }
1715 }
1717 if (y1 >= draw_y2) {
1718 if (reverse_x) {
1719 if (canvas->redraw_aborted.x1 > dx0) { canvas->redraw_aborted.x1 = dx0; }
1720 } else {
1721 if (canvas->redraw_aborted.x0 < x1) { canvas->redraw_aborted.x0 = x1; }
1722 }
1723 }
1725 // INTERRUPTIBLE DISPLAY:
1726 // Process events that may have arrived while we were busy drawing;
1727 // only if we're drawing multiple buffers, and only if this one was not very fast,
1728 // and only if we're allowed to interrupt this redraw
1729 bool ok_to_interrupt = (multiple_buffers && this_buffer > 25000);
1730 if (ok_to_interrupt && (canvas->forced_redraw_limit != -1)) {
1731 ok_to_interrupt = (canvas->forced_redraw_count < canvas->forced_redraw_limit);
1732 }
1734 if (ok_to_interrupt) {
1735 // Run at most max_iterations of the main loop; we cannot process ALL events
1736 // here because some things (e.g. rubberband) flood with dirtying events but will
1737 // not redraw themselves
1738 int max_iterations = 10;
1739 int iterations = 0;
1740 while (Gtk::Main::events_pending() && iterations++ < max_iterations) {
1741 Gtk::Main::iteration(false);
1742 // If one of the iterations has redrawn by itself, abort
1743 if (this_count != canvas->redraw_count) {
1744 canvas->slowest_buffer = slowest_buffer;
1745 if (canvas->forced_redraw_limit != -1) {
1746 canvas->forced_redraw_count++;
1747 }
1748 return 1; // interrupted
1749 }
1750 }
1752 // If not aborted so far, check if the events set redraw or update flags;
1753 // if so, force update and abort
1754 if (canvas->need_redraw || canvas->need_update) {
1755 canvas->slowest_buffer = slowest_buffer;
1756 if (canvas->forced_redraw_limit != -1) {
1757 canvas->forced_redraw_count++;
1758 }
1759 do_update (canvas);
1760 return 1; // interrupted
1761 }
1762 }
1763 }
1764 }
1766 // Remember the slowest buffer of this paint in canvas
1767 canvas->slowest_buffer = slowest_buffer;
1769 return 0; // finished
1770 }
1773 /**
1774 * Helper that draws a specific rectangular part of the canvas.
1775 */
1776 static void
1777 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1778 {
1779 g_return_if_fail (!canvas->need_update);
1781 // Monotonously increment the canvas-global counter on each paint. This will let us find out
1782 // when a new paint happened in event processing during this paint, so we can abort it.
1783 canvas->redraw_count++;
1785 NRRectL rect;
1786 rect.x0 = xx0;
1787 rect.x1 = xx1;
1788 rect.y0 = yy0;
1789 rect.y1 = yy1;
1791 // Clip rect-to-draw by the current visible area
1792 rect.x0 = MAX (rect.x0, canvas->x0);
1793 rect.y0 = MAX (rect.y0, canvas->y0);
1794 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1795 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1797 // Clip rect-aborted-last-time by the current visible area
1798 canvas->redraw_aborted.x0 = MAX (canvas->redraw_aborted.x0, canvas->x0);
1799 canvas->redraw_aborted.y0 = MAX (canvas->redraw_aborted.y0, canvas->y0);
1800 canvas->redraw_aborted.x1 = MIN (canvas->redraw_aborted.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1801 canvas->redraw_aborted.y1 = MIN (canvas->redraw_aborted.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1803 if (canvas->redraw_aborted.x0 < canvas->redraw_aborted.x1 && canvas->redraw_aborted.y0 < canvas->redraw_aborted.y1) {
1804 // There was an aborted redraw last time, now we need to redraw BOTH it and the new rect.
1806 // save the old aborted rect in case we decide to paint it separately (see below)
1807 NRRectL aborted = canvas->redraw_aborted;
1809 // calculate the rectangle union of the both rects (the smallest rectangle which covers both)
1810 NRRectL nion;
1811 nr_rect_l_union (&nion, &rect, &aborted);
1813 // subtract one of the rects-to-draw from the other (the smallest rectangle which covers
1814 // all of the first not covered by the second)
1815 NRRectL rect_minus_aborted;
1816 nr_rect_l_subtract (&rect_minus_aborted, &rect, &aborted);
1818 // Initially, the rect to redraw later (in case we're aborted) is the same as the union of both rects
1819 canvas->redraw_aborted = nion;
1821 // calculate areas of the three rects
1822 if ((nr_rect_l_area(&rect_minus_aborted) + nr_rect_l_area(&aborted)) * 1.2 < nr_rect_l_area(&nion)) {
1823 // If the summary area of the two rects is significantly (at least by 20%) less than
1824 // the area of their rectangular union, it makes sense to paint the two rects
1825 // separately instead of painting their union. This gives a significant speedup when,
1826 // for example, your current canvas is almost painted, with only a strip at bottom
1827 // left, and at that moment you abort it by scrolling down which reveals a new strip at
1828 // the top. Straightforward painting of the union of the aborted rect and the new rect
1829 // will have to repaint the entire canvas! By contrast, the optimized approach below
1830 // paints the two narrow strips in order which is much faster.
1832 // find out which rect to draw first - compare them first by y then by x of the top left corners
1833 NRRectL *first;
1834 NRRectL *second;
1835 if (rect.y0 == aborted.y0) {
1836 if (rect.x0 < aborted.x0) {
1837 first = ▭
1838 second = &aborted;
1839 } else {
1840 second = ▭
1841 first = &aborted;
1842 }
1843 } else if (rect.y0 < aborted.y0) {
1844 first = ▭
1845 second = &aborted;
1846 } else {
1847 second = ▭
1848 first = &aborted;
1849 }
1851 NRRectL second_minus_first;
1852 nr_rect_l_subtract (&second_minus_first, second, first);
1854 // paint the first rect;
1855 if (sp_canvas_paint_rect_internal (canvas, first, &(second_minus_first.x0), &(second_minus_first.y0))) {
1856 // aborted!
1857 return;
1858 }
1860 // if not aborted, assign (second rect minus first) as the new redraw_aborted and paint the same
1861 canvas->redraw_aborted = second_minus_first;
1862 if (sp_canvas_paint_rect_internal (canvas, &second_minus_first, NULL, NULL)) {
1863 return; // aborted
1864 }
1866 } else {
1867 // no need for separate drawing, just draw the union as one rect
1868 if (sp_canvas_paint_rect_internal (canvas, &nion, NULL, NULL)) {
1869 return; // aborted
1870 }
1871 }
1872 } else {
1873 // Nothing was aborted last time, just draw the rect we're given
1875 // Initially, the rect to redraw later (in case we're aborted) is the same as the one we're going to draw now.
1876 canvas->redraw_aborted = rect;
1878 if (sp_canvas_paint_rect_internal (canvas, &rect, NULL, NULL)) {
1879 return; // aborted
1880 }
1881 }
1883 // we've had a full unaborted redraw, reset the full redraw counter
1884 if (canvas->forced_redraw_limit != -1) {
1885 canvas->forced_redraw_count = 0;
1886 }
1887 }
1889 /**
1890 * Force a full redraw after a specified number of interrupted redraws
1891 */
1892 void
1893 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1894 g_return_if_fail(canvas != NULL);
1896 canvas->forced_redraw_limit = count;
1897 canvas->forced_redraw_count = 0;
1898 }
1900 /**
1901 * End forced full redraw requests
1902 */
1903 void
1904 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1905 g_return_if_fail(canvas != NULL);
1907 canvas->forced_redraw_limit = -1;
1908 }
1910 /**
1911 * The canvas widget's expose callback.
1912 */
1913 static gint
1914 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1915 {
1916 SPCanvas *canvas = SP_CANVAS (widget);
1918 if (!GTK_WIDGET_DRAWABLE (widget) ||
1919 (event->window != SP_CANVAS_WINDOW (canvas)))
1920 return FALSE;
1922 int n_rects;
1923 GdkRectangle *rects;
1924 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1926 for (int i = 0; i < n_rects; i++) {
1927 NRRectL rect;
1929 rect.x0 = rects[i].x + canvas->x0;
1930 rect.y0 = rects[i].y + canvas->y0;
1931 rect.x1 = rect.x0 + rects[i].width;
1932 rect.y1 = rect.y0 + rects[i].height;
1934 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1935 }
1937 if (n_rects > 0)
1938 g_free (rects);
1940 return FALSE;
1941 }
1943 /**
1944 * The canvas widget's keypress callback.
1945 */
1946 static gint
1947 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1948 {
1949 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1950 }
1952 /**
1953 * Crossing event handler for the canvas.
1954 */
1955 static gint
1956 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1957 {
1958 SPCanvas *canvas = SP_CANVAS (widget);
1960 if (event->window != SP_CANVAS_WINDOW (canvas))
1961 return FALSE;
1963 canvas->state = event->state;
1964 return pick_current_item (canvas, (GdkEvent *) event);
1965 }
1967 /**
1968 * Focus in handler for the canvas.
1969 */
1970 static gint
1971 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1972 {
1973 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1975 SPCanvas *canvas = SP_CANVAS (widget);
1977 if (canvas->focused_item) {
1978 return emit_event (canvas, (GdkEvent *) event);
1979 } else {
1980 return FALSE;
1981 }
1982 }
1984 /**
1985 * Focus out handler for the canvas.
1986 */
1987 static gint
1988 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1989 {
1990 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1992 SPCanvas *canvas = SP_CANVAS (widget);
1994 if (canvas->focused_item)
1995 return emit_event (canvas, (GdkEvent *) event);
1996 else
1997 return FALSE;
1998 }
2000 /**
2001 * Helper that repaints the areas in the canvas that need it.
2002 */
2003 static int
2004 paint (SPCanvas *canvas)
2005 {
2006 if (canvas->need_update) {
2007 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2008 canvas->need_update = FALSE;
2009 }
2011 if (!canvas->need_redraw)
2012 return TRUE;
2014 GtkWidget const *widget = GTK_WIDGET(canvas);
2015 int const canvas_x1 = canvas->x0 + widget->allocation.width;
2016 int const canvas_y1 = canvas->y0 + widget->allocation.height;
2018 NRRectL topaint;
2019 topaint.x0 = topaint.y0 = topaint.x1 = topaint.y1 = 0;
2021 for (int j=canvas->tTop&(~3);j<canvas->tBottom;j+=4) {
2022 for (int i=canvas->tLeft&(~3);i<canvas->tRight;i+=4) {
2023 int mode=0;
2025 int pl=i+1,pr=i,pt=j+4,pb=j;
2026 for (int l=MAX(j,canvas->tTop);l<MIN(j+4,canvas->tBottom);l++) {
2027 for (int k=MAX(i,canvas->tLeft);k<MIN(i+4,canvas->tRight);k++) {
2028 if ( canvas->tiles[(k-canvas->tLeft)+(l-canvas->tTop)*canvas->tileH] ) {
2029 mode|=1<<((k-i)+(l-j)*4);
2030 if ( k < pl ) pl=k;
2031 if ( k+1 > pr ) pr=k+1;
2032 if ( l < pt ) pt=l;
2033 if ( l+1 > pb ) pb=l+1;
2034 }
2035 canvas->tiles[(k-canvas->tLeft)+(l-canvas->tTop)*canvas->tileH]=0;
2036 }
2037 }
2039 if ( mode ) {
2040 NRRectL tile;
2041 tile.x0 = MAX (pl*32, canvas->x0);
2042 tile.y0 = MAX (pt*32, canvas->y0);
2043 tile.x1 = MIN (pr*32, canvas_x1);
2044 tile.y1 = MIN (pb*32, canvas_y1);
2045 if ((tile.x0 < tile.x1) && (tile.y0 < tile.y1)) {
2046 nr_rect_l_union (&topaint, &topaint, &tile);
2047 }
2049 }
2050 }
2051 }
2053 canvas->need_redraw = FALSE;
2054 sp_canvas_paint_rect (canvas, topaint.x0, topaint.y0, topaint.x1, topaint.y1);
2056 return TRUE;
2057 }
2059 /**
2060 * Helper that invokes update, paint, and repick on canvas.
2061 */
2062 static int
2063 do_update (SPCanvas *canvas)
2064 {
2065 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2066 return TRUE;
2068 /* Cause the update if necessary */
2069 if (canvas->need_update) {
2070 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2071 canvas->need_update = FALSE;
2072 }
2074 /* Paint if able to */
2075 if (GTK_WIDGET_DRAWABLE (canvas)) {
2076 return paint (canvas);
2077 }
2079 /* Pick new current item */
2080 while (canvas->need_repick) {
2081 canvas->need_repick = FALSE;
2082 pick_current_item (canvas, &canvas->pick_event);
2083 }
2085 return TRUE;
2086 }
2088 /**
2089 * Idle handler for the canvas that deals with pending updates and redraws.
2090 */
2091 static gint
2092 idle_handler (gpointer data)
2093 {
2094 GDK_THREADS_ENTER ();
2096 SPCanvas *canvas = SP_CANVAS (data);
2098 const int ret = do_update (canvas);
2100 if (ret) {
2101 /* Reset idle id */
2102 canvas->idle_id = 0;
2103 }
2105 GDK_THREADS_LEAVE ();
2107 return !ret;
2108 }
2110 /**
2111 * Convenience function to add an idle handler to a canvas.
2112 */
2113 static void
2114 add_idle (SPCanvas *canvas)
2115 {
2116 if (canvas->idle_id != 0)
2117 return;
2119 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2120 }
2122 /**
2123 * Returns the root group of the specified canvas.
2124 */
2125 SPCanvasGroup *
2126 sp_canvas_root (SPCanvas *canvas)
2127 {
2128 g_return_val_if_fail (canvas != NULL, NULL);
2129 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2131 return SP_CANVAS_GROUP (canvas->root);
2132 }
2134 /**
2135 * Scrolls canvas to specific position.
2136 */
2137 void
2138 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear)
2139 {
2140 g_return_if_fail (canvas != NULL);
2141 g_return_if_fail (SP_IS_CANVAS (canvas));
2143 int ix = (int) (cx + 0.5);
2144 int iy = (int) (cy + 0.5);
2145 int dx = ix - canvas->x0;
2146 int dy = iy - canvas->y0;
2148 canvas->dx0 = cx;
2149 canvas->dy0 = cy;
2150 canvas->x0 = ix;
2151 canvas->y0 = iy;
2153 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+canvas->widget.allocation.width,canvas->y0+canvas->widget.allocation.height);
2155 if (!clear) {
2156 // scrolling without zoom; redraw only the newly exposed areas
2157 if ((dx != 0) || (dy != 0)) {
2158 int width, height;
2159 width = canvas->widget.allocation.width;
2160 height = canvas->widget.allocation.height;
2161 if (GTK_WIDGET_REALIZED (canvas)) {
2162 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2163 gdk_window_process_updates (SP_CANVAS_WINDOW (canvas), TRUE);
2164 }
2165 if (dx < 0) {
2166 sp_canvas_request_redraw (canvas, ix + 0, iy + 0, ix - dx, iy + height);
2167 } else if (dx > 0) {
2168 sp_canvas_request_redraw (canvas, ix + width - dx, iy + 0, ix + width, iy + height);
2169 }
2170 if (dy < 0) {
2171 sp_canvas_request_redraw (canvas, ix + 0, iy + 0, ix + width, iy - dy);
2172 } else if (dy > 0) {
2173 sp_canvas_request_redraw (canvas, ix + 0, iy + height - dy, ix + width, iy + height);
2174 }
2175 }
2176 } else {
2177 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2178 }
2179 }
2181 /**
2182 * Updates canvas if necessary.
2183 */
2184 void
2185 sp_canvas_update_now (SPCanvas *canvas)
2186 {
2187 g_return_if_fail (canvas != NULL);
2188 g_return_if_fail (SP_IS_CANVAS (canvas));
2190 if (!(canvas->need_update ||
2191 canvas->need_redraw))
2192 return;
2194 remove_idle (canvas);
2195 do_update (canvas);
2196 }
2198 /**
2199 * Update callback for canvas widget.
2200 */
2201 static void
2202 sp_canvas_request_update (SPCanvas *canvas)
2203 {
2204 canvas->need_update = TRUE;
2205 add_idle (canvas);
2206 }
2208 /**
2209 * Forces redraw of rectangular canvas area.
2210 */
2211 void
2212 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2213 {
2214 NRRectL bbox;
2215 NRRectL visible;
2216 NRRectL clip;
2218 g_return_if_fail (canvas != NULL);
2219 g_return_if_fail (SP_IS_CANVAS (canvas));
2221 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2222 if ((x0 >= x1) || (y0 >= y1)) return;
2224 bbox.x0 = x0;
2225 bbox.y0 = y0;
2226 bbox.x1 = x1;
2227 bbox.y1 = y1;
2229 visible.x0 = canvas->x0;
2230 visible.y0 = canvas->y0;
2231 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2232 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2234 nr_rect_l_intersect (&clip, &bbox, &visible);
2236 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2237 add_idle (canvas);
2238 }
2240 /**
2241 * Sets world coordinates from win and canvas.
2242 */
2243 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2244 {
2245 g_return_if_fail (canvas != NULL);
2246 g_return_if_fail (SP_IS_CANVAS (canvas));
2248 if (worldx) *worldx = canvas->x0 + winx;
2249 if (worldy) *worldy = canvas->y0 + winy;
2250 }
2252 /**
2253 * Sets win coordinates from world and canvas.
2254 */
2255 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2256 {
2257 g_return_if_fail (canvas != NULL);
2258 g_return_if_fail (SP_IS_CANVAS (canvas));
2260 if (winx) *winx = worldx - canvas->x0;
2261 if (winy) *winy = worldy - canvas->y0;
2262 }
2264 /**
2265 * Converts point from win to world coordinates.
2266 */
2267 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2268 {
2269 g_assert (canvas != NULL);
2270 g_assert (SP_IS_CANVAS (canvas));
2272 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2273 }
2275 /**
2276 * Converts point from world to win coordinates.
2277 */
2278 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2279 {
2280 g_assert (canvas != NULL);
2281 g_assert (SP_IS_CANVAS (canvas));
2283 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2284 }
2286 /**
2287 * Returns true if point given in world coordinates is inside window.
2288 */
2289 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2290 {
2291 g_assert( canvas != NULL );
2292 g_assert(SP_IS_CANVAS(canvas));
2294 using NR::X;
2295 using NR::Y;
2296 GtkWidget const &w = *GTK_WIDGET(canvas);
2297 return ( ( canvas->x0 <= world[X] ) &&
2298 ( canvas->y0 <= world[Y] ) &&
2299 ( world[X] < canvas->x0 + w.allocation.width ) &&
2300 ( world[Y] < canvas->y0 + w.allocation.height ) );
2301 }
2303 /**
2304 * Return canvas window coordinates as NRRect.
2305 */
2306 NR::Rect SPCanvas::getViewbox() const
2307 {
2308 GtkWidget const *w = GTK_WIDGET(this);
2310 return NR::Rect(NR::Point(dx0, dy0),
2311 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2312 }
2314 inline int sp_canvas_tile_floor(int x)
2315 {
2316 return (x&(~31))/32;
2317 }
2319 inline int sp_canvas_tile_ceil(int x)
2320 {
2321 return ((x+31)&(~31))/32;
2322 }
2324 /**
2325 * Helper that changes tile size for canvas redraw.
2326 */
2327 void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2328 {
2329 if ( nl >= nr || nt >= nb ) {
2330 if ( canvas->tiles ) g_free(canvas->tiles);
2331 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2332 canvas->tileH=canvas->tileV=0;
2333 canvas->tiles=NULL;
2334 return;
2335 }
2336 int tl=sp_canvas_tile_floor(nl);
2337 int tt=sp_canvas_tile_floor(nt);
2338 int tr=sp_canvas_tile_ceil(nr);
2339 int tb=sp_canvas_tile_ceil(nb);
2341 int nh=tr-tl,nv=tb-tt;
2342 uint8_t* ntiles=(uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2343 for (int i=tl;i<tr;i++) {
2344 for (int j=tt;j<tb;j++) {
2345 int ind=(i-tl)+(j-tt)*nh;
2346 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2347 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH];
2348 } else {
2349 ntiles[ind]=0;
2350 }
2351 }
2352 }
2353 if ( canvas->tiles ) g_free(canvas->tiles);
2354 canvas->tiles=ntiles;
2355 canvas->tLeft=tl;
2356 canvas->tTop=tt;
2357 canvas->tRight=tr;
2358 canvas->tBottom=tb;
2359 canvas->tileH=nh;
2360 canvas->tileV=nv;
2361 }
2363 /**
2364 * Helper that marks specific canvas rectangle for redraw.
2365 */
2366 void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2367 {
2368 if ( nl >= nr || nt >= nb ) {
2369 return;
2370 }
2371 int tl=sp_canvas_tile_floor(nl);
2372 int tt=sp_canvas_tile_floor(nt);
2373 int tr=sp_canvas_tile_ceil(nr);
2374 int tb=sp_canvas_tile_ceil(nb);
2375 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2376 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2377 if ( tr > canvas->tRight ) tr=canvas->tRight;
2378 if ( tt < canvas->tTop ) tt=canvas->tTop;
2379 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2381 canvas->need_redraw = TRUE;
2383 for (int i=tl;i<tr;i++) {
2384 for (int j=tt;j<tb;j++) {
2385 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]=1;
2386 }
2387 }
2388 }
2391 /*
2392 Local Variables:
2393 mode:c++
2394 c-file-style:"stroustrup"
2395 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2396 indent-tabs-mode:nil
2397 fill-column:99
2398 End:
2399 */
2400 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :