71e4d955df8f5fdd3175a65ee1d8731692251b53
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 int retval = FALSE;
1281 if (canvas->gen_all_enter_events == false) {
1282 // If a button is down, we'll perform enter and leave events on the
1283 // current item, but not enter on any other item. This is more or
1284 // less like X pointer grabbing for canvas items.
1285 //
1286 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1287 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1289 if (!button_down) canvas->left_grabbed_item = FALSE;
1290 }
1292 /* Save the event in the canvas. This is used to synthesize enter and
1293 * leave events in case the current item changes. It is also used to
1294 * re-pick the current item if the current one gets deleted. Also,
1295 * synthesize an enter event.
1296 */
1297 if (event != &canvas->pick_event) {
1298 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1299 /* these fields have the same offsets in both types of events */
1301 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1302 canvas->pick_event.crossing.window = event->motion.window;
1303 canvas->pick_event.crossing.send_event = event->motion.send_event;
1304 canvas->pick_event.crossing.subwindow = NULL;
1305 canvas->pick_event.crossing.x = event->motion.x;
1306 canvas->pick_event.crossing.y = event->motion.y;
1307 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1308 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1309 canvas->pick_event.crossing.focus = FALSE;
1310 canvas->pick_event.crossing.state = event->motion.state;
1312 /* these fields don't have the same offsets in both types of events */
1314 if (event->type == GDK_MOTION_NOTIFY) {
1315 canvas->pick_event.crossing.x_root = event->motion.x_root;
1316 canvas->pick_event.crossing.y_root = event->motion.y_root;
1317 } else {
1318 canvas->pick_event.crossing.x_root = event->button.x_root;
1319 canvas->pick_event.crossing.y_root = event->button.y_root;
1320 }
1321 } else {
1322 canvas->pick_event = *event;
1323 }
1324 }
1326 /* Don't do anything else if this is a recursive call */
1327 if (canvas->in_repick) return retval;
1329 /* LeaveNotify means that there is no current item, so we don't look for one */
1330 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1331 /* these fields don't have the same offsets in both types of events */
1333 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1334 x = canvas->pick_event.crossing.x;
1335 y = canvas->pick_event.crossing.y;
1336 } else {
1337 x = canvas->pick_event.motion.x;
1338 y = canvas->pick_event.motion.y;
1339 }
1341 /* world coords */
1342 x += canvas->x0;
1343 y += canvas->y0;
1345 /* find the closest item */
1346 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1347 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1348 } else {
1349 canvas->new_current_item = NULL;
1350 }
1351 } else {
1352 canvas->new_current_item = NULL;
1353 }
1355 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1356 return retval; /* current item did not change */
1357 }
1359 /* Synthesize events for old and new current items */
1361 if ((canvas->new_current_item != canvas->current_item)
1362 && (canvas->current_item != NULL)
1363 && !canvas->left_grabbed_item) {
1364 GdkEvent new_event;
1365 SPCanvasItem *item;
1367 item = canvas->current_item;
1369 new_event = canvas->pick_event;
1370 new_event.type = GDK_LEAVE_NOTIFY;
1372 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1373 new_event.crossing.subwindow = NULL;
1374 canvas->in_repick = TRUE;
1375 retval = emit_event (canvas, &new_event);
1376 canvas->in_repick = FALSE;
1377 }
1379 if (canvas->gen_all_enter_events == false) {
1380 // new_current_item may have been set to NULL during the call to
1381 // emit_event() above
1382 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1383 canvas->left_grabbed_item = TRUE;
1384 return retval;
1385 }
1386 }
1388 /* Handle the rest of cases */
1390 canvas->left_grabbed_item = FALSE;
1391 canvas->current_item = canvas->new_current_item;
1393 if (canvas->current_item != NULL) {
1394 GdkEvent new_event;
1396 new_event = canvas->pick_event;
1397 new_event.type = GDK_ENTER_NOTIFY;
1398 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1399 new_event.crossing.subwindow = NULL;
1400 retval = emit_event (canvas, &new_event);
1401 }
1403 return retval;
1404 }
1406 /**
1407 * Button event handler for the canvas.
1408 */
1409 static gint
1410 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1411 {
1412 SPCanvas *canvas = SP_CANVAS (widget);
1414 int retval = FALSE;
1416 /* dispatch normally regardless of the event's window if an item has
1417 has a pointer grab in effect */
1418 if (!canvas->grabbed_item &&
1419 event->window != SP_CANVAS_WINDOW (canvas))
1420 return retval;
1422 int mask;
1423 switch (event->button) {
1424 case 1:
1425 mask = GDK_BUTTON1_MASK;
1426 break;
1427 case 2:
1428 mask = GDK_BUTTON2_MASK;
1429 break;
1430 case 3:
1431 mask = GDK_BUTTON3_MASK;
1432 break;
1433 case 4:
1434 mask = GDK_BUTTON4_MASK;
1435 break;
1436 case 5:
1437 mask = GDK_BUTTON5_MASK;
1438 break;
1439 default:
1440 mask = 0;
1441 }
1443 switch (event->type) {
1444 case GDK_BUTTON_PRESS:
1445 case GDK_2BUTTON_PRESS:
1446 case GDK_3BUTTON_PRESS:
1447 /* Pick the current item as if the button were not pressed, and
1448 * then process the event.
1449 */
1450 canvas->state = event->state;
1451 pick_current_item (canvas, (GdkEvent *) event);
1452 canvas->state ^= mask;
1453 retval = emit_event (canvas, (GdkEvent *) event);
1454 break;
1456 case GDK_BUTTON_RELEASE:
1457 /* Process the event as if the button were pressed, then repick
1458 * after the button has been released
1459 */
1460 canvas->state = event->state;
1461 retval = emit_event (canvas, (GdkEvent *) event);
1462 event->state ^= mask;
1463 canvas->state = event->state;
1464 pick_current_item (canvas, (GdkEvent *) event);
1465 event->state ^= mask;
1466 break;
1468 default:
1469 g_assert_not_reached ();
1470 }
1472 return retval;
1473 }
1475 /**
1476 * Scroll event handler for the canvas.
1477 *
1478 * \todo FIXME: generate motion events to re-select items.
1479 */
1480 static gint
1481 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1482 {
1483 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1484 }
1486 /**
1487 * Motion event handler for the canvas.
1488 */
1489 static int
1490 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1491 {
1492 SPCanvas *canvas = SP_CANVAS (widget);
1494 if (event->window != SP_CANVAS_WINDOW (canvas))
1495 return FALSE;
1497 if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1498 gint x, y;
1499 gdk_window_get_pointer (widget->window, &x, &y, NULL);
1500 event->x = x;
1501 event->y = y;
1502 }
1504 canvas->state = event->state;
1505 pick_current_item (canvas, (GdkEvent *) event);
1507 return emit_event (canvas, (GdkEvent *) event);
1508 }
1510 static void
1511 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)
1512 {
1513 GtkWidget *widget = GTK_WIDGET (canvas);
1515 SPCanvasBuf buf;
1516 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1517 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1518 } else {
1519 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1520 }
1522 buf.buf_rowstride = sw * 3;
1523 buf.rect.x0 = x0;
1524 buf.rect.y0 = y0;
1525 buf.rect.x1 = x1;
1526 buf.rect.y1 = y1;
1527 buf.visible_rect.x0 = draw_x1;
1528 buf.visible_rect.y0 = draw_y1;
1529 buf.visible_rect.x1 = draw_x2;
1530 buf.visible_rect.y1 = draw_y2;
1531 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1532 buf.bg_color = (((color->red & 0xff00) << 8)
1533 | (color->green & 0xff00)
1534 | (color->blue >> 8));
1535 buf.is_empty = true;
1537 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1538 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1539 }
1541 if (buf.is_empty) {
1542 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1543 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1544 canvas->pixmap_gc,
1545 TRUE,
1546 x0 - canvas->x0, y0 - canvas->y0,
1547 x1 - x0, y1 - y0);
1548 } else {
1549 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1550 canvas->pixmap_gc,
1551 x0 - canvas->x0, y0 - canvas->y0,
1552 x1 - x0, y1 - y0,
1553 GDK_RGB_DITHER_MAX,
1554 buf.buf,
1555 sw * 3,
1556 x0 - canvas->x0, y0 - canvas->y0);
1557 }
1559 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1560 nr_pixelstore_256K_free (buf.buf);
1561 } else {
1562 nr_pixelstore_1M_free (buf.buf);
1563 }
1564 }
1566 /* Paint the given rect, while updating canvas->redraw_aborted and running iterations after each
1567 * buffer; make sure canvas->redraw_aborted never goes past aborted_limit (used for 2-rect
1568 * optimized repaint)
1569 */
1570 static int
1571 sp_canvas_paint_rect_internal (SPCanvas *canvas, NRRectL *rect, NR::ICoord *x_aborted_limit, NR::ICoord *y_aborted_limit)
1572 {
1573 int draw_x1 = rect->x0;
1574 int draw_x2 = rect->x1;
1575 int draw_y1 = rect->y0;
1576 int draw_y2 = rect->y1;
1578 // Here we'll store the time it took to draw the slowest buffer of this paint.
1579 glong slowest_buffer = 0;
1581 // Find the optimal buffer dimensions
1582 int bw = draw_x2 - draw_x1;
1583 int bh = draw_y2 - draw_y1;
1584 if ((bw < 1) || (bh < 1))
1585 return 0;
1586 int sw, sh;
1587 if (canvas->rendermode != RENDERMODE_OUTLINE) { // use 256K as a compromise to not slow down gradients
1588 /* 256K is the cached buffer and we need 3 channels */
1589 if (bw * bh < 87381) { // 256K/3
1590 // We can go with single buffer
1591 sw = bw;
1592 sh = bh;
1593 } else if (bw <= (16 * 341)) {
1594 // Go with row buffer
1595 sw = bw;
1596 sh = 87381 / bw;
1597 } else if (bh <= (16 * 256)) {
1598 // Go with column buffer
1599 sw = 87381 / bh;
1600 sh = bh;
1601 } else {
1602 sw = 341;
1603 sh = 256;
1604 }
1605 } else { // paths only, so 1M works faster
1606 /* 1M is the cached buffer and we need 3 channels */
1607 if (bw * bh < 349525) { // 1M/3
1608 // We can go with single buffer
1609 sw = bw;
1610 sh = bh;
1611 } else if (bw <= (16 * 682)) {
1612 // Go with row buffer
1613 sw = bw;
1614 sh = 349525 / bw;
1615 } else if (bh <= (16 * 512)) {
1616 // Go with column buffer
1617 sw = 349525 / bh;
1618 sh = bh;
1619 } else {
1620 sw = 682;
1621 sh = 512;
1622 }
1623 }
1625 // Will this paint require more than one buffer?
1626 bool multiple_buffers = (((draw_y2 - draw_y1) > sh) || ((draw_x2 - draw_x1) > sw)); // or two_rects
1628 // remember the counter during this paint
1629 long this_count = canvas->redraw_count;
1631 // Time values to measure each buffer's paint time
1632 GTimeVal tstart, tfinish;
1634 // This is the main loop which corresponds to the visible left-to-right, top-to-bottom drawing
1635 // of screen blocks (buffers).
1636 for (int y0 = draw_y1; y0 < draw_y2; y0 += sh) {
1637 int y1 = MIN (y0 + sh, draw_y2);
1638 for (int x0 = draw_x1; x0 < draw_x2; x0 += sw) {
1639 int x1 = MIN (x0 + sw, draw_x2);
1641 // OPTIMIZATION IDEA: if drawing is really slow (as measured by canvas->slowest
1642 // buffer), process some events even BEFORE we do any buffers?
1644 // Paint one buffer; measure how long it takes.
1645 g_get_current_time (&tstart);
1646 sp_canvas_paint_single_buffer (canvas, x0, y0, x1, y1, draw_x1, draw_y1, draw_x2, draw_y2, sw);
1647 g_get_current_time (&tfinish);
1649 // Remember the slowest_buffer of this paint.
1650 glong this_buffer = (tfinish.tv_sec - tstart.tv_sec) * 1000000 + (tfinish.tv_usec - tstart.tv_usec);
1651 if (this_buffer > slowest_buffer)
1652 slowest_buffer = this_buffer;
1654 // After each successful buffer, reduce the rect remaining to redraw by what is already redrawn
1655 if (x1 >= draw_x2 && canvas->redraw_aborted.y0 < y1)
1656 canvas->redraw_aborted.y0 = y1;
1657 if (y_aborted_limit != NULL && canvas->redraw_aborted.y0 > *y_aborted_limit)
1658 canvas->redraw_aborted.y0 = *y_aborted_limit;
1660 if (y1 >= draw_y2 && canvas->redraw_aborted.x0 < x1)
1661 canvas->redraw_aborted.x0 = x1;
1662 if (x_aborted_limit != NULL && canvas->redraw_aborted.x0 > *x_aborted_limit)
1663 canvas->redraw_aborted.x0 = *x_aborted_limit;
1665 // INTERRUPTIBLE DISPLAY:
1666 // Process events that may have arrived while we were busy drawing;
1667 // only if we're drawing multiple buffers, and only if this one was not very fast,
1668 // and only if we're allowed to interrupt this redraw
1669 bool ok_to_interrupt = (multiple_buffers && this_buffer > 25000);
1670 if (ok_to_interrupt && (canvas->forced_redraw_limit != -1)) {
1671 ok_to_interrupt = (canvas->forced_redraw_count < canvas->forced_redraw_limit);
1672 }
1674 if (ok_to_interrupt) {
1675 // Run at most max_iterations of the main loop; we cannot process ALL events
1676 // here because some things (e.g. rubberband) flood with dirtying events but will
1677 // not redraw themselves
1678 int max_iterations = 10;
1679 int iterations = 0;
1680 while (Gtk::Main::events_pending() && iterations++ < max_iterations) {
1681 Gtk::Main::iteration(false);
1682 // If one of the iterations has redrawn by itself, abort
1683 if (this_count != canvas->redraw_count) {
1684 canvas->slowest_buffer = slowest_buffer;
1685 if (canvas->forced_redraw_limit != -1) {
1686 canvas->forced_redraw_count++;
1687 }
1688 return 1; // interrupted
1689 }
1690 }
1692 // If not aborted so far, check if the events set redraw or update flags;
1693 // if so, force update and abort
1694 if (canvas->need_redraw || canvas->need_update) {
1695 canvas->slowest_buffer = slowest_buffer;
1696 if (canvas->forced_redraw_limit != -1) {
1697 canvas->forced_redraw_count++;
1698 }
1699 do_update (canvas);
1700 return 1; // interrupted
1701 }
1702 }
1703 }
1704 }
1706 // Remember the slowest buffer of this paint in canvas
1707 canvas->slowest_buffer = slowest_buffer;
1709 return 0; // finished
1710 }
1713 /**
1714 * Helper that draws a specific rectangular part of the canvas.
1715 */
1716 static void
1717 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1718 {
1719 g_return_if_fail (!canvas->need_update);
1721 // Monotonously increment the canvas-global counter on each paint. This will let us find out
1722 // when a new paint happened in event processing during this paint, so we can abort it.
1723 canvas->redraw_count++;
1725 NRRectL rect;
1726 rect.x0 = xx0;
1727 rect.x1 = xx1;
1728 rect.y0 = yy0;
1729 rect.y1 = yy1;
1731 // Clip rect-to-draw by the current visible area
1732 rect.x0 = MAX (rect.x0, canvas->x0);
1733 rect.y0 = MAX (rect.y0, canvas->y0);
1734 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1735 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1737 // Clip rect-aborted-last-time by the current visible area
1738 canvas->redraw_aborted.x0 = MAX (canvas->redraw_aborted.x0, canvas->x0);
1739 canvas->redraw_aborted.y0 = MAX (canvas->redraw_aborted.y0, canvas->y0);
1740 canvas->redraw_aborted.x1 = MIN (canvas->redraw_aborted.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1741 canvas->redraw_aborted.y1 = MIN (canvas->redraw_aborted.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1743 if (canvas->redraw_aborted.x0 < canvas->redraw_aborted.x1 && canvas->redraw_aborted.y0 < canvas->redraw_aborted.y1) {
1744 // There was an aborted redraw last time, now we need to redraw BOTH it and the new rect.
1746 // save the old aborted rect in case we decide to paint it separately (see below)
1747 NRRectL aborted = canvas->redraw_aborted;
1749 // calculate the rectangle union of the both rects (the smallest rectangle which covers both)
1750 NRRectL nion;
1751 nr_rect_l_union (&nion, &rect, &aborted);
1753 // subtract one of the rects-to-draw from the other (the smallest rectangle which covers
1754 // all of the first not covered by the second)
1755 NRRectL rect_minus_aborted;
1756 nr_rect_l_subtract (&rect_minus_aborted, &rect, &aborted);
1758 // Initially, the rect to redraw later (in case we're aborted) is the same as the union of both rects
1759 canvas->redraw_aborted = nion;
1761 // calculate areas of the three rects
1762 if ((nr_rect_l_area(&rect_minus_aborted) + nr_rect_l_area(&aborted)) * 1.2 < nr_rect_l_area(&nion)) {
1763 // If the summary area of the two rects is significantly (at least by 20%) less than
1764 // the area of their rectangular union, it makes sense to paint the two rects
1765 // separately instead of painting their union. This gives a significant speedup when,
1766 // for example, your current canvas is almost painted, with only a strip at bottom
1767 // left, and at that moment you abort it by scrolling down which reveals a new strip at
1768 // the top. Straightforward painting of the union of the aborted rect and the new rect
1769 // will have to repaint the entire canvas! By contrast, the optimized approach below
1770 // paints the two narrow strips in order which is much faster.
1772 // find out which rect to draw first - compare them first by y then by x of the top left corners
1773 NRRectL *first;
1774 NRRectL *second;
1775 if (rect.y0 == aborted.y0) {
1776 if (rect.x0 < aborted.x0) {
1777 first = ▭
1778 second = &aborted;
1779 } else {
1780 second = ▭
1781 first = &aborted;
1782 }
1783 } else if (rect.y0 < aborted.y0) {
1784 first = ▭
1785 second = &aborted;
1786 } else {
1787 second = ▭
1788 first = &aborted;
1789 }
1791 NRRectL second_minus_first;
1792 nr_rect_l_subtract (&second_minus_first, second, first);
1794 // paint the first rect;
1795 if (sp_canvas_paint_rect_internal (canvas, first, &(second_minus_first.x0), &(second_minus_first.y0))) {
1796 // aborted!
1797 return;
1798 }
1800 // if not aborted, assign (second rect minus first) as the new redraw_aborted and paint the same
1801 canvas->redraw_aborted = second_minus_first;
1802 if (sp_canvas_paint_rect_internal (canvas, &second_minus_first, NULL, NULL)) {
1803 return; // aborted
1804 }
1806 } else {
1807 // no need for separate drawing, just draw the union as one rect
1808 if (sp_canvas_paint_rect_internal (canvas, &nion, NULL, NULL)) {
1809 return; // aborted
1810 }
1811 }
1812 } else {
1813 // Nothing was aborted last time, just draw the rect we're given
1815 // Initially, the rect to redraw later (in case we're aborted) is the same as the one we're going to draw now.
1816 canvas->redraw_aborted = rect;
1818 if (sp_canvas_paint_rect_internal (canvas, &rect, NULL, NULL)) {
1819 return; // aborted
1820 }
1821 }
1823 // we've had a full unaborted redraw, reset the full redraw counter
1824 if (canvas->forced_redraw_limit != -1) {
1825 canvas->forced_redraw_count = 0;
1826 }
1827 }
1829 /**
1830 * Force a full redraw after a specified number of interrupted redraws
1831 */
1832 void
1833 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1834 g_return_if_fail(canvas != NULL);
1836 canvas->forced_redraw_limit = count;
1837 canvas->forced_redraw_count = 0;
1838 }
1840 /**
1841 * End forced full redraw requests
1842 */
1843 void
1844 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1845 g_return_if_fail(canvas != NULL);
1847 canvas->forced_redraw_limit = -1;
1848 }
1850 /**
1851 * The canvas widget's expose callback.
1852 */
1853 static gint
1854 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1855 {
1856 SPCanvas *canvas = SP_CANVAS (widget);
1858 if (!GTK_WIDGET_DRAWABLE (widget) ||
1859 (event->window != SP_CANVAS_WINDOW (canvas)))
1860 return FALSE;
1862 int n_rects;
1863 GdkRectangle *rects;
1864 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1866 for (int i = 0; i < n_rects; i++) {
1867 NRRectL rect;
1869 rect.x0 = rects[i].x + canvas->x0;
1870 rect.y0 = rects[i].y + canvas->y0;
1871 rect.x1 = rect.x0 + rects[i].width;
1872 rect.y1 = rect.y0 + rects[i].height;
1874 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1875 }
1877 if (n_rects > 0)
1878 g_free (rects);
1880 return FALSE;
1881 }
1883 /**
1884 * The canvas widget's keypress callback.
1885 */
1886 static gint
1887 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1888 {
1889 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1890 }
1892 /**
1893 * Crossing event handler for the canvas.
1894 */
1895 static gint
1896 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1897 {
1898 SPCanvas *canvas = SP_CANVAS (widget);
1900 if (event->window != SP_CANVAS_WINDOW (canvas))
1901 return FALSE;
1903 canvas->state = event->state;
1904 return pick_current_item (canvas, (GdkEvent *) event);
1905 }
1907 /**
1908 * Focus in handler for the canvas.
1909 */
1910 static gint
1911 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1912 {
1913 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1915 SPCanvas *canvas = SP_CANVAS (widget);
1917 if (canvas->focused_item) {
1918 return emit_event (canvas, (GdkEvent *) event);
1919 } else {
1920 return FALSE;
1921 }
1922 }
1924 /**
1925 * Focus out handler for the canvas.
1926 */
1927 static gint
1928 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1929 {
1930 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1932 SPCanvas *canvas = SP_CANVAS (widget);
1934 if (canvas->focused_item)
1935 return emit_event (canvas, (GdkEvent *) event);
1936 else
1937 return FALSE;
1938 }
1940 /**
1941 * Helper that repaints the areas in the canvas that need it.
1942 */
1943 static int
1944 paint (SPCanvas *canvas)
1945 {
1946 if (canvas->need_update) {
1947 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1948 canvas->need_update = FALSE;
1949 }
1951 if (!canvas->need_redraw)
1952 return TRUE;
1954 GtkWidget const *widget = GTK_WIDGET(canvas);
1955 int const canvas_x1 = canvas->x0 + widget->allocation.width;
1956 int const canvas_y1 = canvas->y0 + widget->allocation.height;
1958 NRRectL topaint;
1959 topaint.x0 = topaint.y0 = topaint.x1 = topaint.y1 = 0;
1961 for (int j=canvas->tTop&(~3);j<canvas->tBottom;j+=4) {
1962 for (int i=canvas->tLeft&(~3);i<canvas->tRight;i+=4) {
1963 int mode=0;
1965 int pl=i+1,pr=i,pt=j+4,pb=j;
1966 for (int l=MAX(j,canvas->tTop);l<MIN(j+4,canvas->tBottom);l++) {
1967 for (int k=MAX(i,canvas->tLeft);k<MIN(i+4,canvas->tRight);k++) {
1968 if ( canvas->tiles[(k-canvas->tLeft)+(l-canvas->tTop)*canvas->tileH] ) {
1969 mode|=1<<((k-i)+(l-j)*4);
1970 if ( k < pl ) pl=k;
1971 if ( k+1 > pr ) pr=k+1;
1972 if ( l < pt ) pt=l;
1973 if ( l+1 > pb ) pb=l+1;
1974 }
1975 canvas->tiles[(k-canvas->tLeft)+(l-canvas->tTop)*canvas->tileH]=0;
1976 }
1977 }
1979 if ( mode ) {
1980 NRRectL tile;
1981 tile.x0 = MAX (pl*32, canvas->x0);
1982 tile.y0 = MAX (pt*32, canvas->y0);
1983 tile.x1 = MIN (pr*32, canvas_x1);
1984 tile.y1 = MIN (pb*32, canvas_y1);
1985 if ((tile.x0 < tile.x1) && (tile.y0 < tile.y1)) {
1986 nr_rect_l_union (&topaint, &topaint, &tile);
1987 }
1989 }
1990 }
1991 }
1993 canvas->need_redraw = FALSE;
1994 sp_canvas_paint_rect (canvas, topaint.x0, topaint.y0, topaint.x1, topaint.y1);
1996 return TRUE;
1997 }
1999 /**
2000 * Helper that invokes update, paint, and repick on canvas.
2001 */
2002 static int
2003 do_update (SPCanvas *canvas)
2004 {
2005 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2006 return TRUE;
2008 /* Cause the update if necessary */
2009 if (canvas->need_update) {
2010 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2011 canvas->need_update = FALSE;
2012 }
2014 /* Paint if able to */
2015 if (GTK_WIDGET_DRAWABLE (canvas)) {
2016 return paint (canvas);
2017 }
2019 /* Pick new current item */
2020 while (canvas->need_repick) {
2021 canvas->need_repick = FALSE;
2022 pick_current_item (canvas, &canvas->pick_event);
2023 }
2025 return TRUE;
2026 }
2028 /**
2029 * Idle handler for the canvas that deals with pending updates and redraws.
2030 */
2031 static gint
2032 idle_handler (gpointer data)
2033 {
2034 GDK_THREADS_ENTER ();
2036 SPCanvas *canvas = SP_CANVAS (data);
2038 const int ret = do_update (canvas);
2040 if (ret) {
2041 /* Reset idle id */
2042 canvas->idle_id = 0;
2043 }
2045 GDK_THREADS_LEAVE ();
2047 return !ret;
2048 }
2050 /**
2051 * Convenience function to add an idle handler to a canvas.
2052 */
2053 static void
2054 add_idle (SPCanvas *canvas)
2055 {
2056 if (canvas->idle_id != 0)
2057 return;
2059 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2060 }
2062 /**
2063 * Returns the root group of the specified canvas.
2064 */
2065 SPCanvasGroup *
2066 sp_canvas_root (SPCanvas *canvas)
2067 {
2068 g_return_val_if_fail (canvas != NULL, NULL);
2069 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2071 return SP_CANVAS_GROUP (canvas->root);
2072 }
2074 /**
2075 * Scrolls canvas to specific position.
2076 */
2077 void
2078 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear)
2079 {
2080 g_return_if_fail (canvas != NULL);
2081 g_return_if_fail (SP_IS_CANVAS (canvas));
2083 int ix = (int) (cx + 0.5);
2084 int iy = (int) (cy + 0.5);
2085 int dx = ix - canvas->x0;
2086 int dy = iy - canvas->y0;
2088 canvas->dx0 = cx;
2089 canvas->dy0 = cy;
2090 canvas->x0 = ix;
2091 canvas->y0 = iy;
2093 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+canvas->widget.allocation.width,canvas->y0+canvas->widget.allocation.height);
2095 if (!clear) {
2096 // scrolling without zoom; redraw only the newly exposed areas
2097 if ((dx != 0) || (dy != 0)) {
2098 int width, height;
2099 width = canvas->widget.allocation.width;
2100 height = canvas->widget.allocation.height;
2101 if (GTK_WIDGET_REALIZED (canvas)) {
2102 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2103 gdk_window_process_updates (SP_CANVAS_WINDOW (canvas), TRUE);
2104 }
2105 if (dx < 0) {
2106 sp_canvas_request_redraw (canvas, ix + 0, iy + 0, ix - dx, iy + height);
2107 } else if (dx > 0) {
2108 sp_canvas_request_redraw (canvas, ix + width - dx, iy + 0, ix + width, iy + height);
2109 }
2110 if (dy < 0) {
2111 sp_canvas_request_redraw (canvas, ix + 0, iy + 0, ix + width, iy - dy);
2112 } else if (dy > 0) {
2113 sp_canvas_request_redraw (canvas, ix + 0, iy + height - dy, ix + width, iy + height);
2114 }
2115 }
2116 } else {
2117 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2118 }
2119 }
2121 /**
2122 * Updates canvas if necessary.
2123 */
2124 void
2125 sp_canvas_update_now (SPCanvas *canvas)
2126 {
2127 g_return_if_fail (canvas != NULL);
2128 g_return_if_fail (SP_IS_CANVAS (canvas));
2130 if (!(canvas->need_update ||
2131 canvas->need_redraw))
2132 return;
2134 remove_idle (canvas);
2135 do_update (canvas);
2136 }
2138 /**
2139 * Update callback for canvas widget.
2140 */
2141 static void
2142 sp_canvas_request_update (SPCanvas *canvas)
2143 {
2144 canvas->need_update = TRUE;
2145 add_idle (canvas);
2146 }
2148 /**
2149 * Forces redraw of rectangular canvas area.
2150 */
2151 void
2152 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2153 {
2154 NRRectL bbox;
2155 NRRectL visible;
2156 NRRectL clip;
2158 g_return_if_fail (canvas != NULL);
2159 g_return_if_fail (SP_IS_CANVAS (canvas));
2161 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2162 if ((x0 >= x1) || (y0 >= y1)) return;
2164 bbox.x0 = x0;
2165 bbox.y0 = y0;
2166 bbox.x1 = x1;
2167 bbox.y1 = y1;
2169 visible.x0 = canvas->x0;
2170 visible.y0 = canvas->y0;
2171 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2172 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2174 nr_rect_l_intersect (&clip, &bbox, &visible);
2176 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2177 add_idle (canvas);
2178 }
2180 /**
2181 * Sets world coordinates from win and canvas.
2182 */
2183 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2184 {
2185 g_return_if_fail (canvas != NULL);
2186 g_return_if_fail (SP_IS_CANVAS (canvas));
2188 if (worldx) *worldx = canvas->x0 + winx;
2189 if (worldy) *worldy = canvas->y0 + winy;
2190 }
2192 /**
2193 * Sets win coordinates from world and canvas.
2194 */
2195 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2196 {
2197 g_return_if_fail (canvas != NULL);
2198 g_return_if_fail (SP_IS_CANVAS (canvas));
2200 if (winx) *winx = worldx - canvas->x0;
2201 if (winy) *winy = worldy - canvas->y0;
2202 }
2204 /**
2205 * Converts point from win to world coordinates.
2206 */
2207 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2208 {
2209 g_assert (canvas != NULL);
2210 g_assert (SP_IS_CANVAS (canvas));
2212 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2213 }
2215 /**
2216 * Converts point from world to win coordinates.
2217 */
2218 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2219 {
2220 g_assert (canvas != NULL);
2221 g_assert (SP_IS_CANVAS (canvas));
2223 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2224 }
2226 /**
2227 * Returns true if point given in world coordinates is inside window.
2228 */
2229 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2230 {
2231 g_assert( canvas != NULL );
2232 g_assert(SP_IS_CANVAS(canvas));
2234 using NR::X;
2235 using NR::Y;
2236 GtkWidget const &w = *GTK_WIDGET(canvas);
2237 return ( ( canvas->x0 <= world[X] ) &&
2238 ( canvas->y0 <= world[Y] ) &&
2239 ( world[X] < canvas->x0 + w.allocation.width ) &&
2240 ( world[Y] < canvas->y0 + w.allocation.height ) );
2241 }
2243 /**
2244 * Return canvas window coordinates as NRRect.
2245 */
2246 NR::Rect SPCanvas::getViewbox() const
2247 {
2248 GtkWidget const *w = GTK_WIDGET(this);
2250 return NR::Rect(NR::Point(dx0, dy0),
2251 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2252 }
2254 inline int sp_canvas_tile_floor(int x)
2255 {
2256 return (x&(~31))/32;
2257 }
2259 inline int sp_canvas_tile_ceil(int x)
2260 {
2261 return ((x+31)&(~31))/32;
2262 }
2264 /**
2265 * Helper that changes tile size for canvas redraw.
2266 */
2267 void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2268 {
2269 if ( nl >= nr || nt >= nb ) {
2270 if ( canvas->tiles ) g_free(canvas->tiles);
2271 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2272 canvas->tileH=canvas->tileV=0;
2273 canvas->tiles=NULL;
2274 return;
2275 }
2276 int tl=sp_canvas_tile_floor(nl);
2277 int tt=sp_canvas_tile_floor(nt);
2278 int tr=sp_canvas_tile_ceil(nr);
2279 int tb=sp_canvas_tile_ceil(nb);
2281 int nh=tr-tl,nv=tb-tt;
2282 uint8_t* ntiles=(uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2283 for (int i=tl;i<tr;i++) {
2284 for (int j=tt;j<tb;j++) {
2285 int ind=(i-tl)+(j-tt)*nh;
2286 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2287 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH];
2288 } else {
2289 ntiles[ind]=0;
2290 }
2291 }
2292 }
2293 if ( canvas->tiles ) g_free(canvas->tiles);
2294 canvas->tiles=ntiles;
2295 canvas->tLeft=tl;
2296 canvas->tTop=tt;
2297 canvas->tRight=tr;
2298 canvas->tBottom=tb;
2299 canvas->tileH=nh;
2300 canvas->tileV=nv;
2301 }
2303 /**
2304 * Helper that marks specific canvas rectangle for redraw.
2305 */
2306 void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2307 {
2308 if ( nl >= nr || nt >= nb ) {
2309 return;
2310 }
2311 int tl=sp_canvas_tile_floor(nl);
2312 int tt=sp_canvas_tile_floor(nt);
2313 int tr=sp_canvas_tile_ceil(nr);
2314 int tb=sp_canvas_tile_ceil(nb);
2315 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2316 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2317 if ( tr > canvas->tRight ) tr=canvas->tRight;
2318 if ( tt < canvas->tTop ) tt=canvas->tTop;
2319 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2321 canvas->need_redraw = TRUE;
2323 for (int i=tl;i<tr;i++) {
2324 for (int j=tt;j<tb;j++) {
2325 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]=1;
2326 }
2327 }
2328 }
2331 /*
2332 Local Variables:
2333 mode:c++
2334 c-file-style:"stroustrup"
2335 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2336 indent-tabs-mode:nil
2337 fill-column:99
2338 End:
2339 */
2340 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :