c8de868849c18c44da5f0c2a13a1526b1478646a
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;
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 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1555 canvas->pixmap_gc,
1556 x0 - canvas->x0, y0 - canvas->y0,
1557 x1 - x0, y1 - y0,
1558 GDK_RGB_DITHER_MAX,
1559 buf.buf,
1560 sw * 3,
1561 x0 - canvas->x0, y0 - canvas->y0);
1562 }
1564 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1565 nr_pixelstore_256K_free (buf.buf);
1566 } else {
1567 nr_pixelstore_1M_free (buf.buf);
1568 }
1569 }
1571 /* Paint the given rect, while updating canvas->redraw_aborted and running iterations after each
1572 * buffer; make sure canvas->redraw_aborted never goes past aborted_limit (used for 2-rect
1573 * optimized repaint)
1574 */
1575 static int
1576 sp_canvas_paint_rect_internal (SPCanvas *canvas, NRRectL *rect, NR::ICoord *x_aborted_limit, NR::ICoord *y_aborted_limit)
1577 {
1578 int draw_x1 = rect->x0;
1579 int draw_x2 = rect->x1;
1580 int draw_y1 = rect->y0;
1581 int draw_y2 = rect->y1;
1583 // Here we'll store the time it took to draw the slowest buffer of this paint.
1584 glong slowest_buffer = 0;
1586 // Find the optimal buffer dimensions
1587 int bw = draw_x2 - draw_x1;
1588 int bh = draw_y2 - draw_y1;
1589 if ((bw < 1) || (bh < 1))
1590 return 0;
1591 int sw, sh;
1592 if (canvas->rendermode != RENDERMODE_OUTLINE) { // use 256K as a compromise to not slow down gradients
1593 /* 256K is the cached buffer and we need 3 channels */
1594 if (bw * bh < 87381) { // 256K/3
1595 // We can go with single buffer
1596 sw = bw;
1597 sh = bh;
1598 } else if (bw <= (16 * 341)) {
1599 // Go with row buffer
1600 sw = bw;
1601 sh = 87381 / bw;
1602 } else if (bh <= (16 * 256)) {
1603 // Go with column buffer
1604 sw = 87381 / bh;
1605 sh = bh;
1606 } else {
1607 sw = 341;
1608 sh = 256;
1609 }
1610 } else { // paths only, so 1M works faster
1611 /* 1M is the cached buffer and we need 3 channels */
1612 if (bw * bh < 349525) { // 1M/3
1613 // We can go with single buffer
1614 sw = bw;
1615 sh = bh;
1616 } else if (bw <= (16 * 682)) {
1617 // Go with row buffer
1618 sw = bw;
1619 sh = 349525 / bw;
1620 } else if (bh <= (16 * 512)) {
1621 // Go with column buffer
1622 sw = 349525 / bh;
1623 sh = bh;
1624 } else {
1625 sw = 682;
1626 sh = 512;
1627 }
1628 }
1630 // Will this paint require more than one buffer?
1631 bool multiple_buffers = (((draw_y2 - draw_y1) > sh) || ((draw_x2 - draw_x1) > sw)); // or two_rects
1633 // remember the counter during this paint
1634 long this_count = canvas->redraw_count;
1636 // Time values to measure each buffer's paint time
1637 GTimeVal tstart, tfinish;
1639 // paint from the corner nearest the mouse pointer
1641 gint x, y;
1642 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1643 NR::Point pw = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1645 bool reverse_x = (pw[NR::X] > ((draw_x2 + draw_x1) / 2));
1646 bool reverse_y = (pw[NR::Y] > ((draw_y2 + draw_y1) / 2));
1648 if ((bw > bh) && (sh > sw)) {
1649 int t = sw; sw = sh; sh = t;
1650 }
1652 // This is the main loop which corresponds to the visible left-to-right, top-to-bottom drawing
1653 // of screen blocks (buffers).
1654 for (int y0 = draw_y1; y0 < draw_y2; y0 += sh) {
1655 int y1 = MIN (y0 + sh, draw_y2);
1656 for (int x0 = draw_x1; x0 < draw_x2; x0 += sw) {
1657 int x1 = MIN (x0 + sw, draw_x2);
1659 int dx0 = x0;
1660 int dx1 = x1;
1661 int dy0 = y0;
1662 int dy1 = y1;
1664 if (reverse_x) {
1665 dx0 = (draw_x2 - (x0 + sw)) + draw_x1;
1666 dx0 = MAX (dx0, draw_x1);
1667 dx1 = (draw_x2 - x0) + draw_x1;
1668 dx1 = MIN (dx1, draw_x2);
1669 }
1670 if (reverse_y) {
1671 dy0 = (draw_y2 - (y0 + sh)) + draw_y1;
1672 dy0 = MAX (dy0, draw_y1);
1673 dy1 = (draw_y2 - y0) + draw_y1;
1674 dy1 = MIN (dy1, draw_y2);
1675 }
1677 // OPTIMIZATION IDEA: if drawing is really slow (as measured by canvas->slowest
1678 // buffer), process some events even BEFORE we do any buffers?
1680 // Paint one buffer; measure how long it takes.
1681 g_get_current_time (&tstart);
1682 sp_canvas_paint_single_buffer (canvas, dx0, dy0, dx1, dy1, draw_x1, draw_y1, draw_x2, draw_y2, sw);
1683 g_get_current_time (&tfinish);
1685 // Remember the slowest_buffer of this paint.
1686 glong this_buffer = (tfinish.tv_sec - tstart.tv_sec) * 1000000 + (tfinish.tv_usec - tstart.tv_usec);
1687 if (this_buffer > slowest_buffer)
1688 slowest_buffer = this_buffer;
1690 // After each successful buffer, reduce the rect remaining to redraw by what is already redrawn
1691 if (x1 >= draw_x2) {
1692 if (reverse_y) {
1693 if (canvas->redraw_aborted.y1 > dy0) { canvas->redraw_aborted.y1 = dy0; }
1694 } else {
1695 if (canvas->redraw_aborted.y0 < y1) { canvas->redraw_aborted.y0 = y1; }
1696 }
1697 }
1699 if (y1 >= draw_y2) {
1700 if (reverse_x) {
1701 if (canvas->redraw_aborted.x1 > dx0) { canvas->redraw_aborted.x1 = dx0; }
1702 } else {
1703 if (canvas->redraw_aborted.x0 < x1) { canvas->redraw_aborted.x0 = x1; }
1704 }
1705 }
1707 // INTERRUPTIBLE DISPLAY:
1708 // Process events that may have arrived while we were busy drawing;
1709 // only if we're drawing multiple buffers, and only if this one was not very fast,
1710 // and only if we're allowed to interrupt this redraw
1711 bool ok_to_interrupt = (multiple_buffers && this_buffer > 25000);
1712 if (ok_to_interrupt && (canvas->forced_redraw_limit != -1)) {
1713 ok_to_interrupt = (canvas->forced_redraw_count < canvas->forced_redraw_limit);
1714 }
1716 if (ok_to_interrupt) {
1717 // Run at most max_iterations of the main loop; we cannot process ALL events
1718 // here because some things (e.g. rubberband) flood with dirtying events but will
1719 // not redraw themselves
1720 int max_iterations = 10;
1721 int iterations = 0;
1722 while (Gtk::Main::events_pending() && iterations++ < max_iterations) {
1723 Gtk::Main::iteration(false);
1724 // If one of the iterations has redrawn by itself, abort
1725 if (this_count != canvas->redraw_count) {
1726 canvas->slowest_buffer = slowest_buffer;
1727 if (canvas->forced_redraw_limit != -1) {
1728 canvas->forced_redraw_count++;
1729 }
1730 return 1; // interrupted
1731 }
1732 }
1734 // If not aborted so far, check if the events set redraw or update flags;
1735 // if so, force update and abort
1736 if (canvas->need_redraw || canvas->need_update) {
1737 canvas->slowest_buffer = slowest_buffer;
1738 if (canvas->forced_redraw_limit != -1) {
1739 canvas->forced_redraw_count++;
1740 }
1741 do_update (canvas);
1742 return 1; // interrupted
1743 }
1744 }
1745 }
1746 }
1748 // Remember the slowest buffer of this paint in canvas
1749 canvas->slowest_buffer = slowest_buffer;
1751 return 0; // finished
1752 }
1755 /**
1756 * Helper that draws a specific rectangular part of the canvas.
1757 */
1758 static void
1759 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1760 {
1761 g_return_if_fail (!canvas->need_update);
1763 // Monotonously increment the canvas-global counter on each paint. This will let us find out
1764 // when a new paint happened in event processing during this paint, so we can abort it.
1765 canvas->redraw_count++;
1767 NRRectL rect;
1768 rect.x0 = xx0;
1769 rect.x1 = xx1;
1770 rect.y0 = yy0;
1771 rect.y1 = yy1;
1773 // Clip rect-to-draw by the current visible area
1774 rect.x0 = MAX (rect.x0, canvas->x0);
1775 rect.y0 = MAX (rect.y0, canvas->y0);
1776 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1777 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1779 // Clip rect-aborted-last-time by the current visible area
1780 canvas->redraw_aborted.x0 = MAX (canvas->redraw_aborted.x0, canvas->x0);
1781 canvas->redraw_aborted.y0 = MAX (canvas->redraw_aborted.y0, canvas->y0);
1782 canvas->redraw_aborted.x1 = MIN (canvas->redraw_aborted.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1783 canvas->redraw_aborted.y1 = MIN (canvas->redraw_aborted.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1785 if (canvas->redraw_aborted.x0 < canvas->redraw_aborted.x1 && canvas->redraw_aborted.y0 < canvas->redraw_aborted.y1) {
1786 // There was an aborted redraw last time, now we need to redraw BOTH it and the new rect.
1788 // save the old aborted rect in case we decide to paint it separately (see below)
1789 NRRectL aborted = canvas->redraw_aborted;
1791 // calculate the rectangle union of the both rects (the smallest rectangle which covers both)
1792 NRRectL nion;
1793 nr_rect_l_union (&nion, &rect, &aborted);
1795 // subtract one of the rects-to-draw from the other (the smallest rectangle which covers
1796 // all of the first not covered by the second)
1797 NRRectL rect_minus_aborted;
1798 nr_rect_l_subtract (&rect_minus_aborted, &rect, &aborted);
1800 // Initially, the rect to redraw later (in case we're aborted) is the same as the union of both rects
1801 canvas->redraw_aborted = nion;
1803 // calculate areas of the three rects
1804 if ((nr_rect_l_area(&rect_minus_aborted) + nr_rect_l_area(&aborted)) * 1.2 < nr_rect_l_area(&nion)) {
1805 // If the summary area of the two rects is significantly (at least by 20%) less than
1806 // the area of their rectangular union, it makes sense to paint the two rects
1807 // separately instead of painting their union. This gives a significant speedup when,
1808 // for example, your current canvas is almost painted, with only a strip at bottom
1809 // left, and at that moment you abort it by scrolling down which reveals a new strip at
1810 // the top. Straightforward painting of the union of the aborted rect and the new rect
1811 // will have to repaint the entire canvas! By contrast, the optimized approach below
1812 // paints the two narrow strips in order which is much faster.
1814 // find out which rect to draw first - compare them first by y then by x of the top left corners
1815 NRRectL *first;
1816 NRRectL *second;
1817 if (rect.y0 == aborted.y0) {
1818 if (rect.x0 < aborted.x0) {
1819 first = ▭
1820 second = &aborted;
1821 } else {
1822 second = ▭
1823 first = &aborted;
1824 }
1825 } else if (rect.y0 < aborted.y0) {
1826 first = ▭
1827 second = &aborted;
1828 } else {
1829 second = ▭
1830 first = &aborted;
1831 }
1833 NRRectL second_minus_first;
1834 nr_rect_l_subtract (&second_minus_first, second, first);
1836 // paint the first rect;
1837 if (sp_canvas_paint_rect_internal (canvas, first, &(second_minus_first.x0), &(second_minus_first.y0))) {
1838 // aborted!
1839 return;
1840 }
1842 // if not aborted, assign (second rect minus first) as the new redraw_aborted and paint the same
1843 canvas->redraw_aborted = second_minus_first;
1844 if (sp_canvas_paint_rect_internal (canvas, &second_minus_first, NULL, NULL)) {
1845 return; // aborted
1846 }
1848 } else {
1849 // no need for separate drawing, just draw the union as one rect
1850 if (sp_canvas_paint_rect_internal (canvas, &nion, NULL, NULL)) {
1851 return; // aborted
1852 }
1853 }
1854 } else {
1855 // Nothing was aborted last time, just draw the rect we're given
1857 // Initially, the rect to redraw later (in case we're aborted) is the same as the one we're going to draw now.
1858 canvas->redraw_aborted = rect;
1860 if (sp_canvas_paint_rect_internal (canvas, &rect, NULL, NULL)) {
1861 return; // aborted
1862 }
1863 }
1865 // we've had a full unaborted redraw, reset the full redraw counter
1866 if (canvas->forced_redraw_limit != -1) {
1867 canvas->forced_redraw_count = 0;
1868 }
1869 }
1871 /**
1872 * Force a full redraw after a specified number of interrupted redraws
1873 */
1874 void
1875 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1876 g_return_if_fail(canvas != NULL);
1878 canvas->forced_redraw_limit = count;
1879 canvas->forced_redraw_count = 0;
1880 }
1882 /**
1883 * End forced full redraw requests
1884 */
1885 void
1886 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1887 g_return_if_fail(canvas != NULL);
1889 canvas->forced_redraw_limit = -1;
1890 }
1892 /**
1893 * The canvas widget's expose callback.
1894 */
1895 static gint
1896 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1897 {
1898 SPCanvas *canvas = SP_CANVAS (widget);
1900 if (!GTK_WIDGET_DRAWABLE (widget) ||
1901 (event->window != SP_CANVAS_WINDOW (canvas)))
1902 return FALSE;
1904 int n_rects;
1905 GdkRectangle *rects;
1906 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1908 for (int i = 0; i < n_rects; i++) {
1909 NRRectL rect;
1911 rect.x0 = rects[i].x + canvas->x0;
1912 rect.y0 = rects[i].y + canvas->y0;
1913 rect.x1 = rect.x0 + rects[i].width;
1914 rect.y1 = rect.y0 + rects[i].height;
1916 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1917 }
1919 if (n_rects > 0)
1920 g_free (rects);
1922 return FALSE;
1923 }
1925 /**
1926 * The canvas widget's keypress callback.
1927 */
1928 static gint
1929 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1930 {
1931 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1932 }
1934 /**
1935 * Crossing event handler for the canvas.
1936 */
1937 static gint
1938 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1939 {
1940 SPCanvas *canvas = SP_CANVAS (widget);
1942 if (event->window != SP_CANVAS_WINDOW (canvas))
1943 return FALSE;
1945 canvas->state = event->state;
1946 return pick_current_item (canvas, (GdkEvent *) event);
1947 }
1949 /**
1950 * Focus in handler for the canvas.
1951 */
1952 static gint
1953 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1954 {
1955 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1957 SPCanvas *canvas = SP_CANVAS (widget);
1959 if (canvas->focused_item) {
1960 return emit_event (canvas, (GdkEvent *) event);
1961 } else {
1962 return FALSE;
1963 }
1964 }
1966 /**
1967 * Focus out handler for the canvas.
1968 */
1969 static gint
1970 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1971 {
1972 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1974 SPCanvas *canvas = SP_CANVAS (widget);
1976 if (canvas->focused_item)
1977 return emit_event (canvas, (GdkEvent *) event);
1978 else
1979 return FALSE;
1980 }
1982 /**
1983 * Helper that repaints the areas in the canvas that need it.
1984 */
1985 static int
1986 paint (SPCanvas *canvas)
1987 {
1988 if (canvas->need_update) {
1989 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1990 canvas->need_update = FALSE;
1991 }
1993 if (!canvas->need_redraw)
1994 return TRUE;
1996 GtkWidget const *widget = GTK_WIDGET(canvas);
1997 int const canvas_x1 = canvas->x0 + widget->allocation.width;
1998 int const canvas_y1 = canvas->y0 + widget->allocation.height;
2000 NRRectL topaint;
2001 topaint.x0 = topaint.y0 = topaint.x1 = topaint.y1 = 0;
2003 for (int j=canvas->tTop&(~3);j<canvas->tBottom;j+=4) {
2004 for (int i=canvas->tLeft&(~3);i<canvas->tRight;i+=4) {
2005 int mode=0;
2007 int pl=i+1,pr=i,pt=j+4,pb=j;
2008 for (int l=MAX(j,canvas->tTop);l<MIN(j+4,canvas->tBottom);l++) {
2009 for (int k=MAX(i,canvas->tLeft);k<MIN(i+4,canvas->tRight);k++) {
2010 if ( canvas->tiles[(k-canvas->tLeft)+(l-canvas->tTop)*canvas->tileH] ) {
2011 mode|=1<<((k-i)+(l-j)*4);
2012 if ( k < pl ) pl=k;
2013 if ( k+1 > pr ) pr=k+1;
2014 if ( l < pt ) pt=l;
2015 if ( l+1 > pb ) pb=l+1;
2016 }
2017 canvas->tiles[(k-canvas->tLeft)+(l-canvas->tTop)*canvas->tileH]=0;
2018 }
2019 }
2021 if ( mode ) {
2022 NRRectL tile;
2023 tile.x0 = MAX (pl*32, canvas->x0);
2024 tile.y0 = MAX (pt*32, canvas->y0);
2025 tile.x1 = MIN (pr*32, canvas_x1);
2026 tile.y1 = MIN (pb*32, canvas_y1);
2027 if ((tile.x0 < tile.x1) && (tile.y0 < tile.y1)) {
2028 nr_rect_l_union (&topaint, &topaint, &tile);
2029 }
2031 }
2032 }
2033 }
2035 canvas->need_redraw = FALSE;
2036 sp_canvas_paint_rect (canvas, topaint.x0, topaint.y0, topaint.x1, topaint.y1);
2038 return TRUE;
2039 }
2041 /**
2042 * Helper that invokes update, paint, and repick on canvas.
2043 */
2044 static int
2045 do_update (SPCanvas *canvas)
2046 {
2047 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2048 return TRUE;
2050 /* Cause the update if necessary */
2051 if (canvas->need_update) {
2052 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2053 canvas->need_update = FALSE;
2054 }
2056 /* Paint if able to */
2057 if (GTK_WIDGET_DRAWABLE (canvas)) {
2058 return paint (canvas);
2059 }
2061 /* Pick new current item */
2062 while (canvas->need_repick) {
2063 canvas->need_repick = FALSE;
2064 pick_current_item (canvas, &canvas->pick_event);
2065 }
2067 return TRUE;
2068 }
2070 /**
2071 * Idle handler for the canvas that deals with pending updates and redraws.
2072 */
2073 static gint
2074 idle_handler (gpointer data)
2075 {
2076 GDK_THREADS_ENTER ();
2078 SPCanvas *canvas = SP_CANVAS (data);
2080 const int ret = do_update (canvas);
2082 if (ret) {
2083 /* Reset idle id */
2084 canvas->idle_id = 0;
2085 }
2087 GDK_THREADS_LEAVE ();
2089 return !ret;
2090 }
2092 /**
2093 * Convenience function to add an idle handler to a canvas.
2094 */
2095 static void
2096 add_idle (SPCanvas *canvas)
2097 {
2098 if (canvas->idle_id != 0)
2099 return;
2101 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2102 }
2104 /**
2105 * Returns the root group of the specified canvas.
2106 */
2107 SPCanvasGroup *
2108 sp_canvas_root (SPCanvas *canvas)
2109 {
2110 g_return_val_if_fail (canvas != NULL, NULL);
2111 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2113 return SP_CANVAS_GROUP (canvas->root);
2114 }
2116 /**
2117 * Scrolls canvas to specific position.
2118 */
2119 void
2120 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear)
2121 {
2122 g_return_if_fail (canvas != NULL);
2123 g_return_if_fail (SP_IS_CANVAS (canvas));
2125 int ix = (int) (cx + 0.5);
2126 int iy = (int) (cy + 0.5);
2127 int dx = ix - canvas->x0;
2128 int dy = iy - canvas->y0;
2130 canvas->dx0 = cx;
2131 canvas->dy0 = cy;
2132 canvas->x0 = ix;
2133 canvas->y0 = iy;
2135 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+canvas->widget.allocation.width,canvas->y0+canvas->widget.allocation.height);
2137 if (!clear) {
2138 // scrolling without zoom; redraw only the newly exposed areas
2139 if ((dx != 0) || (dy != 0)) {
2140 int width, height;
2141 width = canvas->widget.allocation.width;
2142 height = canvas->widget.allocation.height;
2143 if (GTK_WIDGET_REALIZED (canvas)) {
2144 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2145 gdk_window_process_updates (SP_CANVAS_WINDOW (canvas), TRUE);
2146 }
2147 if (dx < 0) {
2148 sp_canvas_request_redraw (canvas, ix + 0, iy + 0, ix - dx, iy + height);
2149 } else if (dx > 0) {
2150 sp_canvas_request_redraw (canvas, ix + width - dx, iy + 0, ix + width, iy + height);
2151 }
2152 if (dy < 0) {
2153 sp_canvas_request_redraw (canvas, ix + 0, iy + 0, ix + width, iy - dy);
2154 } else if (dy > 0) {
2155 sp_canvas_request_redraw (canvas, ix + 0, iy + height - dy, ix + width, iy + height);
2156 }
2157 }
2158 } else {
2159 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2160 }
2161 }
2163 /**
2164 * Updates canvas if necessary.
2165 */
2166 void
2167 sp_canvas_update_now (SPCanvas *canvas)
2168 {
2169 g_return_if_fail (canvas != NULL);
2170 g_return_if_fail (SP_IS_CANVAS (canvas));
2172 if (!(canvas->need_update ||
2173 canvas->need_redraw))
2174 return;
2176 remove_idle (canvas);
2177 do_update (canvas);
2178 }
2180 /**
2181 * Update callback for canvas widget.
2182 */
2183 static void
2184 sp_canvas_request_update (SPCanvas *canvas)
2185 {
2186 canvas->need_update = TRUE;
2187 add_idle (canvas);
2188 }
2190 /**
2191 * Forces redraw of rectangular canvas area.
2192 */
2193 void
2194 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2195 {
2196 NRRectL bbox;
2197 NRRectL visible;
2198 NRRectL clip;
2200 g_return_if_fail (canvas != NULL);
2201 g_return_if_fail (SP_IS_CANVAS (canvas));
2203 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2204 if ((x0 >= x1) || (y0 >= y1)) return;
2206 bbox.x0 = x0;
2207 bbox.y0 = y0;
2208 bbox.x1 = x1;
2209 bbox.y1 = y1;
2211 visible.x0 = canvas->x0;
2212 visible.y0 = canvas->y0;
2213 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2214 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2216 nr_rect_l_intersect (&clip, &bbox, &visible);
2218 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2219 add_idle (canvas);
2220 }
2222 /**
2223 * Sets world coordinates from win and canvas.
2224 */
2225 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2226 {
2227 g_return_if_fail (canvas != NULL);
2228 g_return_if_fail (SP_IS_CANVAS (canvas));
2230 if (worldx) *worldx = canvas->x0 + winx;
2231 if (worldy) *worldy = canvas->y0 + winy;
2232 }
2234 /**
2235 * Sets win coordinates from world and canvas.
2236 */
2237 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2238 {
2239 g_return_if_fail (canvas != NULL);
2240 g_return_if_fail (SP_IS_CANVAS (canvas));
2242 if (winx) *winx = worldx - canvas->x0;
2243 if (winy) *winy = worldy - canvas->y0;
2244 }
2246 /**
2247 * Converts point from win to world coordinates.
2248 */
2249 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2250 {
2251 g_assert (canvas != NULL);
2252 g_assert (SP_IS_CANVAS (canvas));
2254 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2255 }
2257 /**
2258 * Converts point from world to win coordinates.
2259 */
2260 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2261 {
2262 g_assert (canvas != NULL);
2263 g_assert (SP_IS_CANVAS (canvas));
2265 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2266 }
2268 /**
2269 * Returns true if point given in world coordinates is inside window.
2270 */
2271 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2272 {
2273 g_assert( canvas != NULL );
2274 g_assert(SP_IS_CANVAS(canvas));
2276 using NR::X;
2277 using NR::Y;
2278 GtkWidget const &w = *GTK_WIDGET(canvas);
2279 return ( ( canvas->x0 <= world[X] ) &&
2280 ( canvas->y0 <= world[Y] ) &&
2281 ( world[X] < canvas->x0 + w.allocation.width ) &&
2282 ( world[Y] < canvas->y0 + w.allocation.height ) );
2283 }
2285 /**
2286 * Return canvas window coordinates as NRRect.
2287 */
2288 NR::Rect SPCanvas::getViewbox() const
2289 {
2290 GtkWidget const *w = GTK_WIDGET(this);
2292 return NR::Rect(NR::Point(dx0, dy0),
2293 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2294 }
2296 inline int sp_canvas_tile_floor(int x)
2297 {
2298 return (x&(~31))/32;
2299 }
2301 inline int sp_canvas_tile_ceil(int x)
2302 {
2303 return ((x+31)&(~31))/32;
2304 }
2306 /**
2307 * Helper that changes tile size for canvas redraw.
2308 */
2309 void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2310 {
2311 if ( nl >= nr || nt >= nb ) {
2312 if ( canvas->tiles ) g_free(canvas->tiles);
2313 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2314 canvas->tileH=canvas->tileV=0;
2315 canvas->tiles=NULL;
2316 return;
2317 }
2318 int tl=sp_canvas_tile_floor(nl);
2319 int tt=sp_canvas_tile_floor(nt);
2320 int tr=sp_canvas_tile_ceil(nr);
2321 int tb=sp_canvas_tile_ceil(nb);
2323 int nh=tr-tl,nv=tb-tt;
2324 uint8_t* ntiles=(uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2325 for (int i=tl;i<tr;i++) {
2326 for (int j=tt;j<tb;j++) {
2327 int ind=(i-tl)+(j-tt)*nh;
2328 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2329 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH];
2330 } else {
2331 ntiles[ind]=0;
2332 }
2333 }
2334 }
2335 if ( canvas->tiles ) g_free(canvas->tiles);
2336 canvas->tiles=ntiles;
2337 canvas->tLeft=tl;
2338 canvas->tTop=tt;
2339 canvas->tRight=tr;
2340 canvas->tBottom=tb;
2341 canvas->tileH=nh;
2342 canvas->tileV=nv;
2343 }
2345 /**
2346 * Helper that marks specific canvas rectangle for redraw.
2347 */
2348 void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2349 {
2350 if ( nl >= nr || nt >= nb ) {
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);
2357 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2358 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2359 if ( tr > canvas->tRight ) tr=canvas->tRight;
2360 if ( tt < canvas->tTop ) tt=canvas->tTop;
2361 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2363 canvas->need_redraw = TRUE;
2365 for (int i=tl;i<tr;i++) {
2366 for (int j=tt;j<tb;j++) {
2367 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]=1;
2368 }
2369 }
2370 }
2373 /*
2374 Local Variables:
2375 mode:c++
2376 c-file-style:"stroustrup"
2377 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2378 indent-tabs-mode:nil
2379 fill-column:99
2380 End:
2381 */
2382 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :