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