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 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1096 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1097 }
1099 /**
1100 * The canvas widget's unrealize callback.
1101 */
1102 static void
1103 sp_canvas_unrealize (GtkWidget *widget)
1104 {
1105 SPCanvas *canvas = SP_CANVAS (widget);
1107 shutdown_transients (canvas);
1109 gdk_gc_destroy (canvas->pixmap_gc);
1110 canvas->pixmap_gc = NULL;
1112 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1113 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1114 }
1116 /**
1117 * The canvas widget's size_request callback.
1118 */
1119 static void
1120 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1121 {
1122 static_cast<void>(SP_CANVAS (widget));
1124 req->width = 256;
1125 req->height = 256;
1126 }
1128 /**
1129 * The canvas widget's size_allocate callback.
1130 */
1131 static void
1132 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1133 {
1134 SPCanvas *canvas = SP_CANVAS (widget);
1136 /* Schedule redraw of new region */
1137 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1138 if (allocation->width > widget->allocation.width) {
1139 sp_canvas_request_redraw (canvas,
1140 canvas->x0 + widget->allocation.width,
1141 0,
1142 canvas->x0 + allocation->width,
1143 canvas->y0 + allocation->height);
1144 }
1145 if (allocation->height > widget->allocation.height) {
1146 sp_canvas_request_redraw (canvas,
1147 0,
1148 canvas->y0 + widget->allocation.height,
1149 canvas->x0 + allocation->width,
1150 canvas->y0 + allocation->height);
1151 }
1153 widget->allocation = *allocation;
1155 if (GTK_WIDGET_REALIZED (widget)) {
1156 gdk_window_move_resize (widget->window,
1157 widget->allocation.x, widget->allocation.y,
1158 widget->allocation.width, widget->allocation.height);
1159 }
1160 }
1162 /**
1163 * Helper that emits an event for an item in the canvas, be it the current
1164 * item, grabbed item, or focused item, as appropriate.
1165 */
1166 static int
1167 emit_event (SPCanvas *canvas, GdkEvent *event)
1168 {
1169 guint mask;
1171 if (canvas->grabbed_item) {
1172 switch (event->type) {
1173 case GDK_ENTER_NOTIFY:
1174 mask = GDK_ENTER_NOTIFY_MASK;
1175 break;
1176 case GDK_LEAVE_NOTIFY:
1177 mask = GDK_LEAVE_NOTIFY_MASK;
1178 break;
1179 case GDK_MOTION_NOTIFY:
1180 mask = GDK_POINTER_MOTION_MASK;
1181 break;
1182 case GDK_BUTTON_PRESS:
1183 case GDK_2BUTTON_PRESS:
1184 case GDK_3BUTTON_PRESS:
1185 mask = GDK_BUTTON_PRESS_MASK;
1186 break;
1187 case GDK_BUTTON_RELEASE:
1188 mask = GDK_BUTTON_RELEASE_MASK;
1189 break;
1190 case GDK_KEY_PRESS:
1191 mask = GDK_KEY_PRESS_MASK;
1192 break;
1193 case GDK_KEY_RELEASE:
1194 mask = GDK_KEY_RELEASE_MASK;
1195 break;
1196 case GDK_SCROLL:
1197 mask = GDK_SCROLL;
1198 break;
1199 default:
1200 mask = 0;
1201 break;
1202 }
1204 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1205 }
1207 /* Convert to world coordinates -- we have two cases because of diferent
1208 * offsets of the fields in the event structures.
1209 */
1211 GdkEvent ev = *event;
1213 switch (ev.type) {
1214 case GDK_ENTER_NOTIFY:
1215 case GDK_LEAVE_NOTIFY:
1216 ev.crossing.x += canvas->x0;
1217 ev.crossing.y += canvas->y0;
1218 break;
1219 case GDK_MOTION_NOTIFY:
1220 case GDK_BUTTON_PRESS:
1221 case GDK_2BUTTON_PRESS:
1222 case GDK_3BUTTON_PRESS:
1223 case GDK_BUTTON_RELEASE:
1224 ev.motion.x += canvas->x0;
1225 ev.motion.y += canvas->y0;
1226 break;
1227 default:
1228 break;
1229 }
1231 /* Choose where we send the event */
1233 /* canvas->current_item becomes NULL in some cases under Win32
1234 ** (e.g. if the pointer leaves the window). So this is a hack that
1235 ** Lauris applied to SP to get around the problem.
1236 */
1237 SPCanvasItem* item = NULL;
1238 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1239 item = canvas->grabbed_item;
1240 } else {
1241 item = canvas->current_item;
1242 }
1244 if (canvas->focused_item &&
1245 ((event->type == GDK_KEY_PRESS) ||
1246 (event->type == GDK_KEY_RELEASE) ||
1247 (event->type == GDK_FOCUS_CHANGE))) {
1248 item = canvas->focused_item;
1249 }
1251 /* The event is propagated up the hierarchy (for if someone connected to
1252 * a group instead of a leaf event), and emission is stopped if a
1253 * handler returns TRUE, just like for GtkWidget events.
1254 */
1256 gint finished = FALSE;
1258 while (item && !finished) {
1259 gtk_object_ref (GTK_OBJECT (item));
1260 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1261 SPCanvasItem *parent = item->parent;
1262 gtk_object_unref (GTK_OBJECT (item));
1263 item = parent;
1264 }
1266 return finished;
1267 }
1269 /**
1270 * Helper that re-picks the current item in the canvas, based on the event's
1271 * coordinates and emits enter/leave events for items as appropriate.
1272 */
1273 static int
1274 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1275 {
1276 int button_down = 0;
1277 double x, y;
1279 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1280 return FALSE;
1282 int retval = FALSE;
1284 if (canvas->gen_all_enter_events == false) {
1285 // If a button is down, we'll perform enter and leave events on the
1286 // current item, but not enter on any other item. This is more or
1287 // less like X pointer grabbing for canvas items.
1288 //
1289 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1290 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1292 if (!button_down) canvas->left_grabbed_item = FALSE;
1293 }
1295 /* Save the event in the canvas. This is used to synthesize enter and
1296 * leave events in case the current item changes. It is also used to
1297 * re-pick the current item if the current one gets deleted. Also,
1298 * synthesize an enter event.
1299 */
1300 if (event != &canvas->pick_event) {
1301 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1302 /* these fields have the same offsets in both types of events */
1304 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1305 canvas->pick_event.crossing.window = event->motion.window;
1306 canvas->pick_event.crossing.send_event = event->motion.send_event;
1307 canvas->pick_event.crossing.subwindow = NULL;
1308 canvas->pick_event.crossing.x = event->motion.x;
1309 canvas->pick_event.crossing.y = event->motion.y;
1310 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1311 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1312 canvas->pick_event.crossing.focus = FALSE;
1313 canvas->pick_event.crossing.state = event->motion.state;
1315 /* these fields don't have the same offsets in both types of events */
1317 if (event->type == GDK_MOTION_NOTIFY) {
1318 canvas->pick_event.crossing.x_root = event->motion.x_root;
1319 canvas->pick_event.crossing.y_root = event->motion.y_root;
1320 } else {
1321 canvas->pick_event.crossing.x_root = event->button.x_root;
1322 canvas->pick_event.crossing.y_root = event->button.y_root;
1323 }
1324 } else {
1325 canvas->pick_event = *event;
1326 }
1327 }
1329 /* Don't do anything else if this is a recursive call */
1330 if (canvas->in_repick) return retval;
1332 /* LeaveNotify means that there is no current item, so we don't look for one */
1333 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1334 /* these fields don't have the same offsets in both types of events */
1336 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1337 x = canvas->pick_event.crossing.x;
1338 y = canvas->pick_event.crossing.y;
1339 } else {
1340 x = canvas->pick_event.motion.x;
1341 y = canvas->pick_event.motion.y;
1342 }
1344 /* world coords */
1345 x += canvas->x0;
1346 y += canvas->y0;
1348 /* find the closest item */
1349 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1350 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1351 } else {
1352 canvas->new_current_item = NULL;
1353 }
1354 } else {
1355 canvas->new_current_item = NULL;
1356 }
1358 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1359 return retval; /* current item did not change */
1360 }
1362 /* Synthesize events for old and new current items */
1364 if ((canvas->new_current_item != canvas->current_item)
1365 && (canvas->current_item != NULL)
1366 && !canvas->left_grabbed_item) {
1367 GdkEvent new_event;
1368 SPCanvasItem *item;
1370 item = canvas->current_item;
1372 new_event = canvas->pick_event;
1373 new_event.type = GDK_LEAVE_NOTIFY;
1375 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1376 new_event.crossing.subwindow = NULL;
1377 canvas->in_repick = TRUE;
1378 retval = emit_event (canvas, &new_event);
1379 canvas->in_repick = FALSE;
1380 }
1382 if (canvas->gen_all_enter_events == false) {
1383 // new_current_item may have been set to NULL during the call to
1384 // emit_event() above
1385 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1386 canvas->left_grabbed_item = TRUE;
1387 return retval;
1388 }
1389 }
1391 /* Handle the rest of cases */
1393 canvas->left_grabbed_item = FALSE;
1394 canvas->current_item = canvas->new_current_item;
1396 if (canvas->current_item != NULL) {
1397 GdkEvent new_event;
1399 new_event = canvas->pick_event;
1400 new_event.type = GDK_ENTER_NOTIFY;
1401 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1402 new_event.crossing.subwindow = NULL;
1403 retval = emit_event (canvas, &new_event);
1404 }
1406 return retval;
1407 }
1409 /**
1410 * Button event handler for the canvas.
1411 */
1412 static gint
1413 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1414 {
1415 SPCanvas *canvas = SP_CANVAS (widget);
1417 int retval = FALSE;
1419 /* dispatch normally regardless of the event's window if an item has
1420 has a pointer grab in effect */
1421 if (!canvas->grabbed_item &&
1422 event->window != SP_CANVAS_WINDOW (canvas))
1423 return retval;
1425 int mask;
1426 switch (event->button) {
1427 case 1:
1428 mask = GDK_BUTTON1_MASK;
1429 break;
1430 case 2:
1431 mask = GDK_BUTTON2_MASK;
1432 break;
1433 case 3:
1434 mask = GDK_BUTTON3_MASK;
1435 break;
1436 case 4:
1437 mask = GDK_BUTTON4_MASK;
1438 break;
1439 case 5:
1440 mask = GDK_BUTTON5_MASK;
1441 break;
1442 default:
1443 mask = 0;
1444 }
1446 switch (event->type) {
1447 case GDK_BUTTON_PRESS:
1448 case GDK_2BUTTON_PRESS:
1449 case GDK_3BUTTON_PRESS:
1450 /* Pick the current item as if the button were not pressed, and
1451 * then process the event.
1452 */
1453 canvas->state = event->state;
1454 pick_current_item (canvas, (GdkEvent *) event);
1455 canvas->state ^= mask;
1456 retval = emit_event (canvas, (GdkEvent *) event);
1457 break;
1459 case GDK_BUTTON_RELEASE:
1460 /* Process the event as if the button were pressed, then repick
1461 * after the button has been released
1462 */
1463 canvas->state = event->state;
1464 retval = emit_event (canvas, (GdkEvent *) event);
1465 event->state ^= mask;
1466 canvas->state = event->state;
1467 pick_current_item (canvas, (GdkEvent *) event);
1468 event->state ^= mask;
1469 break;
1471 default:
1472 g_assert_not_reached ();
1473 }
1475 return retval;
1476 }
1478 /**
1479 * Scroll event handler for the canvas.
1480 *
1481 * \todo FIXME: generate motion events to re-select items.
1482 */
1483 static gint
1484 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1485 {
1486 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1487 }
1489 /**
1490 * Motion event handler for the canvas.
1491 */
1492 static int
1493 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1494 {
1495 SPCanvas *canvas = SP_CANVAS (widget);
1497 if (event->window != SP_CANVAS_WINDOW (canvas))
1498 return FALSE;
1500 if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1501 gint x, y;
1502 gdk_window_get_pointer (widget->window, &x, &y, NULL);
1503 event->x = x;
1504 event->y = y;
1505 }
1507 canvas->state = event->state;
1508 pick_current_item (canvas, (GdkEvent *) event);
1510 return emit_event (canvas, (GdkEvent *) event);
1511 }
1513 static void
1514 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)
1515 {
1516 GtkWidget *widget = GTK_WIDGET (canvas);
1518 SPCanvasBuf buf;
1519 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1520 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1521 } else {
1522 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1523 }
1525 buf.buf_rowstride = sw * 3;
1526 buf.rect.x0 = x0;
1527 buf.rect.y0 = y0;
1528 buf.rect.x1 = x1;
1529 buf.rect.y1 = y1;
1530 buf.visible_rect.x0 = draw_x1;
1531 buf.visible_rect.y0 = draw_y1;
1532 buf.visible_rect.x1 = draw_x2;
1533 buf.visible_rect.y1 = draw_y2;
1534 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1535 buf.bg_color = (((color->red & 0xff00) << 8)
1536 | (color->green & 0xff00)
1537 | (color->blue >> 8));
1538 buf.is_empty = true;
1540 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1541 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1542 }
1544 if (buf.is_empty) {
1545 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1546 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1547 canvas->pixmap_gc,
1548 TRUE,
1549 x0 - canvas->x0, y0 - canvas->y0,
1550 x1 - x0, y1 - y0);
1551 } else {
1552 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1553 canvas->pixmap_gc,
1554 x0 - canvas->x0, y0 - canvas->y0,
1555 x1 - x0, y1 - y0,
1556 GDK_RGB_DITHER_MAX,
1557 buf.buf,
1558 sw * 3,
1559 x0 - canvas->x0, y0 - canvas->y0);
1560 }
1562 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1563 nr_pixelstore_256K_free (buf.buf);
1564 } else {
1565 nr_pixelstore_1M_free (buf.buf);
1566 }
1567 }
1569 /* Paint the given rect, while updating canvas->redraw_aborted and running iterations after each
1570 * buffer; make sure canvas->redraw_aborted never goes past aborted_limit (used for 2-rect
1571 * optimized repaint)
1572 */
1573 static int
1574 sp_canvas_paint_rect_internal (SPCanvas *canvas, NRRectL *rect, NR::ICoord *x_aborted_limit, NR::ICoord *y_aborted_limit)
1575 {
1576 int draw_x1 = rect->x0;
1577 int draw_x2 = rect->x1;
1578 int draw_y1 = rect->y0;
1579 int draw_y2 = rect->y1;
1581 // Here we'll store the time it took to draw the slowest buffer of this paint.
1582 glong slowest_buffer = 0;
1584 // Find the optimal buffer dimensions
1585 int bw = draw_x2 - draw_x1;
1586 int bh = draw_y2 - draw_y1;
1587 if ((bw < 1) || (bh < 1))
1588 return 0;
1589 int sw, sh;
1590 if (canvas->rendermode != RENDERMODE_OUTLINE) { // use 256K as a compromise to not slow down gradients
1591 /* 256K is the cached buffer and we need 3 channels */
1592 if (bw * bh < 87381) { // 256K/3
1593 // We can go with single buffer
1594 sw = bw;
1595 sh = bh;
1596 } else if (bw <= (16 * 341)) {
1597 // Go with row buffer
1598 sw = bw;
1599 sh = 87381 / bw;
1600 } else if (bh <= (16 * 256)) {
1601 // Go with column buffer
1602 sw = 87381 / bh;
1603 sh = bh;
1604 } else {
1605 sw = 341;
1606 sh = 256;
1607 }
1608 } else { // paths only, so 1M works faster
1609 /* 1M is the cached buffer and we need 3 channels */
1610 if (bw * bh < 349525) { // 1M/3
1611 // We can go with single buffer
1612 sw = bw;
1613 sh = bh;
1614 } else if (bw <= (16 * 682)) {
1615 // Go with row buffer
1616 sw = bw;
1617 sh = 349525 / bw;
1618 } else if (bh <= (16 * 512)) {
1619 // Go with column buffer
1620 sw = 349525 / bh;
1621 sh = bh;
1622 } else {
1623 sw = 682;
1624 sh = 512;
1625 }
1626 }
1628 // Will this paint require more than one buffer?
1629 bool multiple_buffers = (((draw_y2 - draw_y1) > sh) || ((draw_x2 - draw_x1) > sw)); // or two_rects
1631 // remember the counter during this paint
1632 long this_count = canvas->redraw_count;
1634 // Time values to measure each buffer's paint time
1635 GTimeVal tstart, tfinish;
1637 // This is the main loop which corresponds to the visible left-to-right, top-to-bottom drawing
1638 // of screen blocks (buffers).
1639 for (int y0 = draw_y1; y0 < draw_y2; y0 += sh) {
1640 int y1 = MIN (y0 + sh, draw_y2);
1641 for (int x0 = draw_x1; x0 < draw_x2; x0 += sw) {
1642 int x1 = MIN (x0 + sw, draw_x2);
1644 // OPTIMIZATION IDEA: if drawing is really slow (as measured by canvas->slowest
1645 // buffer), process some events even BEFORE we do any buffers?
1647 // Paint one buffer; measure how long it takes.
1648 g_get_current_time (&tstart);
1649 sp_canvas_paint_single_buffer (canvas, x0, y0, x1, y1, draw_x1, draw_y1, draw_x2, draw_y2, sw);
1650 g_get_current_time (&tfinish);
1652 // Remember the slowest_buffer of this paint.
1653 glong this_buffer = (tfinish.tv_sec - tstart.tv_sec) * 1000000 + (tfinish.tv_usec - tstart.tv_usec);
1654 if (this_buffer > slowest_buffer)
1655 slowest_buffer = this_buffer;
1657 // After each successful buffer, reduce the rect remaining to redraw by what is already redrawn
1658 if (x1 >= draw_x2 && canvas->redraw_aborted.y0 < y1)
1659 canvas->redraw_aborted.y0 = y1;
1660 if (y_aborted_limit != NULL && canvas->redraw_aborted.y0 > *y_aborted_limit)
1661 canvas->redraw_aborted.y0 = *y_aborted_limit;
1663 if (y1 >= draw_y2 && canvas->redraw_aborted.x0 < x1)
1664 canvas->redraw_aborted.x0 = x1;
1665 if (x_aborted_limit != NULL && canvas->redraw_aborted.x0 > *x_aborted_limit)
1666 canvas->redraw_aborted.x0 = *x_aborted_limit;
1668 // INTERRUPTIBLE DISPLAY:
1669 // Process events that may have arrived while we were busy drawing;
1670 // only if we're drawing multiple buffers, and only if this one was not very fast,
1671 // and only if we're allowed to interrupt this redraw
1672 bool ok_to_interrupt = (multiple_buffers && this_buffer > 25000);
1673 if (ok_to_interrupt && (canvas->forced_redraw_limit != -1)) {
1674 ok_to_interrupt = (canvas->forced_redraw_count < canvas->forced_redraw_limit);
1675 }
1677 if (ok_to_interrupt) {
1678 // Run at most max_iterations of the main loop; we cannot process ALL events
1679 // here because some things (e.g. rubberband) flood with dirtying events but will
1680 // not redraw themselves
1681 int max_iterations = 10;
1682 int iterations = 0;
1683 while (Gtk::Main::events_pending() && iterations++ < max_iterations) {
1684 Gtk::Main::iteration(false);
1685 // If one of the iterations has redrawn by itself, abort
1686 if (this_count != canvas->redraw_count) {
1687 canvas->slowest_buffer = slowest_buffer;
1688 if (canvas->forced_redraw_limit != -1) {
1689 canvas->forced_redraw_count++;
1690 }
1691 return 1; // interrupted
1692 }
1693 }
1695 // If not aborted so far, check if the events set redraw or update flags;
1696 // if so, force update and abort
1697 if (canvas->need_redraw || canvas->need_update) {
1698 canvas->slowest_buffer = slowest_buffer;
1699 if (canvas->forced_redraw_limit != -1) {
1700 canvas->forced_redraw_count++;
1701 }
1702 do_update (canvas);
1703 return 1; // interrupted
1704 }
1705 }
1706 }
1707 }
1709 // Remember the slowest buffer of this paint in canvas
1710 canvas->slowest_buffer = slowest_buffer;
1712 return 0; // finished
1713 }
1716 /**
1717 * Helper that draws a specific rectangular part of the canvas.
1718 */
1719 static void
1720 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1721 {
1722 g_return_if_fail (!canvas->need_update);
1724 // Monotonously increment the canvas-global counter on each paint. This will let us find out
1725 // when a new paint happened in event processing during this paint, so we can abort it.
1726 canvas->redraw_count++;
1728 NRRectL rect;
1729 rect.x0 = xx0;
1730 rect.x1 = xx1;
1731 rect.y0 = yy0;
1732 rect.y1 = yy1;
1734 // Clip rect-to-draw by the current visible area
1735 rect.x0 = MAX (rect.x0, canvas->x0);
1736 rect.y0 = MAX (rect.y0, canvas->y0);
1737 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1738 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1740 // Clip rect-aborted-last-time by the current visible area
1741 canvas->redraw_aborted.x0 = MAX (canvas->redraw_aborted.x0, canvas->x0);
1742 canvas->redraw_aborted.y0 = MAX (canvas->redraw_aborted.y0, canvas->y0);
1743 canvas->redraw_aborted.x1 = MIN (canvas->redraw_aborted.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1744 canvas->redraw_aborted.y1 = MIN (canvas->redraw_aborted.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1746 if (canvas->redraw_aborted.x0 < canvas->redraw_aborted.x1 && canvas->redraw_aborted.y0 < canvas->redraw_aborted.y1) {
1747 // There was an aborted redraw last time, now we need to redraw BOTH it and the new rect.
1749 // save the old aborted rect in case we decide to paint it separately (see below)
1750 NRRectL aborted = canvas->redraw_aborted;
1752 // calculate the rectangle union of the both rects (the smallest rectangle which covers both)
1753 NRRectL nion;
1754 nr_rect_l_union (&nion, &rect, &aborted);
1756 // subtract one of the rects-to-draw from the other (the smallest rectangle which covers
1757 // all of the first not covered by the second)
1758 NRRectL rect_minus_aborted;
1759 nr_rect_l_subtract (&rect_minus_aborted, &rect, &aborted);
1761 // Initially, the rect to redraw later (in case we're aborted) is the same as the union of both rects
1762 canvas->redraw_aborted = nion;
1764 // calculate areas of the three rects
1765 if ((nr_rect_l_area(&rect_minus_aborted) + nr_rect_l_area(&aborted)) * 1.2 < nr_rect_l_area(&nion)) {
1766 // If the summary area of the two rects is significantly (at least by 20%) less than
1767 // the area of their rectangular union, it makes sense to paint the two rects
1768 // separately instead of painting their union. This gives a significant speedup when,
1769 // for example, your current canvas is almost painted, with only a strip at bottom
1770 // left, and at that moment you abort it by scrolling down which reveals a new strip at
1771 // the top. Straightforward painting of the union of the aborted rect and the new rect
1772 // will have to repaint the entire canvas! By contrast, the optimized approach below
1773 // paints the two narrow strips in order which is much faster.
1775 // find out which rect to draw first - compare them first by y then by x of the top left corners
1776 NRRectL *first;
1777 NRRectL *second;
1778 if (rect.y0 == aborted.y0) {
1779 if (rect.x0 < aborted.x0) {
1780 first = ▭
1781 second = &aborted;
1782 } else {
1783 second = ▭
1784 first = &aborted;
1785 }
1786 } else if (rect.y0 < aborted.y0) {
1787 first = ▭
1788 second = &aborted;
1789 } else {
1790 second = ▭
1791 first = &aborted;
1792 }
1794 NRRectL second_minus_first;
1795 nr_rect_l_subtract (&second_minus_first, second, first);
1797 // paint the first rect;
1798 if (sp_canvas_paint_rect_internal (canvas, first, &(second_minus_first.x0), &(second_minus_first.y0))) {
1799 // aborted!
1800 return;
1801 }
1803 // if not aborted, assign (second rect minus first) as the new redraw_aborted and paint the same
1804 canvas->redraw_aborted = second_minus_first;
1805 if (sp_canvas_paint_rect_internal (canvas, &second_minus_first, NULL, NULL)) {
1806 return; // aborted
1807 }
1809 } else {
1810 // no need for separate drawing, just draw the union as one rect
1811 if (sp_canvas_paint_rect_internal (canvas, &nion, NULL, NULL)) {
1812 return; // aborted
1813 }
1814 }
1815 } else {
1816 // Nothing was aborted last time, just draw the rect we're given
1818 // Initially, the rect to redraw later (in case we're aborted) is the same as the one we're going to draw now.
1819 canvas->redraw_aborted = rect;
1821 if (sp_canvas_paint_rect_internal (canvas, &rect, NULL, NULL)) {
1822 return; // aborted
1823 }
1824 }
1826 // we've had a full unaborted redraw, reset the full redraw counter
1827 if (canvas->forced_redraw_limit != -1) {
1828 canvas->forced_redraw_count = 0;
1829 }
1830 }
1832 /**
1833 * Force a full redraw after a specified number of interrupted redraws
1834 */
1835 void
1836 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1837 g_return_if_fail(canvas != NULL);
1839 canvas->forced_redraw_limit = count;
1840 canvas->forced_redraw_count = 0;
1841 }
1843 /**
1844 * End forced full redraw requests
1845 */
1846 void
1847 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1848 g_return_if_fail(canvas != NULL);
1850 canvas->forced_redraw_limit = -1;
1851 }
1853 /**
1854 * The canvas widget's expose callback.
1855 */
1856 static gint
1857 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1858 {
1859 SPCanvas *canvas = SP_CANVAS (widget);
1861 if (!GTK_WIDGET_DRAWABLE (widget) ||
1862 (event->window != SP_CANVAS_WINDOW (canvas)))
1863 return FALSE;
1865 int n_rects;
1866 GdkRectangle *rects;
1867 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1869 for (int i = 0; i < n_rects; i++) {
1870 NRRectL rect;
1872 rect.x0 = rects[i].x + canvas->x0;
1873 rect.y0 = rects[i].y + canvas->y0;
1874 rect.x1 = rect.x0 + rects[i].width;
1875 rect.y1 = rect.y0 + rects[i].height;
1877 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1878 }
1880 if (n_rects > 0)
1881 g_free (rects);
1883 return FALSE;
1884 }
1886 /**
1887 * The canvas widget's keypress callback.
1888 */
1889 static gint
1890 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1891 {
1892 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1893 }
1895 /**
1896 * Crossing event handler for the canvas.
1897 */
1898 static gint
1899 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1900 {
1901 SPCanvas *canvas = SP_CANVAS (widget);
1903 if (event->window != SP_CANVAS_WINDOW (canvas))
1904 return FALSE;
1906 canvas->state = event->state;
1907 return pick_current_item (canvas, (GdkEvent *) event);
1908 }
1910 /**
1911 * Focus in handler for the canvas.
1912 */
1913 static gint
1914 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1915 {
1916 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1918 SPCanvas *canvas = SP_CANVAS (widget);
1920 if (canvas->focused_item) {
1921 return emit_event (canvas, (GdkEvent *) event);
1922 } else {
1923 return FALSE;
1924 }
1925 }
1927 /**
1928 * Focus out handler for the canvas.
1929 */
1930 static gint
1931 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1932 {
1933 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1935 SPCanvas *canvas = SP_CANVAS (widget);
1937 if (canvas->focused_item)
1938 return emit_event (canvas, (GdkEvent *) event);
1939 else
1940 return FALSE;
1941 }
1943 /**
1944 * Helper that repaints the areas in the canvas that need it.
1945 */
1946 static int
1947 paint (SPCanvas *canvas)
1948 {
1949 if (canvas->need_update) {
1950 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1951 canvas->need_update = FALSE;
1952 }
1954 if (!canvas->need_redraw)
1955 return TRUE;
1957 GtkWidget const *widget = GTK_WIDGET(canvas);
1958 int const canvas_x1 = canvas->x0 + widget->allocation.width;
1959 int const canvas_y1 = canvas->y0 + widget->allocation.height;
1961 NRRectL topaint;
1962 topaint.x0 = topaint.y0 = topaint.x1 = topaint.y1 = 0;
1964 for (int j=canvas->tTop&(~3);j<canvas->tBottom;j+=4) {
1965 for (int i=canvas->tLeft&(~3);i<canvas->tRight;i+=4) {
1966 int mode=0;
1968 int pl=i+1,pr=i,pt=j+4,pb=j;
1969 for (int l=MAX(j,canvas->tTop);l<MIN(j+4,canvas->tBottom);l++) {
1970 for (int k=MAX(i,canvas->tLeft);k<MIN(i+4,canvas->tRight);k++) {
1971 if ( canvas->tiles[(k-canvas->tLeft)+(l-canvas->tTop)*canvas->tileH] ) {
1972 mode|=1<<((k-i)+(l-j)*4);
1973 if ( k < pl ) pl=k;
1974 if ( k+1 > pr ) pr=k+1;
1975 if ( l < pt ) pt=l;
1976 if ( l+1 > pb ) pb=l+1;
1977 }
1978 canvas->tiles[(k-canvas->tLeft)+(l-canvas->tTop)*canvas->tileH]=0;
1979 }
1980 }
1982 if ( mode ) {
1983 NRRectL tile;
1984 tile.x0 = MAX (pl*32, canvas->x0);
1985 tile.y0 = MAX (pt*32, canvas->y0);
1986 tile.x1 = MIN (pr*32, canvas_x1);
1987 tile.y1 = MIN (pb*32, canvas_y1);
1988 if ((tile.x0 < tile.x1) && (tile.y0 < tile.y1)) {
1989 nr_rect_l_union (&topaint, &topaint, &tile);
1990 }
1992 }
1993 }
1994 }
1996 canvas->need_redraw = FALSE;
1997 sp_canvas_paint_rect (canvas, topaint.x0, topaint.y0, topaint.x1, topaint.y1);
1999 return TRUE;
2000 }
2002 /**
2003 * Helper that invokes update, paint, and repick on canvas.
2004 */
2005 static int
2006 do_update (SPCanvas *canvas)
2007 {
2008 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2009 return TRUE;
2011 /* Cause the update if necessary */
2012 if (canvas->need_update) {
2013 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2014 canvas->need_update = FALSE;
2015 }
2017 /* Paint if able to */
2018 if (GTK_WIDGET_DRAWABLE (canvas)) {
2019 return paint (canvas);
2020 }
2022 /* Pick new current item */
2023 while (canvas->need_repick) {
2024 canvas->need_repick = FALSE;
2025 pick_current_item (canvas, &canvas->pick_event);
2026 }
2028 return TRUE;
2029 }
2031 /**
2032 * Idle handler for the canvas that deals with pending updates and redraws.
2033 */
2034 static gint
2035 idle_handler (gpointer data)
2036 {
2037 GDK_THREADS_ENTER ();
2039 SPCanvas *canvas = SP_CANVAS (data);
2041 const int ret = do_update (canvas);
2043 if (ret) {
2044 /* Reset idle id */
2045 canvas->idle_id = 0;
2046 }
2048 GDK_THREADS_LEAVE ();
2050 return !ret;
2051 }
2053 /**
2054 * Convenience function to add an idle handler to a canvas.
2055 */
2056 static void
2057 add_idle (SPCanvas *canvas)
2058 {
2059 if (canvas->idle_id != 0)
2060 return;
2062 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2063 }
2065 /**
2066 * Returns the root group of the specified canvas.
2067 */
2068 SPCanvasGroup *
2069 sp_canvas_root (SPCanvas *canvas)
2070 {
2071 g_return_val_if_fail (canvas != NULL, NULL);
2072 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2074 return SP_CANVAS_GROUP (canvas->root);
2075 }
2077 /**
2078 * Scrolls canvas to specific position.
2079 */
2080 void
2081 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear)
2082 {
2083 g_return_if_fail (canvas != NULL);
2084 g_return_if_fail (SP_IS_CANVAS (canvas));
2086 int ix = (int) (cx + 0.5);
2087 int iy = (int) (cy + 0.5);
2088 int dx = ix - canvas->x0;
2089 int dy = iy - canvas->y0;
2091 canvas->dx0 = cx;
2092 canvas->dy0 = cy;
2093 canvas->x0 = ix;
2094 canvas->y0 = iy;
2096 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+canvas->widget.allocation.width,canvas->y0+canvas->widget.allocation.height);
2098 if (!clear) {
2099 // scrolling without zoom; redraw only the newly exposed areas
2100 if ((dx != 0) || (dy != 0)) {
2101 int width, height;
2102 width = canvas->widget.allocation.width;
2103 height = canvas->widget.allocation.height;
2104 if (GTK_WIDGET_REALIZED (canvas)) {
2105 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2106 gdk_window_process_updates (SP_CANVAS_WINDOW (canvas), TRUE);
2107 }
2108 if (dx < 0) {
2109 sp_canvas_request_redraw (canvas, ix + 0, iy + 0, ix - dx, iy + height);
2110 } else if (dx > 0) {
2111 sp_canvas_request_redraw (canvas, ix + width - dx, iy + 0, ix + width, iy + height);
2112 }
2113 if (dy < 0) {
2114 sp_canvas_request_redraw (canvas, ix + 0, iy + 0, ix + width, iy - dy);
2115 } else if (dy > 0) {
2116 sp_canvas_request_redraw (canvas, ix + 0, iy + height - dy, ix + width, iy + height);
2117 }
2118 }
2119 } else {
2120 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2121 }
2122 }
2124 /**
2125 * Updates canvas if necessary.
2126 */
2127 void
2128 sp_canvas_update_now (SPCanvas *canvas)
2129 {
2130 g_return_if_fail (canvas != NULL);
2131 g_return_if_fail (SP_IS_CANVAS (canvas));
2133 if (!(canvas->need_update ||
2134 canvas->need_redraw))
2135 return;
2137 remove_idle (canvas);
2138 do_update (canvas);
2139 }
2141 /**
2142 * Update callback for canvas widget.
2143 */
2144 static void
2145 sp_canvas_request_update (SPCanvas *canvas)
2146 {
2147 canvas->need_update = TRUE;
2148 add_idle (canvas);
2149 }
2151 /**
2152 * Forces redraw of rectangular canvas area.
2153 */
2154 void
2155 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2156 {
2157 NRRectL bbox;
2158 NRRectL visible;
2159 NRRectL clip;
2161 g_return_if_fail (canvas != NULL);
2162 g_return_if_fail (SP_IS_CANVAS (canvas));
2164 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2165 if ((x0 >= x1) || (y0 >= y1)) return;
2167 bbox.x0 = x0;
2168 bbox.y0 = y0;
2169 bbox.x1 = x1;
2170 bbox.y1 = y1;
2172 visible.x0 = canvas->x0;
2173 visible.y0 = canvas->y0;
2174 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2175 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2177 nr_rect_l_intersect (&clip, &bbox, &visible);
2179 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2180 add_idle (canvas);
2181 }
2183 /**
2184 * Sets world coordinates from win and canvas.
2185 */
2186 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2187 {
2188 g_return_if_fail (canvas != NULL);
2189 g_return_if_fail (SP_IS_CANVAS (canvas));
2191 if (worldx) *worldx = canvas->x0 + winx;
2192 if (worldy) *worldy = canvas->y0 + winy;
2193 }
2195 /**
2196 * Sets win coordinates from world and canvas.
2197 */
2198 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2199 {
2200 g_return_if_fail (canvas != NULL);
2201 g_return_if_fail (SP_IS_CANVAS (canvas));
2203 if (winx) *winx = worldx - canvas->x0;
2204 if (winy) *winy = worldy - canvas->y0;
2205 }
2207 /**
2208 * Converts point from win to world coordinates.
2209 */
2210 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2211 {
2212 g_assert (canvas != NULL);
2213 g_assert (SP_IS_CANVAS (canvas));
2215 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2216 }
2218 /**
2219 * Converts point from world to win coordinates.
2220 */
2221 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2222 {
2223 g_assert (canvas != NULL);
2224 g_assert (SP_IS_CANVAS (canvas));
2226 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2227 }
2229 /**
2230 * Returns true if point given in world coordinates is inside window.
2231 */
2232 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2233 {
2234 g_assert( canvas != NULL );
2235 g_assert(SP_IS_CANVAS(canvas));
2237 using NR::X;
2238 using NR::Y;
2239 GtkWidget const &w = *GTK_WIDGET(canvas);
2240 return ( ( canvas->x0 <= world[X] ) &&
2241 ( canvas->y0 <= world[Y] ) &&
2242 ( world[X] < canvas->x0 + w.allocation.width ) &&
2243 ( world[Y] < canvas->y0 + w.allocation.height ) );
2244 }
2246 /**
2247 * Return canvas window coordinates as NRRect.
2248 */
2249 NR::Rect SPCanvas::getViewbox() const
2250 {
2251 GtkWidget const *w = GTK_WIDGET(this);
2253 return NR::Rect(NR::Point(dx0, dy0),
2254 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2255 }
2257 inline int sp_canvas_tile_floor(int x)
2258 {
2259 return (x&(~31))/32;
2260 }
2262 inline int sp_canvas_tile_ceil(int x)
2263 {
2264 return ((x+31)&(~31))/32;
2265 }
2267 /**
2268 * Helper that changes tile size for canvas redraw.
2269 */
2270 void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2271 {
2272 if ( nl >= nr || nt >= nb ) {
2273 if ( canvas->tiles ) g_free(canvas->tiles);
2274 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2275 canvas->tileH=canvas->tileV=0;
2276 canvas->tiles=NULL;
2277 return;
2278 }
2279 int tl=sp_canvas_tile_floor(nl);
2280 int tt=sp_canvas_tile_floor(nt);
2281 int tr=sp_canvas_tile_ceil(nr);
2282 int tb=sp_canvas_tile_ceil(nb);
2284 int nh=tr-tl,nv=tb-tt;
2285 uint8_t* ntiles=(uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2286 for (int i=tl;i<tr;i++) {
2287 for (int j=tt;j<tb;j++) {
2288 int ind=(i-tl)+(j-tt)*nh;
2289 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2290 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH];
2291 } else {
2292 ntiles[ind]=0;
2293 }
2294 }
2295 }
2296 if ( canvas->tiles ) g_free(canvas->tiles);
2297 canvas->tiles=ntiles;
2298 canvas->tLeft=tl;
2299 canvas->tTop=tt;
2300 canvas->tRight=tr;
2301 canvas->tBottom=tb;
2302 canvas->tileH=nh;
2303 canvas->tileV=nv;
2304 }
2306 /**
2307 * Helper that marks specific canvas rectangle for redraw.
2308 */
2309 void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2310 {
2311 if ( nl >= nr || nt >= nb ) {
2312 return;
2313 }
2314 int tl=sp_canvas_tile_floor(nl);
2315 int tt=sp_canvas_tile_floor(nt);
2316 int tr=sp_canvas_tile_ceil(nr);
2317 int tb=sp_canvas_tile_ceil(nb);
2318 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2319 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2320 if ( tr > canvas->tRight ) tr=canvas->tRight;
2321 if ( tt < canvas->tTop ) tt=canvas->tTop;
2322 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2324 canvas->need_redraw = TRUE;
2326 for (int i=tl;i<tr;i++) {
2327 for (int j=tt;j<tb;j++) {
2328 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]=1;
2329 }
2330 }
2331 }
2334 /*
2335 Local Variables:
2336 mode:c++
2337 c-file-style:"stroustrup"
2338 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2339 indent-tabs-mode:nil
2340 fill-column:99
2341 End:
2342 */
2343 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :