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>
37 enum {
38 RENDERMODE_NORMAL,
39 RENDERMODE_NOAA,
40 RENDERMODE_OUTLINE
41 };
43 const gint sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
45 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
47 enum {
48 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
49 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
50 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
51 };
53 /**
54 * A group of Items.
55 */
56 struct SPCanvasGroup {
57 SPCanvasItem item;
59 GList *items, *last;
60 };
62 /**
63 * The SPCanvasGroup vtable.
64 */
65 struct SPCanvasGroupClass {
66 SPCanvasItemClass parent_class;
67 };
69 /**
70 * The SPCanvas vtable.
71 */
72 struct SPCanvasClass {
73 GtkWidgetClass parent_class;
74 };
76 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
77 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
79 /* SPCanvasItem */
81 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
84 static void sp_canvas_request_update (SPCanvas *canvas);
86 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
87 static void sp_canvas_item_init (SPCanvasItem *item);
88 static void sp_canvas_item_dispose (GObject *object);
89 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args);
91 static int emit_event (SPCanvas *canvas, GdkEvent *event);
93 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
95 static GtkObjectClass *item_parent_class;
97 /**
98 * Registers the SPCanvasItem class with Glib and returns its type number.
99 */
100 GType
101 sp_canvas_item_get_type (void)
102 {
103 static GType type = 0;
104 if (!type) {
105 static const GTypeInfo info = {
106 sizeof (SPCanvasItemClass),
107 NULL, NULL,
108 (GClassInitFunc) sp_canvas_item_class_init,
109 NULL, NULL,
110 sizeof (SPCanvasItem),
111 0,
112 (GInstanceInitFunc) sp_canvas_item_init,
113 NULL
114 };
115 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
116 }
118 return type;
119 }
121 /**
122 * Initializes the SPCanvasItem vtable and the "event" signal.
123 */
124 static void
125 sp_canvas_item_class_init (SPCanvasItemClass *klass)
126 {
127 GObjectClass *object_class = (GObjectClass *) klass;
129 /* fixme: Derive from GObject */
130 item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
132 item_signals[ITEM_EVENT] = g_signal_new ("event",
133 G_TYPE_FROM_CLASS (klass),
134 G_SIGNAL_RUN_LAST,
135 G_STRUCT_OFFSET (SPCanvasItemClass, event),
136 NULL, NULL,
137 sp_marshal_BOOLEAN__POINTER,
138 G_TYPE_BOOLEAN, 1,
139 GDK_TYPE_EVENT);
141 object_class->dispose = sp_canvas_item_dispose;
142 }
144 /**
145 * Callback for initialization of SPCanvasItem.
146 */
147 static void
148 sp_canvas_item_init (SPCanvasItem *item)
149 {
150 item->flags |= SP_CANVAS_ITEM_VISIBLE;
151 item->xform = NR::Matrix(NR::identity());
152 }
154 /**
155 * Constructs new SPCanvasItem on SPCanvasGroup.
156 */
157 SPCanvasItem *
158 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, const gchar *first_arg_name, ...)
159 {
160 va_list args;
162 g_return_val_if_fail (parent != NULL, NULL);
163 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
164 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
166 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
168 va_start (args, first_arg_name);
169 sp_canvas_item_construct (item, parent, first_arg_name, args);
170 va_end (args);
172 return item;
173 }
175 /**
176 * Sets up the newly created SPCanvasItem.
177 *
178 * We make it static for encapsulation reasons since it was nowhere used.
179 */
180 static void
181 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args)
182 {
183 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
184 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
186 item->parent = SP_CANVAS_ITEM (parent);
187 item->canvas = item->parent->canvas;
189 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
191 group_add (SP_CANVAS_GROUP (item->parent), item);
193 sp_canvas_item_request_update (item);
194 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
195 item->canvas->need_repick = TRUE;
196 }
198 /**
199 * Helper function that requests redraw only if item's visible flag is set.
200 */
201 static void
202 redraw_if_visible (SPCanvasItem *item)
203 {
204 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
205 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
206 }
207 }
209 /**
210 * Callback that removes item from all referers and destroys it.
211 */
212 static void
213 sp_canvas_item_dispose (GObject *object)
214 {
215 SPCanvasItem *item = SP_CANVAS_ITEM (object);
217 redraw_if_visible (item);
218 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
220 if (item == item->canvas->current_item) {
221 item->canvas->current_item = NULL;
222 item->canvas->need_repick = TRUE;
223 }
225 if (item == item->canvas->new_current_item) {
226 item->canvas->new_current_item = NULL;
227 item->canvas->need_repick = TRUE;
228 }
230 if (item == item->canvas->grabbed_item) {
231 item->canvas->grabbed_item = NULL;
232 gdk_pointer_ungrab (GDK_CURRENT_TIME);
233 }
235 if (item == item->canvas->focused_item)
236 item->canvas->focused_item = NULL;
238 if (item->parent) {
239 group_remove (SP_CANVAS_GROUP (item->parent), item);
240 }
242 G_OBJECT_CLASS (item_parent_class)->dispose (object);
243 }
245 /**
246 * Helper function to update item and its children.
247 *
248 * NB! affine is parent2canvas.
249 */
250 static void
251 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
252 {
253 /* Apply the child item's transform */
254 NR::Matrix child_affine = item->xform * affine;
256 /* apply object flags to child flags */
257 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
259 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
260 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
262 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
263 child_flags |= SP_CANVAS_UPDATE_AFFINE;
265 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
266 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
267 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
268 }
270 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
271 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
272 }
274 /**
275 * Helper function to invoke the point method of the item.
276 *
277 * The argument x, y should be in the parent's item-relative coordinate
278 * system. This routine applies the inverse of the item's transform,
279 * maintaining the affine invariant.
280 */
281 static double
282 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
283 {
284 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
285 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
287 return NR_HUGE;
288 }
290 /**
291 * Makes the item's affine transformation matrix be equal to the specified
292 * matrix.
293 *
294 * @item: A canvas item.
295 * @affine: An affine transformation matrix.
296 */
297 void
298 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const& affine)
299 {
300 item->xform = affine;
302 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
303 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
304 if (item->parent != NULL) {
305 sp_canvas_item_request_update (item->parent);
306 } else {
307 sp_canvas_request_update (item->canvas);
308 }
309 }
311 item->canvas->need_repick = TRUE;
312 }
314 /**
315 * Convenience function to reorder items in a group's child list.
316 *
317 * This puts the specified link after the "before" link.
318 */
319 static void
320 put_item_after (GList *link, GList *before)
321 {
322 if (link == before)
323 return;
325 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
327 if (before == NULL) {
328 if (link == parent->items) return;
330 link->prev->next = link->next;
332 if (link->next) {
333 link->next->prev = link->prev;
334 } else {
335 parent->last = link->prev;
336 }
338 link->prev = before;
339 link->next = parent->items;
340 link->next->prev = link;
341 parent->items = link;
342 } else {
343 if ((link == parent->last) && (before == parent->last->prev))
344 return;
346 if (link->next)
347 link->next->prev = link->prev;
349 if (link->prev)
350 link->prev->next = link->next;
351 else {
352 parent->items = link->next;
353 parent->items->prev = NULL;
354 }
356 link->prev = before;
357 link->next = before->next;
359 link->prev->next = link;
361 if (link->next)
362 link->next->prev = link;
363 else
364 parent->last = link;
365 }
366 }
369 /**
370 * Raises the item in its parent's stack by the specified number of positions.
371 *
372 * \param item A canvas item.
373 * \param positions Number of steps to raise the item.
374 *
375 * If the number of positions is greater than the distance to the top of the
376 * stack, then the item is put at the top.
377 */
378 void
379 sp_canvas_item_raise (SPCanvasItem *item, int positions)
380 {
381 g_return_if_fail (item != NULL);
382 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
383 g_return_if_fail (positions >= 0);
385 if (!item->parent || positions == 0)
386 return;
388 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
389 GList *link = g_list_find (parent->items, item);
390 g_assert (link != NULL);
392 GList *before;
393 for (before = link; positions && before; positions--)
394 before = before->next;
396 if (!before)
397 before = parent->last;
399 put_item_after (link, before);
401 redraw_if_visible (item);
402 item->canvas->need_repick = TRUE;
403 }
406 /**
407 * Lowers the item in its parent's stack by the specified number of positions.
408 *
409 * \param item A canvas item.
410 * \param positions Number of steps to lower the item.
411 *
412 * If the number of positions is greater than the distance to the bottom of the
413 * stack, then the item is put at the bottom.
414 **/
415 void
416 sp_canvas_item_lower (SPCanvasItem *item, int positions)
417 {
418 g_return_if_fail (item != NULL);
419 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
420 g_return_if_fail (positions >= 1);
422 if (!item->parent || positions == 0)
423 return;
425 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
426 GList *link = g_list_find (parent->items, item);
427 g_assert (link != NULL);
429 GList *before;
430 if (link->prev)
431 for (before = link->prev; positions && before; positions--)
432 before = before->prev;
433 else
434 before = NULL;
436 put_item_after (link, before);
438 redraw_if_visible (item);
439 item->canvas->need_repick = TRUE;
440 }
442 /**
443 * Sets visible flag on item and requests a redraw.
444 */
445 void
446 sp_canvas_item_show (SPCanvasItem *item)
447 {
448 g_return_if_fail (item != NULL);
449 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
451 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
452 return;
454 item->flags |= SP_CANVAS_ITEM_VISIBLE;
456 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
457 item->canvas->need_repick = TRUE;
458 }
460 /**
461 * Clears visible flag on item and requests a redraw.
462 */
463 void
464 sp_canvas_item_hide (SPCanvasItem *item)
465 {
466 g_return_if_fail (item != NULL);
467 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
469 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
470 return;
472 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
474 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
475 item->canvas->need_repick = TRUE;
476 }
478 /**
479 * Grab item under cursor.
480 *
481 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
482 */
483 int
484 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
485 {
486 g_return_val_if_fail (item != NULL, -1);
487 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
488 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
490 if (item->canvas->grabbed_item)
491 return -1;
493 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
494 return -1;
496 /* fixme: Top hack (Lauris) */
497 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
498 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
499 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
500 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
501 NULL, cursor, etime);
503 item->canvas->grabbed_item = item;
504 item->canvas->grabbed_event_mask = event_mask;
505 item->canvas->current_item = item; /* So that events go to the grabbed item */
507 return 0;
508 }
510 /**
511 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
512 * mouse.
513 *
514 * \param item A canvas item that holds a grab.
515 * \param etime The timestamp for ungrabbing the mouse.
516 */
517 void
518 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
519 {
520 g_return_if_fail (item != NULL);
521 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
523 if (item->canvas->grabbed_item != item)
524 return;
526 item->canvas->grabbed_item = NULL;
528 gdk_pointer_ungrab (etime);
529 }
531 /**
532 * Returns the product of all transformation matrices from the root item down
533 * to the item.
534 */
535 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
536 {
537 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
539 NR::Matrix affine = NR::identity();
541 while (item) {
542 affine *= item->xform;
543 item = item->parent;
544 }
545 return affine;
546 }
548 /**
549 * Helper that returns true iff item is descendant of parent.
550 */
551 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
552 {
553 while (item) {
554 if (item == parent)
555 return true;
556 item = item->parent;
557 }
559 return false;
560 }
562 /**
563 * Focus canvas, and item under cursor if it is not already focussed.
564 */
565 void
566 sp_canvas_item_grab_focus (SPCanvasItem *item)
567 {
568 g_return_if_fail (item != NULL);
569 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
570 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
572 SPCanvasItem *focused_item = item->canvas->focused_item;
574 if (focused_item) {
575 GdkEvent ev;
576 ev.focus_change.type = GDK_FOCUS_CHANGE;
577 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
578 ev.focus_change.send_event = FALSE;
579 ev.focus_change.in = FALSE;
581 emit_event (item->canvas, &ev);
582 }
584 item->canvas->focused_item = item;
585 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
587 if (focused_item) {
588 GdkEvent ev;
589 ev.focus_change.type = GDK_FOCUS_CHANGE;
590 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
591 ev.focus_change.send_event = FALSE;
592 ev.focus_change.in = TRUE;
594 emit_event (item->canvas, &ev);
595 }
596 }
598 /**
599 * Requests that the canvas queue an update for the specified item.
600 *
601 * To be used only by item implementations.
602 */
603 void
604 sp_canvas_item_request_update (SPCanvasItem *item)
605 {
606 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
607 return;
609 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
611 if (item->parent != NULL) {
612 /* Recurse up the tree */
613 sp_canvas_item_request_update (item->parent);
614 } else {
615 /* Have reached the top of the tree, make sure the update call gets scheduled. */
616 sp_canvas_request_update (item->canvas);
617 }
618 }
620 /**
621 * Returns position of item in group.
622 */
623 gint sp_canvas_item_order (SPCanvasItem * item)
624 {
625 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
626 }
628 /* SPCanvasGroup */
630 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
631 static void sp_canvas_group_init (SPCanvasGroup *group);
632 static void sp_canvas_group_destroy (GtkObject *object);
634 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
635 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
636 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
638 static SPCanvasItemClass *group_parent_class;
640 /**
641 * Registers SPCanvasGroup class with Gtk and returns its type number.
642 */
643 GtkType
644 sp_canvas_group_get_type (void)
645 {
646 static GtkType group_type = 0;
648 if (!group_type) {
649 static const GtkTypeInfo group_info = {
650 "SPCanvasGroup",
651 sizeof (SPCanvasGroup),
652 sizeof (SPCanvasGroupClass),
653 (GtkClassInitFunc) sp_canvas_group_class_init,
654 (GtkObjectInitFunc) sp_canvas_group_init,
655 NULL, NULL, NULL
656 };
658 group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
659 }
661 return group_type;
662 }
664 /**
665 * Class initialization function for SPCanvasGroupClass
666 */
667 static void
668 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
669 {
670 GtkObjectClass *object_class = (GtkObjectClass *) klass;
671 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
673 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
675 object_class->destroy = sp_canvas_group_destroy;
677 item_class->update = sp_canvas_group_update;
678 item_class->render = sp_canvas_group_render;
679 item_class->point = sp_canvas_group_point;
680 }
682 /**
683 * Callback. Empty.
684 */
685 static void
686 sp_canvas_group_init (SPCanvasGroup */*group*/)
687 {
688 /* Nothing here */
689 }
691 /**
692 * Callback that destroys all items in group and calls group's virtual
693 * destroy() function.
694 */
695 static void
696 sp_canvas_group_destroy (GtkObject *object)
697 {
698 g_return_if_fail (object != NULL);
699 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
701 const SPCanvasGroup *group = SP_CANVAS_GROUP (object);
703 GList *list = group->items;
704 while (list) {
705 SPCanvasItem *child = (SPCanvasItem *)list->data;
706 list = list->next;
708 gtk_object_destroy (GTK_OBJECT (child));
709 }
711 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
712 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
713 }
715 /**
716 * Update handler for canvas groups
717 */
718 static void
719 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
720 {
721 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
722 NR::ConvexHull corners(NR::Point(0, 0));
723 bool empty=true;
725 for (GList *list = group->items; list; list = list->next) {
726 SPCanvasItem *i = (SPCanvasItem *)list->data;
728 sp_canvas_item_invoke_update (i, affine, flags);
730 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
731 if (empty) {
732 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
733 empty = false;
734 } else {
735 corners.add(NR::Point(i->x1, i->y1));
736 }
737 corners.add(NR::Point(i->x2, i->y2));
738 }
739 }
741 NR::Rect const &bounds = corners.bounds();
742 item->x1 = bounds.min()[NR::X];
743 item->y1 = bounds.min()[NR::Y];
744 item->x2 = bounds.max()[NR::X];
745 item->y2 = bounds.max()[NR::Y];
746 }
748 /**
749 * Point handler for canvas groups.
750 */
751 static double
752 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
753 {
754 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
755 const double x = p[NR::X];
756 const double y = p[NR::Y];
757 int x1 = (int)(x - item->canvas->close_enough);
758 int y1 = (int)(y - item->canvas->close_enough);
759 int x2 = (int)(x + item->canvas->close_enough);
760 int y2 = (int)(y + item->canvas->close_enough);
762 double best = 0.0;
763 *actual_item = NULL;
765 double dist = 0.0;
767 for (GList *list = group->items; list; list = list->next) {
768 SPCanvasItem *child = (SPCanvasItem *)list->data;
770 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
771 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
773 int has_point;
774 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
775 dist = sp_canvas_item_invoke_point (child, p, &point_item);
776 has_point = TRUE;
777 } else
778 has_point = FALSE;
780 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
781 best = dist;
782 *actual_item = point_item;
783 }
784 }
785 }
787 return best;
788 }
790 /**
791 * Renders all visible canvas group items in buf rectangle.
792 */
793 static void
794 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
795 {
796 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
798 for (GList *list = group->items; list; list = list->next) {
799 SPCanvasItem *child = (SPCanvasItem *)list->data;
800 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
801 if ((child->x1 < buf->rect.x1) &&
802 (child->y1 < buf->rect.y1) &&
803 (child->x2 > buf->rect.x0) &&
804 (child->y2 > buf->rect.y0)) {
805 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
806 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
807 }
808 }
809 }
810 }
812 /**
813 * Adds an item to a canvas group.
814 */
815 static void
816 group_add (SPCanvasGroup *group, SPCanvasItem *item)
817 {
818 gtk_object_ref (GTK_OBJECT (item));
819 gtk_object_sink (GTK_OBJECT (item));
821 if (!group->items) {
822 group->items = g_list_append (group->items, item);
823 group->last = group->items;
824 } else {
825 group->last = g_list_append (group->last, item)->next;
826 }
828 sp_canvas_item_request_update (item);
829 }
831 /**
832 * Removes an item from a canvas group
833 */
834 static void
835 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
836 {
837 g_return_if_fail (group != NULL);
838 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
839 g_return_if_fail (item != NULL);
841 for (GList *children = group->items; children; children = children->next) {
842 if (children->data == item) {
844 /* Unparent the child */
846 item->parent = NULL;
847 gtk_object_unref (GTK_OBJECT (item));
849 /* Remove it from the list */
851 if (children == group->last) group->last = children->prev;
853 group->items = g_list_remove_link (group->items, children);
854 g_list_free (children);
855 break;
856 }
857 }
858 }
860 /* SPCanvas */
862 static void sp_canvas_class_init (SPCanvasClass *klass);
863 static void sp_canvas_init (SPCanvas *canvas);
864 static void sp_canvas_destroy (GtkObject *object);
866 static void sp_canvas_realize (GtkWidget *widget);
867 static void sp_canvas_unrealize (GtkWidget *widget);
869 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
870 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
872 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
873 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
874 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
875 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
876 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
877 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
878 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
879 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
881 static GtkWidgetClass *canvas_parent_class;
883 void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb);
884 void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb);
885 static int do_update (SPCanvas *canvas);
887 /**
888 * Registers the SPCanvas class if necessary, and returns the type ID
889 * associated to it.
890 *
891 * \return The type ID of the SPCanvas class.
892 **/
893 GtkType
894 sp_canvas_get_type (void)
895 {
896 static GtkType canvas_type = 0;
898 if (!canvas_type) {
899 static const GtkTypeInfo canvas_info = {
900 "SPCanvas",
901 sizeof (SPCanvas),
902 sizeof (SPCanvasClass),
903 (GtkClassInitFunc) sp_canvas_class_init,
904 (GtkObjectInitFunc) sp_canvas_init,
905 NULL, NULL, NULL
906 };
908 canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
909 }
911 return canvas_type;
912 }
914 /**
915 * Class initialization function for SPCanvasClass.
916 */
917 static void
918 sp_canvas_class_init (SPCanvasClass *klass)
919 {
920 GtkObjectClass *object_class = (GtkObjectClass *) klass;
921 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
923 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
925 object_class->destroy = sp_canvas_destroy;
927 widget_class->realize = sp_canvas_realize;
928 widget_class->unrealize = sp_canvas_unrealize;
929 widget_class->size_request = sp_canvas_size_request;
930 widget_class->size_allocate = sp_canvas_size_allocate;
931 widget_class->button_press_event = sp_canvas_button;
932 widget_class->button_release_event = sp_canvas_button;
933 widget_class->motion_notify_event = sp_canvas_motion;
934 widget_class->scroll_event = sp_canvas_scroll;
935 widget_class->expose_event = sp_canvas_expose;
936 widget_class->key_press_event = sp_canvas_key;
937 widget_class->key_release_event = sp_canvas_key;
938 widget_class->enter_notify_event = sp_canvas_crossing;
939 widget_class->leave_notify_event = sp_canvas_crossing;
940 widget_class->focus_in_event = sp_canvas_focus_in;
941 widget_class->focus_out_event = sp_canvas_focus_out;
942 }
944 /**
945 * Callback: object initialization for SPCanvas.
946 */
947 static void
948 sp_canvas_init (SPCanvas *canvas)
949 {
950 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
951 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
952 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
954 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
955 canvas->pick_event.crossing.x = 0;
956 canvas->pick_event.crossing.y = 0;
958 /* Create the root item as a special case */
959 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
960 canvas->root->canvas = canvas;
962 gtk_object_ref (GTK_OBJECT (canvas->root));
963 gtk_object_sink (GTK_OBJECT (canvas->root));
965 canvas->need_repick = TRUE;
967 // See comment at in sp-canvas.h.
968 canvas->gen_all_enter_events = false;
970 canvas->tiles=NULL;
971 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
972 canvas->tileH=canvas->tileV=0;
974 canvas->redraw_aborted.x0 = NR_HUGE_L;
975 canvas->redraw_aborted.x1 = -NR_HUGE_L;
976 canvas->redraw_aborted.y0 = NR_HUGE_L;
977 canvas->redraw_aborted.y1 = -NR_HUGE_L;
979 canvas->redraw_count = 0;
981 canvas->forced_redraw_count = 0;
982 canvas->forced_redraw_limit = 0;
984 canvas->slowest_buffer = 0;
985 }
987 /**
988 * Convenience function to remove the idle handler of a canvas.
989 */
990 static void
991 remove_idle (SPCanvas *canvas)
992 {
993 if (canvas->idle_id) {
994 gtk_idle_remove (canvas->idle_id);
995 canvas->idle_id = 0;
996 }
997 }
999 /*
1000 * Removes the transient state of the canvas (idle handler, grabs).
1001 */
1002 static void
1003 shutdown_transients (SPCanvas *canvas)
1004 {
1005 /* We turn off the need_redraw flag, since if the canvas is mapped again
1006 * it will request a redraw anyways. We do not turn off the need_update
1007 * flag, though, because updates are not queued when the canvas remaps
1008 * itself.
1009 */
1010 if (canvas->need_redraw) {
1011 canvas->need_redraw = FALSE;
1012 }
1013 if ( canvas->tiles ) g_free(canvas->tiles);
1014 canvas->tiles=NULL;
1015 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1016 canvas->tileH=canvas->tileV=0;
1018 if (canvas->grabbed_item) {
1019 canvas->grabbed_item = NULL;
1020 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1021 }
1023 remove_idle (canvas);
1024 }
1026 /**
1027 * Destroy handler for SPCanvas.
1028 */
1029 static void
1030 sp_canvas_destroy (GtkObject *object)
1031 {
1032 SPCanvas *canvas = SP_CANVAS (object);
1034 if (canvas->root) {
1035 gtk_object_unref (GTK_OBJECT (canvas->root));
1036 canvas->root = NULL;
1037 }
1039 shutdown_transients (canvas);
1041 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1042 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1043 }
1045 /**
1046 * Returns new canvas as widget.
1047 */
1048 GtkWidget *
1049 sp_canvas_new_aa (void)
1050 {
1051 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1053 return (GtkWidget *) canvas;
1054 }
1056 /**
1057 * The canvas widget's realize callback.
1058 */
1059 static void
1060 sp_canvas_realize (GtkWidget *widget)
1061 {
1062 SPCanvas *canvas = SP_CANVAS (widget);
1064 GdkWindowAttr attributes;
1065 attributes.window_type = GDK_WINDOW_CHILD;
1066 attributes.x = widget->allocation.x;
1067 attributes.y = widget->allocation.y;
1068 attributes.width = widget->allocation.width;
1069 attributes.height = widget->allocation.height;
1070 attributes.wclass = GDK_INPUT_OUTPUT;
1071 attributes.visual = gdk_rgb_get_visual ();
1072 attributes.colormap = gdk_rgb_get_cmap ();
1073 attributes.event_mask = (gtk_widget_get_events (widget) |
1074 GDK_EXPOSURE_MASK |
1075 GDK_BUTTON_PRESS_MASK |
1076 GDK_BUTTON_RELEASE_MASK |
1077 GDK_POINTER_MOTION_MASK |
1078 GDK_PROXIMITY_IN_MASK |
1079 GDK_PROXIMITY_OUT_MASK |
1080 GDK_KEY_PRESS_MASK |
1081 GDK_KEY_RELEASE_MASK |
1082 GDK_ENTER_NOTIFY_MASK |
1083 GDK_LEAVE_NOTIFY_MASK |
1084 GDK_FOCUS_CHANGE_MASK);
1085 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1087 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1088 gdk_window_set_user_data (widget->window, widget);
1089 gtk_widget_set_events(widget, attributes.event_mask);
1091 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1093 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1094 }
1096 /**
1097 * The canvas widget's unrealize callback.
1098 */
1099 static void
1100 sp_canvas_unrealize (GtkWidget *widget)
1101 {
1102 SPCanvas *canvas = SP_CANVAS (widget);
1104 shutdown_transients (canvas);
1106 gdk_gc_destroy (canvas->pixmap_gc);
1107 canvas->pixmap_gc = NULL;
1109 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1110 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1111 }
1113 /**
1114 * The canvas widget's size_request callback.
1115 */
1116 static void
1117 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1118 {
1119 static_cast<void>(SP_CANVAS (widget));
1121 req->width = 256;
1122 req->height = 256;
1123 }
1125 /**
1126 * The canvas widget's size_allocate callback.
1127 */
1128 static void
1129 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1130 {
1131 SPCanvas *canvas = SP_CANVAS (widget);
1133 /* Schedule redraw of new region */
1134 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1135 if (allocation->width > widget->allocation.width) {
1136 sp_canvas_request_redraw (canvas,
1137 canvas->x0 + widget->allocation.width,
1138 0,
1139 canvas->x0 + allocation->width,
1140 canvas->y0 + allocation->height);
1141 }
1142 if (allocation->height > widget->allocation.height) {
1143 sp_canvas_request_redraw (canvas,
1144 0,
1145 canvas->y0 + widget->allocation.height,
1146 canvas->x0 + allocation->width,
1147 canvas->y0 + allocation->height);
1148 }
1150 widget->allocation = *allocation;
1152 if (GTK_WIDGET_REALIZED (widget)) {
1153 gdk_window_move_resize (widget->window,
1154 widget->allocation.x, widget->allocation.y,
1155 widget->allocation.width, widget->allocation.height);
1156 }
1157 }
1159 /**
1160 * Helper that emits an event for an item in the canvas, be it the current
1161 * item, grabbed item, or focused item, as appropriate.
1162 */
1163 static int
1164 emit_event (SPCanvas *canvas, GdkEvent *event)
1165 {
1166 guint mask;
1168 if (canvas->grabbed_item) {
1169 switch (event->type) {
1170 case GDK_ENTER_NOTIFY:
1171 mask = GDK_ENTER_NOTIFY_MASK;
1172 break;
1173 case GDK_LEAVE_NOTIFY:
1174 mask = GDK_LEAVE_NOTIFY_MASK;
1175 break;
1176 case GDK_MOTION_NOTIFY:
1177 mask = GDK_POINTER_MOTION_MASK;
1178 break;
1179 case GDK_BUTTON_PRESS:
1180 case GDK_2BUTTON_PRESS:
1181 case GDK_3BUTTON_PRESS:
1182 mask = GDK_BUTTON_PRESS_MASK;
1183 break;
1184 case GDK_BUTTON_RELEASE:
1185 mask = GDK_BUTTON_RELEASE_MASK;
1186 break;
1187 case GDK_KEY_PRESS:
1188 mask = GDK_KEY_PRESS_MASK;
1189 break;
1190 case GDK_KEY_RELEASE:
1191 mask = GDK_KEY_RELEASE_MASK;
1192 break;
1193 case GDK_SCROLL:
1194 mask = GDK_SCROLL;
1195 break;
1196 default:
1197 mask = 0;
1198 break;
1199 }
1201 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1202 }
1204 /* Convert to world coordinates -- we have two cases because of diferent
1205 * offsets of the fields in the event structures.
1206 */
1208 GdkEvent ev = *event;
1210 switch (ev.type) {
1211 case GDK_ENTER_NOTIFY:
1212 case GDK_LEAVE_NOTIFY:
1213 ev.crossing.x += canvas->x0;
1214 ev.crossing.y += canvas->y0;
1215 break;
1216 case GDK_MOTION_NOTIFY:
1217 case GDK_BUTTON_PRESS:
1218 case GDK_2BUTTON_PRESS:
1219 case GDK_3BUTTON_PRESS:
1220 case GDK_BUTTON_RELEASE:
1221 ev.motion.x += canvas->x0;
1222 ev.motion.y += canvas->y0;
1223 break;
1224 default:
1225 break;
1226 }
1228 /* Choose where we send the event */
1230 /* canvas->current_item becomes NULL in some cases under Win32
1231 ** (e.g. if the pointer leaves the window). So this is a hack that
1232 ** Lauris applied to SP to get around the problem.
1233 */
1234 SPCanvasItem* item = NULL;
1235 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1236 item = canvas->grabbed_item;
1237 } else {
1238 item = canvas->current_item;
1239 }
1241 if (canvas->focused_item &&
1242 ((event->type == GDK_KEY_PRESS) ||
1243 (event->type == GDK_KEY_RELEASE) ||
1244 (event->type == GDK_FOCUS_CHANGE))) {
1245 item = canvas->focused_item;
1246 }
1248 /* The event is propagated up the hierarchy (for if someone connected to
1249 * a group instead of a leaf event), and emission is stopped if a
1250 * handler returns TRUE, just like for GtkWidget events.
1251 */
1253 gint finished = FALSE;
1255 while (item && !finished) {
1256 gtk_object_ref (GTK_OBJECT (item));
1257 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1258 SPCanvasItem *parent = item->parent;
1259 gtk_object_unref (GTK_OBJECT (item));
1260 item = parent;
1261 }
1263 return finished;
1264 }
1266 /**
1267 * Helper that re-picks the current item in the canvas, based on the event's
1268 * coordinates and emits enter/leave events for items as appropriate.
1269 */
1270 static int
1271 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1272 {
1273 int button_down = 0;
1274 double x, y;
1276 int retval = FALSE;
1278 if (canvas->gen_all_enter_events == false) {
1279 // If a button is down, we'll perform enter and leave events on the
1280 // current item, but not enter on any other item. This is more or
1281 // less like X pointer grabbing for canvas items.
1282 //
1283 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1284 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1286 if (!button_down) canvas->left_grabbed_item = FALSE;
1287 }
1289 /* Save the event in the canvas. This is used to synthesize enter and
1290 * leave events in case the current item changes. It is also used to
1291 * re-pick the current item if the current one gets deleted. Also,
1292 * synthesize an enter event.
1293 */
1294 if (event != &canvas->pick_event) {
1295 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1296 /* these fields have the same offsets in both types of events */
1298 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1299 canvas->pick_event.crossing.window = event->motion.window;
1300 canvas->pick_event.crossing.send_event = event->motion.send_event;
1301 canvas->pick_event.crossing.subwindow = NULL;
1302 canvas->pick_event.crossing.x = event->motion.x;
1303 canvas->pick_event.crossing.y = event->motion.y;
1304 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1305 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1306 canvas->pick_event.crossing.focus = FALSE;
1307 canvas->pick_event.crossing.state = event->motion.state;
1309 /* these fields don't have the same offsets in both types of events */
1311 if (event->type == GDK_MOTION_NOTIFY) {
1312 canvas->pick_event.crossing.x_root = event->motion.x_root;
1313 canvas->pick_event.crossing.y_root = event->motion.y_root;
1314 } else {
1315 canvas->pick_event.crossing.x_root = event->button.x_root;
1316 canvas->pick_event.crossing.y_root = event->button.y_root;
1317 }
1318 } else {
1319 canvas->pick_event = *event;
1320 }
1321 }
1323 /* Don't do anything else if this is a recursive call */
1324 if (canvas->in_repick) return retval;
1326 /* LeaveNotify means that there is no current item, so we don't look for one */
1327 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1328 /* these fields don't have the same offsets in both types of events */
1330 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1331 x = canvas->pick_event.crossing.x;
1332 y = canvas->pick_event.crossing.y;
1333 } else {
1334 x = canvas->pick_event.motion.x;
1335 y = canvas->pick_event.motion.y;
1336 }
1338 /* world coords */
1339 x += canvas->x0;
1340 y += canvas->y0;
1342 /* find the closest item */
1343 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1344 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1345 } else {
1346 canvas->new_current_item = NULL;
1347 }
1348 } else {
1349 canvas->new_current_item = NULL;
1350 }
1352 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1353 return retval; /* current item did not change */
1354 }
1356 /* Synthesize events for old and new current items */
1358 if ((canvas->new_current_item != canvas->current_item)
1359 && (canvas->current_item != NULL)
1360 && !canvas->left_grabbed_item) {
1361 GdkEvent new_event;
1362 SPCanvasItem *item;
1364 item = canvas->current_item;
1366 new_event = canvas->pick_event;
1367 new_event.type = GDK_LEAVE_NOTIFY;
1369 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1370 new_event.crossing.subwindow = NULL;
1371 canvas->in_repick = TRUE;
1372 retval = emit_event (canvas, &new_event);
1373 canvas->in_repick = FALSE;
1374 }
1376 if (canvas->gen_all_enter_events == false) {
1377 // new_current_item may have been set to NULL during the call to
1378 // emit_event() above
1379 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1380 canvas->left_grabbed_item = TRUE;
1381 return retval;
1382 }
1383 }
1385 /* Handle the rest of cases */
1387 canvas->left_grabbed_item = FALSE;
1388 canvas->current_item = canvas->new_current_item;
1390 if (canvas->current_item != NULL) {
1391 GdkEvent new_event;
1393 new_event = canvas->pick_event;
1394 new_event.type = GDK_ENTER_NOTIFY;
1395 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1396 new_event.crossing.subwindow = NULL;
1397 retval = emit_event (canvas, &new_event);
1398 }
1400 return retval;
1401 }
1403 /**
1404 * Button event handler for the canvas.
1405 */
1406 static gint
1407 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1408 {
1409 SPCanvas *canvas = SP_CANVAS (widget);
1411 int retval = FALSE;
1413 /* dispatch normally regardless of the event's window if an item has
1414 has a pointer grab in effect */
1415 if (!canvas->grabbed_item &&
1416 event->window != SP_CANVAS_WINDOW (canvas))
1417 return retval;
1419 int mask;
1420 switch (event->button) {
1421 case 1:
1422 mask = GDK_BUTTON1_MASK;
1423 break;
1424 case 2:
1425 mask = GDK_BUTTON2_MASK;
1426 break;
1427 case 3:
1428 mask = GDK_BUTTON3_MASK;
1429 break;
1430 case 4:
1431 mask = GDK_BUTTON4_MASK;
1432 break;
1433 case 5:
1434 mask = GDK_BUTTON5_MASK;
1435 break;
1436 default:
1437 mask = 0;
1438 }
1440 switch (event->type) {
1441 case GDK_BUTTON_PRESS:
1442 case GDK_2BUTTON_PRESS:
1443 case GDK_3BUTTON_PRESS:
1444 /* Pick the current item as if the button were not pressed, and
1445 * then process the event.
1446 */
1447 canvas->state = event->state;
1448 pick_current_item (canvas, (GdkEvent *) event);
1449 canvas->state ^= mask;
1450 retval = emit_event (canvas, (GdkEvent *) event);
1451 break;
1453 case GDK_BUTTON_RELEASE:
1454 /* Process the event as if the button were pressed, then repick
1455 * after the button has been released
1456 */
1457 canvas->state = event->state;
1458 retval = emit_event (canvas, (GdkEvent *) event);
1459 event->state ^= mask;
1460 canvas->state = event->state;
1461 pick_current_item (canvas, (GdkEvent *) event);
1462 event->state ^= mask;
1463 break;
1465 default:
1466 g_assert_not_reached ();
1467 }
1469 return retval;
1470 }
1472 /**
1473 * Scroll event handler for the canvas.
1474 *
1475 * \todo FIXME: generate motion events to re-select items.
1476 */
1477 static gint
1478 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1479 {
1480 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1481 }
1483 /**
1484 * Motion event handler for the canvas.
1485 */
1486 static int
1487 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1488 {
1489 SPCanvas *canvas = SP_CANVAS (widget);
1491 if (event->window != SP_CANVAS_WINDOW (canvas))
1492 return FALSE;
1494 if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1495 gint x, y;
1496 gdk_window_get_pointer (widget->window, &x, &y, NULL);
1497 event->x = x;
1498 event->y = y;
1499 }
1501 canvas->state = event->state;
1502 pick_current_item (canvas, (GdkEvent *) event);
1504 return emit_event (canvas, (GdkEvent *) event);
1505 }
1507 static void
1508 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)
1509 {
1510 GtkWidget *widget = GTK_WIDGET (canvas);
1512 SPCanvasBuf buf;
1513 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1514 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1515 } else {
1516 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1517 }
1519 buf.buf_rowstride = sw * 3;
1520 buf.rect.x0 = x0;
1521 buf.rect.y0 = y0;
1522 buf.rect.x1 = x1;
1523 buf.rect.y1 = y1;
1524 buf.visible_rect.x0 = draw_x1;
1525 buf.visible_rect.y0 = draw_y1;
1526 buf.visible_rect.x1 = draw_x2;
1527 buf.visible_rect.y1 = draw_y2;
1528 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1529 buf.bg_color = (((color->red & 0xff00) << 8)
1530 | (color->green & 0xff00)
1531 | (color->blue >> 8));
1532 buf.is_empty = true;
1534 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1535 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1536 }
1538 if (buf.is_empty) {
1539 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1540 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1541 canvas->pixmap_gc,
1542 TRUE,
1543 x0 - canvas->x0, y0 - canvas->y0,
1544 x1 - x0, y1 - y0);
1545 } else {
1546 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1547 canvas->pixmap_gc,
1548 x0 - canvas->x0, y0 - canvas->y0,
1549 x1 - x0, y1 - y0,
1550 GDK_RGB_DITHER_MAX,
1551 buf.buf,
1552 sw * 3,
1553 x0 - canvas->x0, y0 - canvas->y0);
1554 }
1556 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1557 nr_pixelstore_256K_free (buf.buf);
1558 } else {
1559 nr_pixelstore_1M_free (buf.buf);
1560 }
1561 }
1563 /**
1564 * Helper that draws a specific rectangular part of the canvas.
1565 */
1566 static void
1567 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1568 {
1569 g_return_if_fail (!canvas->need_update);
1571 // Monotonously increment the canvas-global counter on each paint. This will let us find out
1572 // when a new paint happened in event processing during this paint, so we can abort it.
1573 canvas->redraw_count++;
1575 NRRectL rect;
1576 rect.x0 = xx0;
1577 rect.x1 = xx1;
1578 rect.y0 = yy0;
1579 rect.y1 = yy1;
1581 if (canvas->redraw_aborted.x0 < canvas->redraw_aborted.x1 || canvas->redraw_aborted.y0 < canvas->redraw_aborted.y1) {
1582 // There was an aborted redraw last time, now we need to redraw BOTH it and the new rect.
1584 // OPTIMIZATION IDEA:
1585 // if (area(rect) + area(redraw_aborted)) * 1.2 > area (union)
1586 // then paint their union (as done below)
1587 // else
1588 // two_rects = true; paint new rect and aborted rect SEPARATELY without unioning.
1589 // Without this, when you scroll down and quickly up, the entire screen has to be redrawn,
1590 // because the union of the aborted strip at top and the newly exposed strip at bottom
1591 // covers the whole screen.
1593 // For now, just always do a union.
1594 rect.x0 = MIN(rect.x0, canvas->redraw_aborted.x0);
1595 rect.x1 = MAX(rect.x1, canvas->redraw_aborted.x1);
1596 rect.y0 = MIN(rect.y0, canvas->redraw_aborted.y0);
1597 rect.y1 = MAX(rect.y1, canvas->redraw_aborted.y1);
1598 }
1600 // Clip drawable rect by the visible area
1601 int draw_x1 = MAX (rect.x0, canvas->x0);
1602 int draw_y1 = MAX (rect.y0, canvas->y0);
1603 int draw_x2 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1604 int draw_y2 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1606 // Here we'll store the time it took to draw the slowest buffer of this paint.
1607 glong slowest_buffer = 0;
1609 // Initially, the rect to redraw later (in case we're aborted) is the same as the one we're going to draw now.
1610 canvas->redraw_aborted.x0 = draw_x1;
1611 canvas->redraw_aborted.x1 = draw_x2;
1612 canvas->redraw_aborted.y0 = draw_y1;
1613 canvas->redraw_aborted.y1 = draw_y2;
1615 // Find the optimal buffer dimensions
1616 int bw = draw_x2 - draw_x1;
1617 int bh = draw_y2 - draw_y1;
1618 if ((bw < 1) || (bh < 1))
1619 return;
1620 int sw, sh;
1621 if (canvas->rendermode != RENDERMODE_OUTLINE) { // use 256K as a compromise to not slow down gradients
1622 /* 256K is the cached buffer and we need 3 channels */
1623 if (bw * bh < 87381) { // 256K/3
1624 // We can go with single buffer
1625 sw = bw;
1626 sh = bh;
1627 } else if (bw <= (16 * 341)) {
1628 // Go with row buffer
1629 sw = bw;
1630 sh = 87381 / bw;
1631 } else if (bh <= (16 * 256)) {
1632 // Go with column buffer
1633 sw = 87381 / bh;
1634 sh = bh;
1635 } else {
1636 sw = 341;
1637 sh = 256;
1638 }
1639 } else { // paths only, so 1M works faster
1640 /* 1M is the cached buffer and we need 3 channels */
1641 if (bw * bh < 349525) { // 1M/3
1642 // We can go with single buffer
1643 sw = bw;
1644 sh = bh;
1645 } else if (bw <= (16 * 682)) {
1646 // Go with row buffer
1647 sw = bw;
1648 sh = 349525 / bw;
1649 } else if (bh <= (16 * 512)) {
1650 // Go with column buffer
1651 sw = 349525 / bh;
1652 sh = bh;
1653 } else {
1654 sw = 682;
1655 sh = 512;
1656 }
1657 }
1659 // Will this paint require more than one buffer?
1660 bool multiple_buffers = (((draw_y2 - draw_y1) > sh) || ((draw_x2 - draw_x1) > sw)); // or two_rects
1662 // remember the counter during this paint
1663 long this_count = canvas->redraw_count;
1665 // Time values to measure each buffer's paint time
1666 GTimeVal tstart, tfinish;
1668 // This is the main loop which corresponds to the visible left-to-right, top-to-bottom drawing
1669 // of screen blocks (buffers).
1670 for (int y0 = draw_y1; y0 < draw_y2; y0 += sh) {
1671 int y1 = MIN (y0 + sh, draw_y2);
1672 for (int x0 = draw_x1; x0 < draw_x2; x0 += sw) {
1673 int x1 = MIN (x0 + sw, draw_x2);
1675 // OPTIMIZATION IDEA: if drawing is really slow (as measured by canvas->slowest
1676 // buffer), process some events even BEFORE we do any buffers?
1678 // Paint one buffer; measure how long it takes.
1679 g_get_current_time (&tstart);
1680 sp_canvas_paint_single_buffer (canvas, x0, y0, x1, y1, draw_x1, draw_y1, draw_x2, draw_y2, sw);
1681 g_get_current_time (&tfinish);
1683 // Remember the slowest_buffer of this paint.
1684 glong this_buffer = (tfinish.tv_sec - tstart.tv_sec) * 1000000 + (tfinish.tv_usec - tstart.tv_usec);
1685 if (this_buffer > slowest_buffer)
1686 slowest_buffer = this_buffer;
1688 // After each successful buffer, reduce the rect remaining to redraw by what is already redrawn
1689 if (x1 >= draw_x2 && canvas->redraw_aborted.y0 < y1)
1690 canvas->redraw_aborted.y0 = y1;
1691 if (y1 >= draw_y2 && canvas->redraw_aborted.x0 < x1)
1692 canvas->redraw_aborted.x0 = x1;
1694 // INTERRUPTIBLE DISPLAY:
1695 // Process events that may have arrived while we were busy drawing;
1696 // only if we're drawing multiple buffers, and only if this one was not very fast,
1697 // and only if we're allowed to interrupt this redraw
1698 bool ok_to_interrupt = (multiple_buffers && this_buffer > 25000);
1699 if (ok_to_interrupt && canvas->forced_redraw_limit) {
1700 ok_to_interrupt = (canvas->forced_redraw_count < canvas->forced_redraw_limit);
1701 }
1703 if (ok_to_interrupt) {
1704 // Run at most max_iterations of the main loop; we cannot process ALL events
1705 // here because some things (e.g. rubberband) flood with dirtying events but will
1706 // not redraw themselves
1707 int max_iterations = 10;
1708 int iterations = 0;
1709 while (Gtk::Main::events_pending() && iterations++ < max_iterations) {
1710 Gtk::Main::iteration(false);
1711 // If one of the iterations has redrawn by itself, abort
1712 if (this_count != canvas->redraw_count) {
1713 canvas->slowest_buffer = slowest_buffer;
1714 return;
1715 }
1716 }
1718 // If not aborted so far, check if the events set redraw or update flags;
1719 // if so, force update and abort
1720 if (canvas->need_redraw || canvas->need_update) {
1721 canvas->slowest_buffer = slowest_buffer;
1722 if (canvas->forced_redraw_limit) {
1723 canvas->forced_redraw_count++;
1724 }
1725 do_update (canvas);
1726 return;
1727 }
1728 }
1729 }
1730 }
1732 // Remember the slowest buffer of this paint in canvas
1733 canvas->slowest_buffer = slowest_buffer;
1735 // we've had a full redraw, reset the full redraw counter
1736 if (canvas->forced_redraw_limit) {
1737 canvas->forced_redraw_count = 0;
1738 }
1739 }
1741 /**
1742 * Force a full redraw after a specified number of interrupted redraws
1743 */
1744 void
1745 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1746 g_return_if_fail(canvas != NULL);
1748 canvas->forced_redraw_limit = count;
1749 canvas->forced_redraw_count = 0;
1750 }
1752 /**
1753 * End forced full redraw requests
1754 */
1755 void
1756 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1757 g_return_if_fail(canvas != NULL);
1759 canvas->forced_redraw_limit = 0;
1760 }
1762 /**
1763 * The canvas widget's expose callback.
1764 */
1765 static gint
1766 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1767 {
1768 SPCanvas *canvas = SP_CANVAS (widget);
1770 if (!GTK_WIDGET_DRAWABLE (widget) ||
1771 (event->window != SP_CANVAS_WINDOW (canvas)))
1772 return FALSE;
1774 int n_rects;
1775 GdkRectangle *rects;
1776 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1778 for (int i = 0; i < n_rects; i++) {
1779 NRRectL rect;
1781 rect.x0 = rects[i].x + canvas->x0;
1782 rect.y0 = rects[i].y + canvas->y0;
1783 rect.x1 = rect.x0 + rects[i].width;
1784 rect.y1 = rect.y0 + rects[i].height;
1786 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1787 }
1789 if (n_rects > 0)
1790 g_free (rects);
1792 return FALSE;
1793 }
1795 /**
1796 * The canvas widget's keypress callback.
1797 */
1798 static gint
1799 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1800 {
1801 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1802 }
1804 /**
1805 * Crossing event handler for the canvas.
1806 */
1807 static gint
1808 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1809 {
1810 SPCanvas *canvas = SP_CANVAS (widget);
1812 if (event->window != SP_CANVAS_WINDOW (canvas))
1813 return FALSE;
1815 canvas->state = event->state;
1816 return pick_current_item (canvas, (GdkEvent *) event);
1817 }
1819 /**
1820 * Focus in handler for the canvas.
1821 */
1822 static gint
1823 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1824 {
1825 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1827 SPCanvas *canvas = SP_CANVAS (widget);
1829 if (canvas->focused_item) {
1830 return emit_event (canvas, (GdkEvent *) event);
1831 } else {
1832 return FALSE;
1833 }
1834 }
1836 /**
1837 * Focus out handler for the canvas.
1838 */
1839 static gint
1840 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1841 {
1842 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1844 SPCanvas *canvas = SP_CANVAS (widget);
1846 if (canvas->focused_item)
1847 return emit_event (canvas, (GdkEvent *) event);
1848 else
1849 return FALSE;
1850 }
1852 /**
1853 * Helper that repaints the areas in the canvas that need it.
1854 */
1855 static int
1856 paint (SPCanvas *canvas)
1857 {
1858 if (canvas->need_update) {
1859 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1860 canvas->need_update = FALSE;
1861 }
1863 if (!canvas->need_redraw)
1864 return TRUE;
1866 GtkWidget const *widget = GTK_WIDGET(canvas);
1867 int const canvas_x1 = canvas->x0 + widget->allocation.width;
1868 int const canvas_y1 = canvas->y0 + widget->allocation.height;
1870 NRRectL topaint;
1871 topaint.x0 = topaint.y0 = topaint.x1 = topaint.y1 = 0;
1873 for (int j=canvas->tTop&(~3);j<canvas->tBottom;j+=4) {
1874 for (int i=canvas->tLeft&(~3);i<canvas->tRight;i+=4) {
1875 int mode=0;
1877 int pl=i+1,pr=i,pt=j+4,pb=j;
1878 for (int l=MAX(j,canvas->tTop);l<MIN(j+4,canvas->tBottom);l++) {
1879 for (int k=MAX(i,canvas->tLeft);k<MIN(i+4,canvas->tRight);k++) {
1880 if ( canvas->tiles[(k-canvas->tLeft)+(l-canvas->tTop)*canvas->tileH] ) {
1881 mode|=1<<((k-i)+(l-j)*4);
1882 if ( k < pl ) pl=k;
1883 if ( k+1 > pr ) pr=k+1;
1884 if ( l < pt ) pt=l;
1885 if ( l+1 > pb ) pb=l+1;
1886 }
1887 canvas->tiles[(k-canvas->tLeft)+(l-canvas->tTop)*canvas->tileH]=0;
1888 }
1889 }
1891 if ( mode ) {
1892 NRRectL tile;
1893 tile.x0 = MAX (pl*32, canvas->x0);
1894 tile.y0 = MAX (pt*32, canvas->y0);
1895 tile.x1 = MIN (pr*32, canvas_x1);
1896 tile.y1 = MIN (pb*32, canvas_y1);
1897 if ((tile.x0 < tile.x1) && (tile.y0 < tile.y1)) {
1898 nr_rect_l_union (&topaint, &topaint, &tile);
1899 }
1901 }
1902 }
1903 }
1905 canvas->need_redraw = FALSE;
1906 sp_canvas_paint_rect (canvas, topaint.x0, topaint.y0, topaint.x1, topaint.y1);
1908 return TRUE;
1909 }
1911 /**
1912 * Helper that invokes update, paint, and repick on canvas.
1913 */
1914 static int
1915 do_update (SPCanvas *canvas)
1916 {
1917 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1918 return TRUE;
1920 /* Cause the update if necessary */
1921 if (canvas->need_update) {
1922 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1923 canvas->need_update = FALSE;
1924 }
1926 /* Paint if able to */
1927 if (GTK_WIDGET_DRAWABLE (canvas)) {
1928 return paint (canvas);
1929 }
1931 /* Pick new current item */
1932 while (canvas->need_repick) {
1933 canvas->need_repick = FALSE;
1934 pick_current_item (canvas, &canvas->pick_event);
1935 }
1937 return TRUE;
1938 }
1940 /**
1941 * Idle handler for the canvas that deals with pending updates and redraws.
1942 */
1943 static gint
1944 idle_handler (gpointer data)
1945 {
1946 GDK_THREADS_ENTER ();
1948 SPCanvas *canvas = SP_CANVAS (data);
1950 const int ret = do_update (canvas);
1952 if (ret) {
1953 /* Reset idle id */
1954 canvas->idle_id = 0;
1955 }
1957 GDK_THREADS_LEAVE ();
1959 return !ret;
1960 }
1962 /**
1963 * Convenience function to add an idle handler to a canvas.
1964 */
1965 static void
1966 add_idle (SPCanvas *canvas)
1967 {
1968 if (canvas->idle_id != 0)
1969 return;
1971 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
1972 }
1974 /**
1975 * Returns the root group of the specified canvas.
1976 */
1977 SPCanvasGroup *
1978 sp_canvas_root (SPCanvas *canvas)
1979 {
1980 g_return_val_if_fail (canvas != NULL, NULL);
1981 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
1983 return SP_CANVAS_GROUP (canvas->root);
1984 }
1986 /**
1987 * Scrolls canvas to specific position.
1988 */
1989 void
1990 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear)
1991 {
1992 g_return_if_fail (canvas != NULL);
1993 g_return_if_fail (SP_IS_CANVAS (canvas));
1995 int ix = (int) (cx + 0.5);
1996 int iy = (int) (cy + 0.5);
1997 int dx = ix - canvas->x0;
1998 int dy = iy - canvas->y0;
2000 canvas->dx0 = cx;
2001 canvas->dy0 = cy;
2002 canvas->x0 = ix;
2003 canvas->y0 = iy;
2005 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+canvas->widget.allocation.width,canvas->y0+canvas->widget.allocation.height);
2007 if (!clear) {
2008 // scrolling without zoom; redraw only the newly exposed areas
2009 if ((dx != 0) || (dy != 0)) {
2010 int width, height;
2011 width = canvas->widget.allocation.width;
2012 height = canvas->widget.allocation.height;
2013 if (GTK_WIDGET_REALIZED (canvas)) {
2014 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2015 gdk_window_process_updates (SP_CANVAS_WINDOW (canvas), TRUE);
2016 }
2017 if (dx < 0) {
2018 sp_canvas_request_redraw (canvas, ix + 0, iy + 0, ix - dx, iy + height);
2019 } else if (dx > 0) {
2020 sp_canvas_request_redraw (canvas, ix + width - dx, iy + 0, ix + width, iy + height);
2021 }
2022 if (dy < 0) {
2023 sp_canvas_request_redraw (canvas, ix + 0, iy + 0, ix + width, iy - dy);
2024 } else if (dy > 0) {
2025 sp_canvas_request_redraw (canvas, ix + 0, iy + height - dy, ix + width, iy + height);
2026 }
2027 }
2028 } else {
2029 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2030 }
2031 }
2033 /**
2034 * Updates canvas if necessary.
2035 */
2036 void
2037 sp_canvas_update_now (SPCanvas *canvas)
2038 {
2039 g_return_if_fail (canvas != NULL);
2040 g_return_if_fail (SP_IS_CANVAS (canvas));
2042 if (!(canvas->need_update ||
2043 canvas->need_redraw))
2044 return;
2046 remove_idle (canvas);
2047 do_update (canvas);
2048 }
2050 /**
2051 * Update callback for canvas widget.
2052 */
2053 static void
2054 sp_canvas_request_update (SPCanvas *canvas)
2055 {
2056 canvas->need_update = TRUE;
2057 add_idle (canvas);
2058 }
2060 /**
2061 * Forces redraw of rectangular canvas area.
2062 */
2063 void
2064 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2065 {
2066 NRRectL bbox;
2067 NRRectL visible;
2068 NRRectL clip;
2070 g_return_if_fail (canvas != NULL);
2071 g_return_if_fail (SP_IS_CANVAS (canvas));
2073 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2074 if ((x0 >= x1) || (y0 >= y1)) return;
2076 bbox.x0 = x0;
2077 bbox.y0 = y0;
2078 bbox.x1 = x1;
2079 bbox.y1 = y1;
2081 visible.x0 = canvas->x0;
2082 visible.y0 = canvas->y0;
2083 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2084 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2086 nr_rect_l_intersect (&clip, &bbox, &visible);
2088 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2089 add_idle (canvas);
2090 }
2092 /**
2093 * Sets world coordinates from win and canvas.
2094 */
2095 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2096 {
2097 g_return_if_fail (canvas != NULL);
2098 g_return_if_fail (SP_IS_CANVAS (canvas));
2100 if (worldx) *worldx = canvas->x0 + winx;
2101 if (worldy) *worldy = canvas->y0 + winy;
2102 }
2104 /**
2105 * Sets win coordinates from world and canvas.
2106 */
2107 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2108 {
2109 g_return_if_fail (canvas != NULL);
2110 g_return_if_fail (SP_IS_CANVAS (canvas));
2112 if (winx) *winx = worldx - canvas->x0;
2113 if (winy) *winy = worldy - canvas->y0;
2114 }
2116 /**
2117 * Converts point from win to world coordinates.
2118 */
2119 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2120 {
2121 g_assert (canvas != NULL);
2122 g_assert (SP_IS_CANVAS (canvas));
2124 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2125 }
2127 /**
2128 * Converts point from world to win coordinates.
2129 */
2130 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2131 {
2132 g_assert (canvas != NULL);
2133 g_assert (SP_IS_CANVAS (canvas));
2135 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2136 }
2138 /**
2139 * Returns true if point given in world coordinates is inside window.
2140 */
2141 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2142 {
2143 g_assert( canvas != NULL );
2144 g_assert(SP_IS_CANVAS(canvas));
2146 using NR::X;
2147 using NR::Y;
2148 GtkWidget const &w = *GTK_WIDGET(canvas);
2149 return ( ( canvas->x0 <= world[X] ) &&
2150 ( canvas->y0 <= world[Y] ) &&
2151 ( world[X] < canvas->x0 + w.allocation.width ) &&
2152 ( world[Y] < canvas->y0 + w.allocation.height ) );
2153 }
2155 /**
2156 * Return canvas window coordinates as NRRect.
2157 */
2158 NR::Rect SPCanvas::getViewbox() const
2159 {
2160 GtkWidget const *w = GTK_WIDGET(this);
2162 return NR::Rect(NR::Point(dx0, dy0),
2163 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2164 }
2166 inline int sp_canvas_tile_floor(int x)
2167 {
2168 return (x&(~31))/32;
2169 }
2171 inline int sp_canvas_tile_ceil(int x)
2172 {
2173 return ((x+31)&(~31))/32;
2174 }
2176 /**
2177 * Helper that changes tile size for canvas redraw.
2178 */
2179 void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2180 {
2181 if ( nl >= nr || nt >= nb ) {
2182 if ( canvas->tiles ) g_free(canvas->tiles);
2183 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2184 canvas->tileH=canvas->tileV=0;
2185 canvas->tiles=NULL;
2186 return;
2187 }
2188 int tl=sp_canvas_tile_floor(nl);
2189 int tt=sp_canvas_tile_floor(nt);
2190 int tr=sp_canvas_tile_ceil(nr);
2191 int tb=sp_canvas_tile_ceil(nb);
2193 int nh=tr-tl,nv=tb-tt;
2194 uint8_t* ntiles=(uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2195 for (int i=tl;i<tr;i++) {
2196 for (int j=tt;j<tb;j++) {
2197 int ind=(i-tl)+(j-tt)*nh;
2198 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2199 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH];
2200 } else {
2201 ntiles[ind]=0;
2202 }
2203 }
2204 }
2205 if ( canvas->tiles ) g_free(canvas->tiles);
2206 canvas->tiles=ntiles;
2207 canvas->tLeft=tl;
2208 canvas->tTop=tt;
2209 canvas->tRight=tr;
2210 canvas->tBottom=tb;
2211 canvas->tileH=nh;
2212 canvas->tileV=nv;
2213 }
2215 /**
2216 * Helper that marks specific canvas rectangle for redraw.
2217 */
2218 void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2219 {
2220 if ( nl >= nr || nt >= nb ) {
2221 return;
2222 }
2223 int tl=sp_canvas_tile_floor(nl);
2224 int tt=sp_canvas_tile_floor(nt);
2225 int tr=sp_canvas_tile_ceil(nr);
2226 int tb=sp_canvas_tile_ceil(nb);
2227 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2228 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2229 if ( tr > canvas->tRight ) tr=canvas->tRight;
2230 if ( tt < canvas->tTop ) tt=canvas->tTop;
2231 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2233 canvas->need_redraw = TRUE;
2235 for (int i=tl;i<tr;i++) {
2236 for (int j=tt;j<tb;j++) {
2237 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]=1;
2238 }
2239 }
2240 }
2243 /*
2244 Local Variables:
2245 mode:c++
2246 c-file-style:"stroustrup"
2247 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2248 indent-tabs-mode:nil
2249 fill-column:99
2250 End:
2251 */
2252 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :