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"
37 #include "box3d-context.h"
38 #include "inkscape.h"
40 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
41 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
42 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
43 #define TILE_SIZE 32
45 enum {
46 RENDERMODE_NORMAL,
47 RENDERMODE_NOAA,
48 RENDERMODE_OUTLINE
49 };
51 const gint sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
53 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
55 enum {
56 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
57 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
58 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
59 };
61 /**
62 * A group of Items.
63 */
64 struct SPCanvasGroup {
65 SPCanvasItem item;
67 GList *items, *last;
68 };
70 /**
71 * The SPCanvasGroup vtable.
72 */
73 struct SPCanvasGroupClass {
74 SPCanvasItemClass parent_class;
75 };
77 /**
78 * The SPCanvas vtable.
79 */
80 struct SPCanvasClass {
81 GtkWidgetClass parent_class;
82 };
84 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
85 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
87 /* SPCanvasItem */
89 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
92 static void sp_canvas_request_update (SPCanvas *canvas);
94 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
95 static void sp_canvas_item_init (SPCanvasItem *item);
96 static void sp_canvas_item_dispose (GObject *object);
97 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args);
99 static int emit_event (SPCanvas *canvas, GdkEvent *event);
101 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
103 static GtkObjectClass *item_parent_class;
105 /**
106 * Registers the SPCanvasItem class with Glib and returns its type number.
107 */
108 GType
109 sp_canvas_item_get_type (void)
110 {
111 static GType type = 0;
112 if (!type) {
113 static const GTypeInfo info = {
114 sizeof (SPCanvasItemClass),
115 NULL, NULL,
116 (GClassInitFunc) sp_canvas_item_class_init,
117 NULL, NULL,
118 sizeof (SPCanvasItem),
119 0,
120 (GInstanceInitFunc) sp_canvas_item_init,
121 NULL
122 };
123 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
124 }
126 return type;
127 }
129 /**
130 * Initializes the SPCanvasItem vtable and the "event" signal.
131 */
132 static void
133 sp_canvas_item_class_init (SPCanvasItemClass *klass)
134 {
135 GObjectClass *object_class = (GObjectClass *) klass;
137 /* fixme: Derive from GObject */
138 item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
140 item_signals[ITEM_EVENT] = g_signal_new ("event",
141 G_TYPE_FROM_CLASS (klass),
142 G_SIGNAL_RUN_LAST,
143 G_STRUCT_OFFSET (SPCanvasItemClass, event),
144 NULL, NULL,
145 sp_marshal_BOOLEAN__POINTER,
146 G_TYPE_BOOLEAN, 1,
147 GDK_TYPE_EVENT);
149 object_class->dispose = sp_canvas_item_dispose;
150 }
152 /**
153 * Callback for initialization of SPCanvasItem.
154 */
155 static void
156 sp_canvas_item_init (SPCanvasItem *item)
157 {
158 item->flags |= SP_CANVAS_ITEM_VISIBLE;
159 item->xform = NR::Matrix(NR::identity());
160 }
162 /**
163 * Constructs new SPCanvasItem on SPCanvasGroup.
164 */
165 SPCanvasItem *
166 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, const gchar *first_arg_name, ...)
167 {
168 va_list args;
170 g_return_val_if_fail (parent != NULL, NULL);
171 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
172 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
174 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
176 va_start (args, first_arg_name);
177 sp_canvas_item_construct (item, parent, first_arg_name, args);
178 va_end (args);
180 return item;
181 }
183 /**
184 * Sets up the newly created SPCanvasItem.
185 *
186 * We make it static for encapsulation reasons since it was nowhere used.
187 */
188 static void
189 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args)
190 {
191 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
192 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
194 item->parent = SP_CANVAS_ITEM (parent);
195 item->canvas = item->parent->canvas;
197 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
199 group_add (SP_CANVAS_GROUP (item->parent), item);
201 sp_canvas_item_request_update (item);
202 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
203 item->canvas->need_repick = TRUE;
204 }
206 /**
207 * Helper function that requests redraw only if item's visible flag is set.
208 */
209 static void
210 redraw_if_visible (SPCanvasItem *item)
211 {
212 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
213 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
214 }
215 }
217 /**
218 * Callback that removes item from all referers and destroys it.
219 */
220 static void
221 sp_canvas_item_dispose (GObject *object)
222 {
223 SPCanvasItem *item = SP_CANVAS_ITEM (object);
225 redraw_if_visible (item);
226 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
228 if (item == item->canvas->current_item) {
229 item->canvas->current_item = NULL;
230 item->canvas->need_repick = TRUE;
231 }
233 if (item == item->canvas->new_current_item) {
234 item->canvas->new_current_item = NULL;
235 item->canvas->need_repick = TRUE;
236 }
238 if (item == item->canvas->grabbed_item) {
239 item->canvas->grabbed_item = NULL;
240 gdk_pointer_ungrab (GDK_CURRENT_TIME);
241 }
243 if (item == item->canvas->focused_item)
244 item->canvas->focused_item = NULL;
246 if (item->parent) {
247 group_remove (SP_CANVAS_GROUP (item->parent), item);
248 }
250 G_OBJECT_CLASS (item_parent_class)->dispose (object);
251 }
253 /**
254 * Helper function to update item and its children.
255 *
256 * NB! affine is parent2canvas.
257 */
258 static void
259 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
260 {
261 /* Apply the child item's transform */
262 NR::Matrix child_affine = item->xform * affine;
264 /* apply object flags to child flags */
265 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
267 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
268 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
270 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
271 child_flags |= SP_CANVAS_UPDATE_AFFINE;
273 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
274 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
275 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
276 }
278 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
279 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
280 }
282 /**
283 * Helper function to invoke the point method of the item.
284 *
285 * The argument x, y should be in the parent's item-relative coordinate
286 * system. This routine applies the inverse of the item's transform,
287 * maintaining the affine invariant.
288 */
289 static double
290 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
291 {
292 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
293 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
295 return NR_HUGE;
296 }
298 /**
299 * Makes the item's affine transformation matrix be equal to the specified
300 * matrix.
301 *
302 * @item: A canvas item.
303 * @affine: An affine transformation matrix.
304 */
305 void
306 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const& affine)
307 {
308 item->xform = affine;
310 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
311 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
312 if (item->parent != NULL) {
313 sp_canvas_item_request_update (item->parent);
314 } else {
315 sp_canvas_request_update (item->canvas);
316 }
317 }
319 item->canvas->need_repick = TRUE;
320 }
322 /**
323 * Convenience function to reorder items in a group's child list.
324 *
325 * This puts the specified link after the "before" link.
326 */
327 static void
328 put_item_after (GList *link, GList *before)
329 {
330 if (link == before)
331 return;
333 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
335 if (before == NULL) {
336 if (link == parent->items) return;
338 link->prev->next = link->next;
340 if (link->next) {
341 link->next->prev = link->prev;
342 } else {
343 parent->last = link->prev;
344 }
346 link->prev = before;
347 link->next = parent->items;
348 link->next->prev = link;
349 parent->items = link;
350 } else {
351 if ((link == parent->last) && (before == parent->last->prev))
352 return;
354 if (link->next)
355 link->next->prev = link->prev;
357 if (link->prev)
358 link->prev->next = link->next;
359 else {
360 parent->items = link->next;
361 parent->items->prev = NULL;
362 }
364 link->prev = before;
365 link->next = before->next;
367 link->prev->next = link;
369 if (link->next)
370 link->next->prev = link;
371 else
372 parent->last = link;
373 }
374 }
377 /**
378 * Raises the item in its parent's stack by the specified number of positions.
379 *
380 * \param item A canvas item.
381 * \param positions Number of steps to raise the item.
382 *
383 * If the number of positions is greater than the distance to the top of the
384 * stack, then the item is put at the top.
385 */
386 void
387 sp_canvas_item_raise (SPCanvasItem *item, int positions)
388 {
389 g_return_if_fail (item != NULL);
390 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
391 g_return_if_fail (positions >= 0);
393 if (!item->parent || positions == 0)
394 return;
396 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
397 GList *link = g_list_find (parent->items, item);
398 g_assert (link != NULL);
400 GList *before;
401 for (before = link; positions && before; positions--)
402 before = before->next;
404 if (!before)
405 before = parent->last;
407 put_item_after (link, before);
409 redraw_if_visible (item);
410 item->canvas->need_repick = TRUE;
411 }
414 /**
415 * Lowers the item in its parent's stack by the specified number of positions.
416 *
417 * \param item A canvas item.
418 * \param positions Number of steps to lower the item.
419 *
420 * If the number of positions is greater than the distance to the bottom of the
421 * stack, then the item is put at the bottom.
422 **/
423 void
424 sp_canvas_item_lower (SPCanvasItem *item, int positions)
425 {
426 g_return_if_fail (item != NULL);
427 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
428 g_return_if_fail (positions >= 1);
430 if (!item->parent || positions == 0)
431 return;
433 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
434 GList *link = g_list_find (parent->items, item);
435 g_assert (link != NULL);
437 GList *before;
438 if (link->prev)
439 for (before = link->prev; positions && before; positions--)
440 before = before->prev;
441 else
442 before = NULL;
444 put_item_after (link, before);
446 redraw_if_visible (item);
447 item->canvas->need_repick = TRUE;
448 }
450 /**
451 * Sets visible flag on item and requests a redraw.
452 */
453 void
454 sp_canvas_item_show (SPCanvasItem *item)
455 {
456 g_return_if_fail (item != NULL);
457 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
459 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
460 return;
462 item->flags |= SP_CANVAS_ITEM_VISIBLE;
464 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
465 item->canvas->need_repick = TRUE;
466 }
468 /**
469 * Clears visible flag on item and requests a redraw.
470 */
471 void
472 sp_canvas_item_hide (SPCanvasItem *item)
473 {
474 g_return_if_fail (item != NULL);
475 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
477 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
478 return;
480 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
482 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
483 item->canvas->need_repick = TRUE;
484 }
486 /**
487 * Grab item under cursor.
488 *
489 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
490 */
491 int
492 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
493 {
494 g_return_val_if_fail (item != NULL, -1);
495 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
496 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
498 if (item->canvas->grabbed_item)
499 return -1;
501 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
502 return -1;
504 /* fixme: Top hack (Lauris) */
505 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
506 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
507 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
508 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
509 NULL, cursor, etime);
511 item->canvas->grabbed_item = item;
512 item->canvas->grabbed_event_mask = event_mask;
513 item->canvas->current_item = item; /* So that events go to the grabbed item */
515 return 0;
516 }
518 /**
519 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
520 * mouse.
521 *
522 * \param item A canvas item that holds a grab.
523 * \param etime The timestamp for ungrabbing the mouse.
524 */
525 void
526 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
527 {
528 g_return_if_fail (item != NULL);
529 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
531 if (item->canvas->grabbed_item != item)
532 return;
534 item->canvas->grabbed_item = NULL;
536 gdk_pointer_ungrab (etime);
537 }
539 /**
540 * Returns the product of all transformation matrices from the root item down
541 * to the item.
542 */
543 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
544 {
545 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
547 NR::Matrix affine = NR::identity();
549 while (item) {
550 affine *= item->xform;
551 item = item->parent;
552 }
553 return affine;
554 }
556 /**
557 * Helper that returns true iff item is descendant of parent.
558 */
559 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
560 {
561 while (item) {
562 if (item == parent)
563 return true;
564 item = item->parent;
565 }
567 return false;
568 }
570 /**
571 * Focus canvas, and item under cursor if it is not already focussed.
572 */
573 void
574 sp_canvas_item_grab_focus (SPCanvasItem *item)
575 {
576 g_return_if_fail (item != NULL);
577 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
578 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
580 SPCanvasItem *focused_item = item->canvas->focused_item;
582 if (focused_item) {
583 GdkEvent ev;
584 ev.focus_change.type = GDK_FOCUS_CHANGE;
585 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
586 ev.focus_change.send_event = FALSE;
587 ev.focus_change.in = FALSE;
589 emit_event (item->canvas, &ev);
590 }
592 item->canvas->focused_item = item;
593 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
595 if (focused_item) {
596 GdkEvent ev;
597 ev.focus_change.type = GDK_FOCUS_CHANGE;
598 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
599 ev.focus_change.send_event = FALSE;
600 ev.focus_change.in = TRUE;
602 emit_event (item->canvas, &ev);
603 }
604 }
606 /**
607 * Requests that the canvas queue an update for the specified item.
608 *
609 * To be used only by item implementations.
610 */
611 void
612 sp_canvas_item_request_update (SPCanvasItem *item)
613 {
614 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
615 return;
617 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
619 if (item->parent != NULL) {
620 /* Recurse up the tree */
621 sp_canvas_item_request_update (item->parent);
622 } else {
623 /* Have reached the top of the tree, make sure the update call gets scheduled. */
624 sp_canvas_request_update (item->canvas);
625 }
626 }
628 /**
629 * Returns position of item in group.
630 */
631 gint sp_canvas_item_order (SPCanvasItem * item)
632 {
633 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
634 }
636 /* SPCanvasGroup */
638 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
639 static void sp_canvas_group_init (SPCanvasGroup *group);
640 static void sp_canvas_group_destroy (GtkObject *object);
642 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
643 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
644 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
646 static SPCanvasItemClass *group_parent_class;
648 /**
649 * Registers SPCanvasGroup class with Gtk and returns its type number.
650 */
651 GtkType
652 sp_canvas_group_get_type (void)
653 {
654 static GtkType group_type = 0;
656 if (!group_type) {
657 static const GtkTypeInfo group_info = {
658 "SPCanvasGroup",
659 sizeof (SPCanvasGroup),
660 sizeof (SPCanvasGroupClass),
661 (GtkClassInitFunc) sp_canvas_group_class_init,
662 (GtkObjectInitFunc) sp_canvas_group_init,
663 NULL, NULL, NULL
664 };
666 group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
667 }
669 return group_type;
670 }
672 /**
673 * Class initialization function for SPCanvasGroupClass
674 */
675 static void
676 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
677 {
678 GtkObjectClass *object_class = (GtkObjectClass *) klass;
679 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
681 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
683 object_class->destroy = sp_canvas_group_destroy;
685 item_class->update = sp_canvas_group_update;
686 item_class->render = sp_canvas_group_render;
687 item_class->point = sp_canvas_group_point;
688 }
690 /**
691 * Callback. Empty.
692 */
693 static void
694 sp_canvas_group_init (SPCanvasGroup */*group*/)
695 {
696 /* Nothing here */
697 }
699 /**
700 * Callback that destroys all items in group and calls group's virtual
701 * destroy() function.
702 */
703 static void
704 sp_canvas_group_destroy (GtkObject *object)
705 {
706 g_return_if_fail (object != NULL);
707 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
709 const SPCanvasGroup *group = SP_CANVAS_GROUP (object);
711 GList *list = group->items;
712 while (list) {
713 SPCanvasItem *child = (SPCanvasItem *)list->data;
714 list = list->next;
716 gtk_object_destroy (GTK_OBJECT (child));
717 }
719 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
720 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
721 }
723 /**
724 * Update handler for canvas groups
725 */
726 static void
727 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
728 {
729 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
730 NR::ConvexHull corners(NR::Point(0, 0));
731 bool empty=true;
733 for (GList *list = group->items; list; list = list->next) {
734 SPCanvasItem *i = (SPCanvasItem *)list->data;
736 sp_canvas_item_invoke_update (i, affine, flags);
738 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
739 if (empty) {
740 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
741 empty = false;
742 } else {
743 corners.add(NR::Point(i->x1, i->y1));
744 }
745 corners.add(NR::Point(i->x2, i->y2));
746 }
747 }
749 NR::Maybe<NR::Rect> const bounds = corners.bounds();
750 if (bounds) {
751 item->x1 = bounds->min()[NR::X];
752 item->y1 = bounds->min()[NR::Y];
753 item->x2 = bounds->max()[NR::X];
754 item->y2 = bounds->max()[NR::Y];
755 } else {
756 // FIXME ?
757 item->x1 = item->x2 = item->y1 = item->y2 = 0;
758 }
759 }
761 /**
762 * Point handler for canvas groups.
763 */
764 static double
765 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
766 {
767 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
768 const double x = p[NR::X];
769 const double y = p[NR::Y];
770 int x1 = (int)(x - item->canvas->close_enough);
771 int y1 = (int)(y - item->canvas->close_enough);
772 int x2 = (int)(x + item->canvas->close_enough);
773 int y2 = (int)(y + item->canvas->close_enough);
775 double best = 0.0;
776 *actual_item = NULL;
778 double dist = 0.0;
780 for (GList *list = group->items; list; list = list->next) {
781 SPCanvasItem *child = (SPCanvasItem *)list->data;
783 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
784 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
786 int has_point;
787 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
788 dist = sp_canvas_item_invoke_point (child, p, &point_item);
789 has_point = TRUE;
790 } else
791 has_point = FALSE;
793 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
794 best = dist;
795 *actual_item = point_item;
796 }
797 }
798 }
800 return best;
801 }
803 /**
804 * Renders all visible canvas group items in buf rectangle.
805 */
806 static void
807 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
808 {
809 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
811 for (GList *list = group->items; list; list = list->next) {
812 SPCanvasItem *child = (SPCanvasItem *)list->data;
813 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
814 if ((child->x1 < buf->rect.x1) &&
815 (child->y1 < buf->rect.y1) &&
816 (child->x2 > buf->rect.x0) &&
817 (child->y2 > buf->rect.y0)) {
818 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
819 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
820 }
821 }
822 }
823 }
825 /**
826 * Adds an item to a canvas group.
827 */
828 static void
829 group_add (SPCanvasGroup *group, SPCanvasItem *item)
830 {
831 gtk_object_ref (GTK_OBJECT (item));
832 gtk_object_sink (GTK_OBJECT (item));
834 if (!group->items) {
835 group->items = g_list_append (group->items, item);
836 group->last = group->items;
837 } else {
838 group->last = g_list_append (group->last, item)->next;
839 }
841 sp_canvas_item_request_update (item);
842 }
844 /**
845 * Removes an item from a canvas group
846 */
847 static void
848 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
849 {
850 g_return_if_fail (group != NULL);
851 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
852 g_return_if_fail (item != NULL);
854 for (GList *children = group->items; children; children = children->next) {
855 if (children->data == item) {
857 /* Unparent the child */
859 item->parent = NULL;
860 gtk_object_unref (GTK_OBJECT (item));
862 /* Remove it from the list */
864 if (children == group->last) group->last = children->prev;
866 group->items = g_list_remove_link (group->items, children);
867 g_list_free (children);
868 break;
869 }
870 }
871 }
873 /* SPCanvas */
875 static void sp_canvas_class_init (SPCanvasClass *klass);
876 static void sp_canvas_init (SPCanvas *canvas);
877 static void sp_canvas_destroy (GtkObject *object);
879 static void sp_canvas_realize (GtkWidget *widget);
880 static void sp_canvas_unrealize (GtkWidget *widget);
882 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
883 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
885 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
886 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
887 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
888 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
889 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
890 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
891 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
892 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
894 static GtkWidgetClass *canvas_parent_class;
896 void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb);
897 void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb);
898 static int do_update (SPCanvas *canvas);
900 /**
901 * Registers the SPCanvas class if necessary, and returns the type ID
902 * associated to it.
903 *
904 * \return The type ID of the SPCanvas class.
905 **/
906 GtkType
907 sp_canvas_get_type (void)
908 {
909 static GtkType canvas_type = 0;
911 if (!canvas_type) {
912 static const GtkTypeInfo canvas_info = {
913 "SPCanvas",
914 sizeof (SPCanvas),
915 sizeof (SPCanvasClass),
916 (GtkClassInitFunc) sp_canvas_class_init,
917 (GtkObjectInitFunc) sp_canvas_init,
918 NULL, NULL, NULL
919 };
921 canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
922 }
924 return canvas_type;
925 }
927 /**
928 * Class initialization function for SPCanvasClass.
929 */
930 static void
931 sp_canvas_class_init (SPCanvasClass *klass)
932 {
933 GtkObjectClass *object_class = (GtkObjectClass *) klass;
934 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
936 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
938 object_class->destroy = sp_canvas_destroy;
940 widget_class->realize = sp_canvas_realize;
941 widget_class->unrealize = sp_canvas_unrealize;
942 widget_class->size_request = sp_canvas_size_request;
943 widget_class->size_allocate = sp_canvas_size_allocate;
944 widget_class->button_press_event = sp_canvas_button;
945 widget_class->button_release_event = sp_canvas_button;
946 widget_class->motion_notify_event = sp_canvas_motion;
947 widget_class->scroll_event = sp_canvas_scroll;
948 widget_class->expose_event = sp_canvas_expose;
949 widget_class->key_press_event = sp_canvas_key;
950 widget_class->key_release_event = sp_canvas_key;
951 widget_class->enter_notify_event = sp_canvas_crossing;
952 widget_class->leave_notify_event = sp_canvas_crossing;
953 widget_class->focus_in_event = sp_canvas_focus_in;
954 widget_class->focus_out_event = sp_canvas_focus_out;
955 }
957 /**
958 * Callback: object initialization for SPCanvas.
959 */
960 static void
961 sp_canvas_init (SPCanvas *canvas)
962 {
963 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
964 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
965 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
967 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
968 canvas->pick_event.crossing.x = 0;
969 canvas->pick_event.crossing.y = 0;
971 /* Create the root item as a special case */
972 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
973 canvas->root->canvas = canvas;
975 gtk_object_ref (GTK_OBJECT (canvas->root));
976 gtk_object_sink (GTK_OBJECT (canvas->root));
978 canvas->need_repick = TRUE;
980 // See comment at in sp-canvas.h.
981 canvas->gen_all_enter_events = false;
983 canvas->tiles=NULL;
984 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
985 canvas->tileH=canvas->tileV=0;
987 canvas->redraw_aborted.x0 = NR_HUGE_L;
988 canvas->redraw_aborted.x1 = -NR_HUGE_L;
989 canvas->redraw_aborted.y0 = NR_HUGE_L;
990 canvas->redraw_aborted.y1 = -NR_HUGE_L;
992 canvas->redraw_count = 0;
994 canvas->forced_redraw_count = 0;
995 canvas->forced_redraw_limit = -1;
997 canvas->slowest_buffer = 0;
999 canvas->is_scrolling = false;
1001 }
1003 /**
1004 * Convenience function to remove the idle handler of a canvas.
1005 */
1006 static void
1007 remove_idle (SPCanvas *canvas)
1008 {
1009 if (canvas->idle_id) {
1010 gtk_idle_remove (canvas->idle_id);
1011 canvas->idle_id = 0;
1012 }
1013 }
1015 /*
1016 * Removes the transient state of the canvas (idle handler, grabs).
1017 */
1018 static void
1019 shutdown_transients (SPCanvas *canvas)
1020 {
1021 /* We turn off the need_redraw flag, since if the canvas is mapped again
1022 * it will request a redraw anyways. We do not turn off the need_update
1023 * flag, though, because updates are not queued when the canvas remaps
1024 * itself.
1025 */
1026 if (canvas->need_redraw) {
1027 canvas->need_redraw = FALSE;
1028 }
1029 if ( canvas->tiles ) g_free(canvas->tiles);
1030 canvas->tiles=NULL;
1031 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1032 canvas->tileH=canvas->tileV=0;
1034 if (canvas->grabbed_item) {
1035 canvas->grabbed_item = NULL;
1036 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1037 }
1039 remove_idle (canvas);
1040 }
1042 /**
1043 * Destroy handler for SPCanvas.
1044 */
1045 static void
1046 sp_canvas_destroy (GtkObject *object)
1047 {
1048 SPCanvas *canvas = SP_CANVAS (object);
1050 if (canvas->root) {
1051 gtk_object_unref (GTK_OBJECT (canvas->root));
1052 canvas->root = NULL;
1053 }
1055 shutdown_transients (canvas);
1057 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1058 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1059 }
1061 /**
1062 * Returns new canvas as widget.
1063 */
1064 GtkWidget *
1065 sp_canvas_new_aa (void)
1066 {
1067 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1069 return (GtkWidget *) canvas;
1070 }
1072 /**
1073 * The canvas widget's realize callback.
1074 */
1075 static void
1076 sp_canvas_realize (GtkWidget *widget)
1077 {
1078 SPCanvas *canvas = SP_CANVAS (widget);
1080 GdkWindowAttr attributes;
1081 attributes.window_type = GDK_WINDOW_CHILD;
1082 attributes.x = widget->allocation.x;
1083 attributes.y = widget->allocation.y;
1084 attributes.width = widget->allocation.width;
1085 attributes.height = widget->allocation.height;
1086 attributes.wclass = GDK_INPUT_OUTPUT;
1087 attributes.visual = gdk_rgb_get_visual ();
1088 attributes.colormap = gdk_rgb_get_cmap ();
1089 attributes.event_mask = (gtk_widget_get_events (widget) |
1090 GDK_EXPOSURE_MASK |
1091 GDK_BUTTON_PRESS_MASK |
1092 GDK_BUTTON_RELEASE_MASK |
1093 GDK_POINTER_MOTION_MASK |
1094 GDK_PROXIMITY_IN_MASK |
1095 GDK_PROXIMITY_OUT_MASK |
1096 GDK_KEY_PRESS_MASK |
1097 GDK_KEY_RELEASE_MASK |
1098 GDK_ENTER_NOTIFY_MASK |
1099 GDK_LEAVE_NOTIFY_MASK |
1100 GDK_FOCUS_CHANGE_MASK);
1101 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1103 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1104 gdk_window_set_user_data (widget->window, widget);
1106 if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1107 gtk_widget_set_events(widget, attributes.event_mask);
1109 widget->style = gtk_style_attach (widget->style, widget->window);
1111 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1113 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1114 }
1116 /**
1117 * The canvas widget's unrealize callback.
1118 */
1119 static void
1120 sp_canvas_unrealize (GtkWidget *widget)
1121 {
1122 SPCanvas *canvas = SP_CANVAS (widget);
1124 shutdown_transients (canvas);
1126 gdk_gc_destroy (canvas->pixmap_gc);
1127 canvas->pixmap_gc = NULL;
1129 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1130 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1131 }
1133 /**
1134 * The canvas widget's size_request callback.
1135 */
1136 static void
1137 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1138 {
1139 static_cast<void>(SP_CANVAS (widget));
1141 req->width = 256;
1142 req->height = 256;
1143 }
1145 /**
1146 * The canvas widget's size_allocate callback.
1147 */
1148 static void
1149 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1150 {
1151 SPCanvas *canvas = SP_CANVAS (widget);
1153 /* Schedule redraw of new region */
1154 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1155 if (allocation->width > widget->allocation.width) {
1156 sp_canvas_request_redraw (canvas,
1157 canvas->x0 + widget->allocation.width,
1158 0,
1159 canvas->x0 + allocation->width,
1160 canvas->y0 + allocation->height);
1161 }
1162 if (allocation->height > widget->allocation.height) {
1163 sp_canvas_request_redraw (canvas,
1164 0,
1165 canvas->y0 + widget->allocation.height,
1166 canvas->x0 + allocation->width,
1167 canvas->y0 + allocation->height);
1168 }
1170 widget->allocation = *allocation;
1172 if (GTK_WIDGET_REALIZED (widget)) {
1173 gdk_window_move_resize (widget->window,
1174 widget->allocation.x, widget->allocation.y,
1175 widget->allocation.width, widget->allocation.height);
1176 }
1177 }
1179 /**
1180 * Helper that emits an event for an item in the canvas, be it the current
1181 * item, grabbed item, or focused item, as appropriate.
1182 */
1183 static int
1184 emit_event (SPCanvas *canvas, GdkEvent *event)
1185 {
1186 guint mask;
1188 if (canvas->grabbed_item) {
1189 switch (event->type) {
1190 case GDK_ENTER_NOTIFY:
1191 mask = GDK_ENTER_NOTIFY_MASK;
1192 break;
1193 case GDK_LEAVE_NOTIFY:
1194 mask = GDK_LEAVE_NOTIFY_MASK;
1195 break;
1196 case GDK_MOTION_NOTIFY:
1197 mask = GDK_POINTER_MOTION_MASK;
1198 break;
1199 case GDK_BUTTON_PRESS:
1200 case GDK_2BUTTON_PRESS:
1201 case GDK_3BUTTON_PRESS:
1202 mask = GDK_BUTTON_PRESS_MASK;
1203 break;
1204 case GDK_BUTTON_RELEASE:
1205 mask = GDK_BUTTON_RELEASE_MASK;
1206 break;
1207 case GDK_KEY_PRESS:
1208 mask = GDK_KEY_PRESS_MASK;
1209 break;
1210 case GDK_KEY_RELEASE:
1211 mask = GDK_KEY_RELEASE_MASK;
1212 break;
1213 case GDK_SCROLL:
1214 mask = GDK_SCROLL;
1215 break;
1216 default:
1217 mask = 0;
1218 break;
1219 }
1221 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1222 }
1224 /* Convert to world coordinates -- we have two cases because of diferent
1225 * offsets of the fields in the event structures.
1226 */
1228 GdkEvent ev = *event;
1230 switch (ev.type) {
1231 case GDK_ENTER_NOTIFY:
1232 case GDK_LEAVE_NOTIFY:
1233 ev.crossing.x += canvas->x0;
1234 ev.crossing.y += canvas->y0;
1235 break;
1236 case GDK_MOTION_NOTIFY:
1237 case GDK_BUTTON_PRESS:
1238 case GDK_2BUTTON_PRESS:
1239 case GDK_3BUTTON_PRESS:
1240 case GDK_BUTTON_RELEASE:
1241 ev.motion.x += canvas->x0;
1242 ev.motion.y += canvas->y0;
1243 break;
1244 default:
1245 break;
1246 }
1248 /* Choose where we send the event */
1250 /* canvas->current_item becomes NULL in some cases under Win32
1251 ** (e.g. if the pointer leaves the window). So this is a hack that
1252 ** Lauris applied to SP to get around the problem.
1253 */
1254 SPCanvasItem* item = NULL;
1255 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1256 item = canvas->grabbed_item;
1257 } else {
1258 item = canvas->current_item;
1259 }
1261 if (canvas->focused_item &&
1262 ((event->type == GDK_KEY_PRESS) ||
1263 (event->type == GDK_KEY_RELEASE) ||
1264 (event->type == GDK_FOCUS_CHANGE))) {
1265 item = canvas->focused_item;
1266 }
1268 /* The event is propagated up the hierarchy (for if someone connected to
1269 * a group instead of a leaf event), and emission is stopped if a
1270 * handler returns TRUE, just like for GtkWidget events.
1271 */
1273 gint finished = FALSE;
1275 while (item && !finished) {
1276 gtk_object_ref (GTK_OBJECT (item));
1277 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1278 SPCanvasItem *parent = item->parent;
1279 gtk_object_unref (GTK_OBJECT (item));
1280 item = parent;
1281 }
1283 return finished;
1284 }
1286 /**
1287 * Helper that re-picks the current item in the canvas, based on the event's
1288 * coordinates and emits enter/leave events for items as appropriate.
1289 */
1290 static int
1291 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1292 {
1293 int button_down = 0;
1294 double x, y;
1296 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1297 return FALSE;
1299 int retval = FALSE;
1301 if (canvas->gen_all_enter_events == false) {
1302 // If a button is down, we'll perform enter and leave events on the
1303 // current item, but not enter on any other item. This is more or
1304 // less like X pointer grabbing for canvas items.
1305 //
1306 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1307 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1309 if (!button_down) canvas->left_grabbed_item = FALSE;
1310 }
1312 /* Save the event in the canvas. This is used to synthesize enter and
1313 * leave events in case the current item changes. It is also used to
1314 * re-pick the current item if the current one gets deleted. Also,
1315 * synthesize an enter event.
1316 */
1317 if (event != &canvas->pick_event) {
1318 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1319 /* these fields have the same offsets in both types of events */
1321 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1322 canvas->pick_event.crossing.window = event->motion.window;
1323 canvas->pick_event.crossing.send_event = event->motion.send_event;
1324 canvas->pick_event.crossing.subwindow = NULL;
1325 canvas->pick_event.crossing.x = event->motion.x;
1326 canvas->pick_event.crossing.y = event->motion.y;
1327 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1328 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1329 canvas->pick_event.crossing.focus = FALSE;
1330 canvas->pick_event.crossing.state = event->motion.state;
1332 /* these fields don't have the same offsets in both types of events */
1334 if (event->type == GDK_MOTION_NOTIFY) {
1335 canvas->pick_event.crossing.x_root = event->motion.x_root;
1336 canvas->pick_event.crossing.y_root = event->motion.y_root;
1337 } else {
1338 canvas->pick_event.crossing.x_root = event->button.x_root;
1339 canvas->pick_event.crossing.y_root = event->button.y_root;
1340 }
1341 } else {
1342 canvas->pick_event = *event;
1343 }
1344 }
1346 /* Don't do anything else if this is a recursive call */
1347 if (canvas->in_repick) return retval;
1349 /* LeaveNotify means that there is no current item, so we don't look for one */
1350 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1351 /* these fields don't have the same offsets in both types of events */
1353 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1354 x = canvas->pick_event.crossing.x;
1355 y = canvas->pick_event.crossing.y;
1356 } else {
1357 x = canvas->pick_event.motion.x;
1358 y = canvas->pick_event.motion.y;
1359 }
1361 /* world coords */
1362 x += canvas->x0;
1363 y += canvas->y0;
1365 /* find the closest item */
1366 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1367 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1368 } else {
1369 canvas->new_current_item = NULL;
1370 }
1371 } else {
1372 canvas->new_current_item = NULL;
1373 }
1375 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1376 return retval; /* current item did not change */
1377 }
1379 /* Synthesize events for old and new current items */
1381 if ((canvas->new_current_item != canvas->current_item)
1382 && (canvas->current_item != NULL)
1383 && !canvas->left_grabbed_item) {
1384 GdkEvent new_event;
1385 SPCanvasItem *item;
1387 item = canvas->current_item;
1389 new_event = canvas->pick_event;
1390 new_event.type = GDK_LEAVE_NOTIFY;
1392 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1393 new_event.crossing.subwindow = NULL;
1394 canvas->in_repick = TRUE;
1395 retval = emit_event (canvas, &new_event);
1396 canvas->in_repick = FALSE;
1397 }
1399 if (canvas->gen_all_enter_events == false) {
1400 // new_current_item may have been set to NULL during the call to
1401 // emit_event() above
1402 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1403 canvas->left_grabbed_item = TRUE;
1404 return retval;
1405 }
1406 }
1408 /* Handle the rest of cases */
1410 canvas->left_grabbed_item = FALSE;
1411 canvas->current_item = canvas->new_current_item;
1413 if (canvas->current_item != NULL) {
1414 GdkEvent new_event;
1416 new_event = canvas->pick_event;
1417 new_event.type = GDK_ENTER_NOTIFY;
1418 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1419 new_event.crossing.subwindow = NULL;
1420 retval = emit_event (canvas, &new_event);
1421 }
1423 return retval;
1424 }
1426 /**
1427 * Button event handler for the canvas.
1428 */
1429 static gint
1430 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1431 {
1432 SPCanvas *canvas = SP_CANVAS (widget);
1434 int retval = FALSE;
1436 /* dispatch normally regardless of the event's window if an item has
1437 has a pointer grab in effect */
1438 if (!canvas->grabbed_item &&
1439 event->window != SP_CANVAS_WINDOW (canvas))
1440 return retval;
1442 int mask;
1443 switch (event->button) {
1444 case 1:
1445 mask = GDK_BUTTON1_MASK;
1446 break;
1447 case 2:
1448 mask = GDK_BUTTON2_MASK;
1449 break;
1450 case 3:
1451 mask = GDK_BUTTON3_MASK;
1452 break;
1453 case 4:
1454 mask = GDK_BUTTON4_MASK;
1455 break;
1456 case 5:
1457 mask = GDK_BUTTON5_MASK;
1458 break;
1459 default:
1460 mask = 0;
1461 }
1463 switch (event->type) {
1464 case GDK_BUTTON_PRESS:
1465 case GDK_2BUTTON_PRESS:
1466 case GDK_3BUTTON_PRESS:
1467 /* Pick the current item as if the button were not pressed, and
1468 * then process the event.
1469 */
1470 canvas->state = event->state;
1471 pick_current_item (canvas, (GdkEvent *) event);
1472 canvas->state ^= mask;
1473 retval = emit_event (canvas, (GdkEvent *) event);
1474 break;
1476 case GDK_BUTTON_RELEASE:
1477 /* Process the event as if the button were pressed, then repick
1478 * after the button has been released
1479 */
1480 canvas->state = event->state;
1481 retval = emit_event (canvas, (GdkEvent *) event);
1482 event->state ^= mask;
1483 canvas->state = event->state;
1484 pick_current_item (canvas, (GdkEvent *) event);
1485 event->state ^= mask;
1486 break;
1488 default:
1489 g_assert_not_reached ();
1490 }
1492 return retval;
1493 }
1495 /**
1496 * Scroll event handler for the canvas.
1497 *
1498 * \todo FIXME: generate motion events to re-select items.
1499 */
1500 static gint
1501 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1502 {
1503 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1504 }
1506 /**
1507 * Motion event handler for the canvas.
1508 */
1509 static int
1510 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1511 {
1512 SPCanvas *canvas = SP_CANVAS (widget);
1514 if (event->window != SP_CANVAS_WINDOW (canvas))
1515 return FALSE;
1517 if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1518 gint x, y;
1519 gdk_window_get_pointer (widget->window, &x, &y, NULL);
1520 event->x = x;
1521 event->y = y;
1522 }
1524 canvas->state = event->state;
1525 pick_current_item (canvas, (GdkEvent *) event);
1527 return emit_event (canvas, (GdkEvent *) event);
1528 }
1530 static void
1531 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)
1532 {
1533 GtkWidget *widget = GTK_WIDGET (canvas);
1535 SPCanvasBuf buf;
1536 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1537 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1538 } else {
1539 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1540 }
1542 buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1543 buf.rect.x0 = x0;
1544 buf.rect.y0 = y0;
1545 buf.rect.x1 = x1;
1546 buf.rect.y1 = y1;
1547 buf.visible_rect.x0 = draw_x1;
1548 buf.visible_rect.y0 = draw_y1;
1549 buf.visible_rect.x1 = draw_x2;
1550 buf.visible_rect.y1 = draw_y2;
1551 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1552 buf.bg_color = (((color->red & 0xff00) << 8)
1553 | (color->green & 0xff00)
1554 | (color->blue >> 8));
1555 buf.is_empty = true;
1557 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1558 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1559 }
1561 if (buf.is_empty) {
1562 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1563 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1564 canvas->pixmap_gc,
1565 TRUE,
1566 x0 - canvas->x0, y0 - canvas->y0,
1567 x1 - x0, y1 - y0);
1568 } else {
1569 /*
1570 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1571 // Why this must not be done currently:
1572 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1573 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)
1574 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels,
1575 // we need more bufs to paint a given area and as a result it's even a bit slower
1577 cairo_surface_t* cst = cairo_image_surface_create_for_data (
1578 buf.buf,
1579 CAIRO_FORMAT_RGB24, // unpacked, i.e. 32 bits! one byte is unused
1580 x1 - x0, y1 - y0,
1581 buf.buf_rowstride
1582 );
1583 cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1584 cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1585 cairo_paint (ct);
1586 cairo_destroy (ct);
1587 cairo_surface_finish (cst);
1588 cairo_surface_destroy (cst);
1589 */
1591 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1592 canvas->pixmap_gc,
1593 x0 - canvas->x0, y0 - canvas->y0,
1594 x1 - x0, y1 - y0,
1595 GDK_RGB_DITHER_MAX,
1596 buf.buf,
1597 sw * 3,
1598 x0 - canvas->x0, y0 - canvas->y0);
1599 }
1601 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1602 nr_pixelstore_256K_free (buf.buf);
1603 } else {
1604 nr_pixelstore_1M_free (buf.buf);
1605 }
1606 }
1608 /* Paint the given rect, while updating canvas->redraw_aborted and running iterations after each
1609 * buffer; make sure canvas->redraw_aborted never goes past aborted_limit (used for 2-rect
1610 * optimized repaint)
1611 */
1612 static int
1613 sp_canvas_paint_rect_internal (SPCanvas *canvas, NRRectL *rect, NR::ICoord *x_aborted_limit, NR::ICoord *y_aborted_limit)
1614 {
1615 int draw_x1 = rect->x0;
1616 int draw_x2 = rect->x1;
1617 int draw_y1 = rect->y0;
1618 int draw_y2 = rect->y1;
1620 // Here we'll store the time it took to draw the slowest buffer of this paint.
1621 glong slowest_buffer = 0;
1623 // Find the optimal buffer dimensions
1624 int bw = draw_x2 - draw_x1;
1625 int bh = draw_y2 - draw_y1;
1626 if ((bw < 1) || (bh < 1))
1627 return 0;
1629 int sw, sh; // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1630 if (canvas->rendermode != RENDERMODE_OUTLINE) { // use 256K as a compromise to not slow down gradients
1631 /* 256K is the cached buffer and we need 3 channels */
1632 if (bw * bh < 87381) { // 256K/3
1633 // We can go with single buffer
1634 sw = bw;
1635 sh = bh;
1636 } else if (bw <= (16 * 341)) {
1637 // Go with row buffer
1638 sw = bw;
1639 sh = 87381 / bw;
1640 } else if (bh <= (16 * 256)) {
1641 // Go with column buffer
1642 sw = 87381 / bh;
1643 sh = bh;
1644 } else {
1645 sw = 341;
1646 sh = 256;
1647 }
1648 } else { // paths only, so 1M works faster
1649 /* 1M is the cached buffer and we need 3 channels */
1650 if (bw * bh < 349525) { // 1M/3
1651 // We can go with single buffer
1652 sw = bw;
1653 sh = bh;
1654 } else if (bw <= (16 * 682)) {
1655 // Go with row buffer
1656 sw = bw;
1657 sh = 349525 / bw;
1658 } else if (bh <= (16 * 512)) {
1659 // Go with column buffer
1660 sw = 349525 / bh;
1661 sh = bh;
1662 } else {
1663 sw = 682;
1664 sh = 512;
1665 }
1666 }
1668 // Will this paint require more than one buffer?
1669 bool multiple_buffers = (((draw_y2 - draw_y1) > sh) || ((draw_x2 - draw_x1) > sw)); // or two_rects
1671 // remember the counter during this paint
1672 long this_count = canvas->redraw_count;
1674 // Time values to measure each buffer's paint time
1675 GTimeVal tstart, tfinish;
1677 // paint from the corner nearest the mouse pointer
1679 gint x, y;
1680 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1681 NR::Point pw = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1683 bool reverse_x = (pw[NR::X] > ((draw_x2 + draw_x1) / 2));
1684 bool reverse_y = (pw[NR::Y] > ((draw_y2 + draw_y1) / 2));
1686 if ((bw > bh) && (sh > sw)) {
1687 int t = sw; sw = sh; sh = t;
1688 }
1690 // This is the main loop which corresponds to the visible left-to-right, top-to-bottom drawing
1691 // of screen blocks (buffers).
1692 for (int y0 = draw_y1; y0 < draw_y2; y0 += sh) {
1693 int y1 = MIN (y0 + sh, draw_y2);
1694 for (int x0 = draw_x1; x0 < draw_x2; x0 += sw) {
1695 int x1 = MIN (x0 + sw, draw_x2);
1697 int dx0 = x0;
1698 int dx1 = x1;
1699 int dy0 = y0;
1700 int dy1 = y1;
1702 if (reverse_x) {
1703 dx0 = (draw_x2 - (x0 + sw)) + draw_x1;
1704 dx0 = MAX (dx0, draw_x1);
1705 dx1 = (draw_x2 - x0) + draw_x1;
1706 dx1 = MIN (dx1, draw_x2);
1707 }
1708 if (reverse_y) {
1709 dy0 = (draw_y2 - (y0 + sh)) + draw_y1;
1710 dy0 = MAX (dy0, draw_y1);
1711 dy1 = (draw_y2 - y0) + draw_y1;
1712 dy1 = MIN (dy1, draw_y2);
1713 }
1715 // SMOOTH SCROLLING: if we are scrolling, process pending events even before doing any rendering.
1716 // This allows for scrolling smoothly without hiccups. Any accumulated redraws will be made
1717 // when scrolling stops. The scrolling flag is set by sp_canvas_scroll_to for each scroll and zeroed
1718 // here for each redraw, to ensure it never gets stuck.
1720 // OPTIMIZATION IDEA: if drawing is really slow (as measured by canvas->slowest
1721 // buffer), do the same - process some events even before we paint any buffers
1723 if (canvas->is_scrolling) {
1724 while (Gtk::Main::events_pending()) { // process any events
1725 Gtk::Main::iteration(false);
1726 }
1727 canvas->is_scrolling = false;
1728 if (this_count != canvas->redraw_count) { // if there was redraw,
1729 return 1; // interrupt this one
1730 }
1731 }
1733 // Paint one buffer; measure how long it takes.
1734 g_get_current_time (&tstart);
1735 sp_canvas_paint_single_buffer (canvas, dx0, dy0, dx1, dy1, draw_x1, draw_y1, draw_x2, draw_y2, sw);
1736 g_get_current_time (&tfinish);
1738 // Remember the slowest_buffer of this paint.
1739 glong this_buffer = (tfinish.tv_sec - tstart.tv_sec) * 1000000 + (tfinish.tv_usec - tstart.tv_usec);
1740 if (this_buffer > slowest_buffer)
1741 slowest_buffer = this_buffer;
1743 // After each successful buffer, reduce the rect remaining to redraw by what is already redrawn
1744 if (x1 >= draw_x2) {
1745 if (reverse_y) {
1746 if (canvas->redraw_aborted.y1 > dy0) { canvas->redraw_aborted.y1 = dy0; }
1747 } else {
1748 if (canvas->redraw_aborted.y0 < y1) { canvas->redraw_aborted.y0 = y1; }
1749 }
1750 }
1752 if (y1 >= draw_y2) {
1753 if (reverse_x) {
1754 if (canvas->redraw_aborted.x1 > dx0) { canvas->redraw_aborted.x1 = dx0; }
1755 } else {
1756 if (canvas->redraw_aborted.x0 < x1) { canvas->redraw_aborted.x0 = x1; }
1757 }
1758 }
1760 // INTERRUPTIBLE DISPLAY:
1761 // Process events that may have arrived while we were busy drawing;
1762 // only if we're drawing multiple buffers, and only if this one was not very fast,
1763 // and only if we're allowed to interrupt this redraw
1764 bool ok_to_interrupt = (multiple_buffers && this_buffer > 25000);
1765 if (ok_to_interrupt && (canvas->forced_redraw_limit != -1)) {
1766 ok_to_interrupt = (canvas->forced_redraw_count < canvas->forced_redraw_limit);
1767 }
1769 if (ok_to_interrupt) {
1770 // Run at most max_iterations of the main loop; we cannot process ALL events
1771 // here because some things (e.g. rubberband) flood with dirtying events but will
1772 // not redraw themselves
1773 int max_iterations = 10;
1774 int iterations = 0;
1775 while (Gtk::Main::events_pending() && iterations++ < max_iterations) {
1776 Gtk::Main::iteration(false);
1777 // If one of the iterations has redrawn by itself, abort
1778 if (this_count != canvas->redraw_count) {
1779 canvas->slowest_buffer = slowest_buffer;
1780 if (canvas->forced_redraw_limit != -1) {
1781 canvas->forced_redraw_count++;
1782 }
1783 return 1; // interrupted
1784 }
1785 }
1787 // If not aborted so far, check if the events set redraw or update flags;
1788 // if so, force update and abort
1789 if (canvas->need_redraw || canvas->need_update) {
1790 canvas->slowest_buffer = slowest_buffer;
1791 if (canvas->forced_redraw_limit != -1) {
1792 canvas->forced_redraw_count++;
1793 }
1794 do_update (canvas);
1795 return 1; // interrupted
1796 }
1797 }
1798 }
1799 }
1801 // Remember the slowest buffer of this paint in canvas
1802 canvas->slowest_buffer = slowest_buffer;
1804 return 0; // finished
1805 }
1808 /**
1809 * Helper that draws a specific rectangular part of the canvas.
1810 */
1811 static void
1812 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1813 {
1814 g_return_if_fail (!canvas->need_update);
1816 // Monotonously increment the canvas-global counter on each paint. This will let us find out
1817 // when a new paint happened in event processing during this paint, so we can abort it.
1818 canvas->redraw_count++;
1820 NRRectL rect;
1821 rect.x0 = xx0;
1822 rect.x1 = xx1;
1823 rect.y0 = yy0;
1824 rect.y1 = yy1;
1826 // Clip rect-to-draw by the current visible area
1827 rect.x0 = MAX (rect.x0, canvas->x0);
1828 rect.y0 = MAX (rect.y0, canvas->y0);
1829 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1830 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1832 // Clip rect-aborted-last-time by the current visible area
1833 canvas->redraw_aborted.x0 = MAX (canvas->redraw_aborted.x0, canvas->x0);
1834 canvas->redraw_aborted.y0 = MAX (canvas->redraw_aborted.y0, canvas->y0);
1835 canvas->redraw_aborted.x1 = MIN (canvas->redraw_aborted.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1836 canvas->redraw_aborted.y1 = MIN (canvas->redraw_aborted.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1838 if (canvas->redraw_aborted.x0 < canvas->redraw_aborted.x1 && canvas->redraw_aborted.y0 < canvas->redraw_aborted.y1) {
1839 // There was an aborted redraw last time, now we need to redraw BOTH it and the new rect.
1841 // save the old aborted rect in case we decide to paint it separately (see below)
1842 NRRectL aborted = canvas->redraw_aborted;
1844 // calculate the rectangle union of the both rects (the smallest rectangle which covers both)
1845 NRRectL nion;
1846 nr_rect_l_union (&nion, &rect, &aborted);
1848 // subtract one of the rects-to-draw from the other (the smallest rectangle which covers
1849 // all of the first not covered by the second)
1850 NRRectL rect_minus_aborted;
1851 nr_rect_l_subtract (&rect_minus_aborted, &rect, &aborted);
1853 // Initially, the rect to redraw later (in case we're aborted) is the same as the union of both rects
1854 canvas->redraw_aborted = nion;
1856 // calculate areas of the three rects
1857 if ((nr_rect_l_area(&rect_minus_aborted) + nr_rect_l_area(&aborted)) * 1.2 < nr_rect_l_area(&nion)) {
1858 // If the summary area of the two rects is significantly (at least by 20%) less than
1859 // the area of their rectangular union, it makes sense to paint the two rects
1860 // separately instead of painting their union. This gives a significant speedup when,
1861 // for example, your current canvas is almost painted, with only a strip at bottom
1862 // left, and at that moment you abort it by scrolling down which reveals a new strip at
1863 // the top. Straightforward painting of the union of the aborted rect and the new rect
1864 // will have to repaint the entire canvas! By contrast, the optimized approach below
1865 // paints the two narrow strips in order which is much faster.
1867 // find out which rect to draw first - compare them first by y then by x of the top left corners
1868 NRRectL *first;
1869 NRRectL *second;
1870 if (rect.y0 == aborted.y0) {
1871 if (rect.x0 < aborted.x0) {
1872 first = ▭
1873 second = &aborted;
1874 } else {
1875 second = ▭
1876 first = &aborted;
1877 }
1878 } else if (rect.y0 < aborted.y0) {
1879 first = ▭
1880 second = &aborted;
1881 } else {
1882 second = ▭
1883 first = &aborted;
1884 }
1886 NRRectL second_minus_first;
1887 nr_rect_l_subtract (&second_minus_first, second, first);
1889 // paint the first rect;
1890 if (sp_canvas_paint_rect_internal (canvas, first, &(second_minus_first.x0), &(second_minus_first.y0))) {
1891 // aborted!
1892 return;
1893 }
1895 // if not aborted, assign (second rect minus first) as the new redraw_aborted and paint the same
1896 canvas->redraw_aborted = second_minus_first;
1897 if (sp_canvas_paint_rect_internal (canvas, &second_minus_first, NULL, NULL)) {
1898 return; // aborted
1899 }
1901 } else {
1902 // no need for separate drawing, just draw the union as one rect
1903 if (sp_canvas_paint_rect_internal (canvas, &nion, NULL, NULL)) {
1904 return; // aborted
1905 }
1906 }
1907 } else {
1908 // Nothing was aborted last time, just draw the rect we're given
1910 // Initially, the rect to redraw later (in case we're aborted) is the same as the one we're going to draw now.
1911 canvas->redraw_aborted = rect;
1913 if (sp_canvas_paint_rect_internal (canvas, &rect, NULL, NULL)) {
1914 return; // aborted
1915 }
1916 }
1918 // we've had a full unaborted redraw, reset the full redraw counter
1919 if (canvas->forced_redraw_limit != -1) {
1920 canvas->forced_redraw_count = 0;
1921 }
1922 }
1924 /**
1925 * Force a full redraw after a specified number of interrupted redraws
1926 */
1927 void
1928 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1929 g_return_if_fail(canvas != NULL);
1931 canvas->forced_redraw_limit = count;
1932 canvas->forced_redraw_count = 0;
1933 }
1935 /**
1936 * End forced full redraw requests
1937 */
1938 void
1939 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1940 g_return_if_fail(canvas != NULL);
1942 canvas->forced_redraw_limit = -1;
1943 }
1945 /**
1946 * The canvas widget's expose callback.
1947 */
1948 static gint
1949 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1950 {
1951 SPCanvas *canvas = SP_CANVAS (widget);
1953 if (!GTK_WIDGET_DRAWABLE (widget) ||
1954 (event->window != SP_CANVAS_WINDOW (canvas)))
1955 return FALSE;
1957 int n_rects;
1958 GdkRectangle *rects;
1959 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1961 for (int i = 0; i < n_rects; i++) {
1962 NRRectL rect;
1964 rect.x0 = rects[i].x + canvas->x0;
1965 rect.y0 = rects[i].y + canvas->y0;
1966 rect.x1 = rect.x0 + rects[i].width;
1967 rect.y1 = rect.y0 + rects[i].height;
1969 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1970 }
1972 if (n_rects > 0)
1973 g_free (rects);
1975 return FALSE;
1976 }
1978 /**
1979 * The canvas widget's keypress callback.
1980 */
1981 static gint
1982 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1983 {
1984 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1985 }
1987 /**
1988 * Crossing event handler for the canvas.
1989 */
1990 static gint
1991 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1992 {
1993 SPCanvas *canvas = SP_CANVAS (widget);
1995 if (event->window != SP_CANVAS_WINDOW (canvas))
1996 return FALSE;
1998 canvas->state = event->state;
1999 return pick_current_item (canvas, (GdkEvent *) event);
2000 }
2002 /**
2003 * Focus in handler for the canvas.
2004 */
2005 static gint
2006 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2007 {
2008 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2010 SPCanvas *canvas = SP_CANVAS (widget);
2012 if (canvas->focused_item) {
2013 return emit_event (canvas, (GdkEvent *) event);
2014 } else {
2015 return FALSE;
2016 }
2017 }
2019 /**
2020 * Focus out handler for the canvas.
2021 */
2022 static gint
2023 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2024 {
2025 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2027 SPCanvas *canvas = SP_CANVAS (widget);
2029 if (canvas->focused_item)
2030 return emit_event (canvas, (GdkEvent *) event);
2031 else
2032 return FALSE;
2033 }
2035 /**
2036 * Helper that repaints the areas in the canvas that need it.
2037 */
2038 static int
2039 paint (SPCanvas *canvas)
2040 {
2041 if (canvas->need_update) {
2042 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2043 canvas->need_update = FALSE;
2044 }
2046 if (!canvas->need_redraw)
2047 return TRUE;
2049 GtkWidget const *widget = GTK_WIDGET(canvas);
2050 int const canvas_x1 = canvas->x0 + widget->allocation.width;
2051 int const canvas_y1 = canvas->y0 + widget->allocation.height;
2053 bool dirty = false;
2055 int pl = canvas->tRight, pr = canvas->tLeft, pt = canvas->tBottom, pb = canvas->tTop; // start with "inverted" tile rect
2057 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2058 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2060 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2062 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2063 dirty = true;
2064 // make (pl..pr)x(pt..pb) the minimal rect covering all dirtied tiles
2065 if ( i < pl ) pl = i;
2066 if ( i+1 > pr ) pr = i+1;
2067 if ( j < pt ) pt = j;
2068 if ( j+1 > pb ) pb = j+1;
2069 }
2071 canvas->tiles[tile_index] = 0; // undirty this tile
2072 }
2073 }
2075 canvas->need_redraw = FALSE;
2077 if ( dirty ) {
2078 NRRectL topaint;
2079 topaint.x0 = MAX (pl*TILE_SIZE, canvas->x0);
2080 topaint.y0 = MAX (pt*TILE_SIZE, canvas->y0);
2081 topaint.x1 = MIN (pr*TILE_SIZE, canvas_x1);
2082 topaint.y1 = MIN (pb*TILE_SIZE, canvas_y1);
2083 if ((topaint.x0 < topaint.x1) && (topaint.y0 < topaint.y1)) {
2084 sp_canvas_paint_rect (canvas, topaint.x0, topaint.y0, topaint.x1, topaint.y1);
2085 }
2086 }
2088 return TRUE;
2089 }
2091 /**
2092 * Helper that invokes update, paint, and repick on canvas.
2093 */
2094 static int
2095 do_update (SPCanvas *canvas)
2096 {
2097 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2098 return TRUE;
2100 /* Cause the update if necessary */
2101 if (canvas->need_update) {
2102 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2103 canvas->need_update = FALSE;
2104 }
2106 /* Paint if able to */
2107 if (GTK_WIDGET_DRAWABLE (canvas)) {
2108 return paint (canvas);
2109 }
2111 /* Pick new current item */
2112 while (canvas->need_repick) {
2113 canvas->need_repick = FALSE;
2114 pick_current_item (canvas, &canvas->pick_event);
2115 }
2117 return TRUE;
2118 }
2120 /**
2121 * Idle handler for the canvas that deals with pending updates and redraws.
2122 */
2123 static gint
2124 idle_handler (gpointer data)
2125 {
2126 GDK_THREADS_ENTER ();
2128 SPCanvas *canvas = SP_CANVAS (data);
2130 const int ret = do_update (canvas);
2132 if (ret) {
2133 /* Reset idle id */
2134 canvas->idle_id = 0;
2135 }
2137 GDK_THREADS_LEAVE ();
2139 return !ret;
2140 }
2142 /**
2143 * Convenience function to add an idle handler to a canvas.
2144 */
2145 static void
2146 add_idle (SPCanvas *canvas)
2147 {
2148 if (canvas->idle_id != 0)
2149 return;
2151 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2152 }
2154 /**
2155 * Returns the root group of the specified canvas.
2156 */
2157 SPCanvasGroup *
2158 sp_canvas_root (SPCanvas *canvas)
2159 {
2160 g_return_val_if_fail (canvas != NULL, NULL);
2161 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2163 return SP_CANVAS_GROUP (canvas->root);
2164 }
2166 /**
2167 * Scrolls canvas to specific position.
2168 */
2169 void
2170 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2171 {
2172 g_return_if_fail (canvas != NULL);
2173 g_return_if_fail (SP_IS_CANVAS (canvas));
2175 int ix = (int) (cx + 0.5);
2176 int iy = (int) (cy + 0.5);
2177 int dx = ix - canvas->x0;
2178 int dy = iy - canvas->y0;
2180 canvas->dx0 = cx;
2181 canvas->dy0 = cy;
2182 canvas->x0 = ix;
2183 canvas->y0 = iy;
2185 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2187 if (!clear) {
2188 // scrolling without zoom; redraw only the newly exposed areas
2189 if ((dx != 0) || (dy != 0)) {
2190 canvas->is_scrolling = is_scrolling;
2191 if (GTK_WIDGET_REALIZED (canvas)) {
2192 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2193 }
2194 }
2195 } else {
2196 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2197 }
2199 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
2200 SPEventContext *ec = inkscape_active_event_context();
2201 if (SP_IS_3DBOX_CONTEXT (ec)) {
2202 // We could avoid redraw during panning by checking the status of is_scrolling, but this is
2203 // neither faster nor does it get rid of artefacts, so we update PLs unconditionally
2204 SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec);
2205 bc->_vpdrag->updateLines();
2206 }
2207 }
2209 /**
2210 * Updates canvas if necessary.
2211 */
2212 void
2213 sp_canvas_update_now (SPCanvas *canvas)
2214 {
2215 g_return_if_fail (canvas != NULL);
2216 g_return_if_fail (SP_IS_CANVAS (canvas));
2218 if (!(canvas->need_update ||
2219 canvas->need_redraw))
2220 return;
2222 remove_idle (canvas);
2223 do_update (canvas);
2224 }
2226 /**
2227 * Update callback for canvas widget.
2228 */
2229 static void
2230 sp_canvas_request_update (SPCanvas *canvas)
2231 {
2232 canvas->need_update = TRUE;
2233 add_idle (canvas);
2234 }
2236 /**
2237 * Forces redraw of rectangular canvas area.
2238 */
2239 void
2240 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2241 {
2242 NRRectL bbox;
2243 NRRectL visible;
2244 NRRectL clip;
2246 g_return_if_fail (canvas != NULL);
2247 g_return_if_fail (SP_IS_CANVAS (canvas));
2249 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2250 if ((x0 >= x1) || (y0 >= y1)) return;
2252 bbox.x0 = x0;
2253 bbox.y0 = y0;
2254 bbox.x1 = x1;
2255 bbox.y1 = y1;
2257 visible.x0 = canvas->x0;
2258 visible.y0 = canvas->y0;
2259 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2260 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2262 nr_rect_l_intersect (&clip, &bbox, &visible);
2264 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2265 add_idle (canvas);
2266 }
2268 /**
2269 * Sets world coordinates from win and canvas.
2270 */
2271 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2272 {
2273 g_return_if_fail (canvas != NULL);
2274 g_return_if_fail (SP_IS_CANVAS (canvas));
2276 if (worldx) *worldx = canvas->x0 + winx;
2277 if (worldy) *worldy = canvas->y0 + winy;
2278 }
2280 /**
2281 * Sets win coordinates from world and canvas.
2282 */
2283 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2284 {
2285 g_return_if_fail (canvas != NULL);
2286 g_return_if_fail (SP_IS_CANVAS (canvas));
2288 if (winx) *winx = worldx - canvas->x0;
2289 if (winy) *winy = worldy - canvas->y0;
2290 }
2292 /**
2293 * Converts point from win to world coordinates.
2294 */
2295 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2296 {
2297 g_assert (canvas != NULL);
2298 g_assert (SP_IS_CANVAS (canvas));
2300 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2301 }
2303 /**
2304 * Converts point from world to win coordinates.
2305 */
2306 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2307 {
2308 g_assert (canvas != NULL);
2309 g_assert (SP_IS_CANVAS (canvas));
2311 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2312 }
2314 /**
2315 * Returns true if point given in world coordinates is inside window.
2316 */
2317 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2318 {
2319 g_assert( canvas != NULL );
2320 g_assert(SP_IS_CANVAS(canvas));
2322 using NR::X;
2323 using NR::Y;
2324 GtkWidget const &w = *GTK_WIDGET(canvas);
2325 return ( ( canvas->x0 <= world[X] ) &&
2326 ( canvas->y0 <= world[Y] ) &&
2327 ( world[X] < canvas->x0 + w.allocation.width ) &&
2328 ( world[Y] < canvas->y0 + w.allocation.height ) );
2329 }
2331 /**
2332 * Return canvas window coordinates as NR::Rect.
2333 */
2334 NR::Rect SPCanvas::getViewbox() const
2335 {
2336 GtkWidget const *w = GTK_WIDGET(this);
2338 return NR::Rect(NR::Point(dx0, dy0),
2339 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2340 }
2342 inline int sp_canvas_tile_floor(int x)
2343 {
2344 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2345 }
2347 inline int sp_canvas_tile_ceil(int x)
2348 {
2349 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2350 }
2352 /**
2353 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2354 */
2355 void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2356 {
2357 if ( nl >= nr || nt >= nb ) {
2358 if ( canvas->tiles ) g_free(canvas->tiles);
2359 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2360 canvas->tileH=canvas->tileV=0;
2361 canvas->tiles=NULL;
2362 return;
2363 }
2364 int tl=sp_canvas_tile_floor(nl);
2365 int tt=sp_canvas_tile_floor(nt);
2366 int tr=sp_canvas_tile_ceil(nr);
2367 int tb=sp_canvas_tile_ceil(nb);
2369 int nh = tr-tl, nv = tb-tt;
2370 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2371 for (int i=tl; i<tr; i++) {
2372 for (int j=tt; j<tb; j++) {
2373 int ind = (i-tl) + (j-tt)*nh;
2374 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2375 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2376 } else {
2377 ntiles[ind]=0; // newly exposed areas get 0
2378 }
2379 }
2380 }
2381 if ( canvas->tiles ) g_free(canvas->tiles);
2382 canvas->tiles=ntiles;
2383 canvas->tLeft=tl;
2384 canvas->tTop=tt;
2385 canvas->tRight=tr;
2386 canvas->tBottom=tb;
2387 canvas->tileH=nh;
2388 canvas->tileV=nv;
2389 }
2391 /**
2392 * Helper that marks specific canvas rectangle for redraw by dirtying its tiles
2393 */
2394 void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb)
2395 {
2396 if ( nl >= nr || nt >= nb ) {
2397 return;
2398 }
2399 int tl=sp_canvas_tile_floor(nl);
2400 int tt=sp_canvas_tile_floor(nt);
2401 int tr=sp_canvas_tile_ceil(nr);
2402 int tb=sp_canvas_tile_ceil(nb);
2403 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2404 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2405 if ( tr > canvas->tRight ) tr=canvas->tRight;
2406 if ( tt < canvas->tTop ) tt=canvas->tTop;
2407 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2409 canvas->need_redraw = TRUE;
2411 for (int i=tl; i<tr; i++) {
2412 for (int j=tt; j<tb; j++) {
2413 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = 1;
2414 }
2415 }
2416 }
2419 /*
2420 Local Variables:
2421 mode:c++
2422 c-file-style:"stroustrup"
2423 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2424 indent-tabs-mode:nil
2425 fill-column:99
2426 End:
2427 */
2428 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :