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>
27 #include <gtk/gtkversion.h>
29 #include <gtkmm.h>
31 #include <helper/sp-marshal.h>
32 #include <display/sp-canvas.h>
33 #include "display-forward.h"
34 #include <libnr/nr-matrix-fns.h>
35 #include <libnr/nr-matrix-ops.h>
36 #include <libnr/nr-convex-hull.h>
37 #include "prefs-utils.h"
38 #include "inkscape.h"
39 #include "sodipodi-ctrlrect.h"
40 #if ENABLE_LCMS
41 #include "color-profile-fns.h"
42 #endif // ENABLE_LCMS
43 #include "display/rendermode.h"
44 #include "libnr/nr-blit.h"
45 #include "display/inkscape-cairo.h"
46 #include "debug/gdk-event-latency-tracker.h"
48 using Inkscape::Debug::GdkEventLatencyTracker;
50 // GTK_CHECK_VERSION returns false on failure
51 #define HAS_GDK_EVENT_REQUEST_MOTIONS GTK_CHECK_VERSION(2, 12, 0)
53 // gtk_check_version returns non-NULL on failure
54 static bool const HAS_BROKEN_MOTION_HINTS =
55 true || gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS;
57 // Define this to visualize the regions to be redrawn
58 //#define DEBUG_REDRAW 1;
60 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
61 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
62 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
63 #define TILE_SIZE 16
65 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
67 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
69 enum {
70 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
71 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
72 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
73 };
75 /**
76 * A group of Items.
77 */
78 struct SPCanvasGroup {
79 SPCanvasItem item;
81 GList *items, *last;
82 };
84 /**
85 * The SPCanvasGroup vtable.
86 */
87 struct SPCanvasGroupClass {
88 SPCanvasItemClass parent_class;
89 };
91 /**
92 * The SPCanvas vtable.
93 */
94 struct SPCanvasClass {
95 GtkWidgetClass parent_class;
96 };
98 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
99 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
101 /* SPCanvasItem */
103 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
106 static void sp_canvas_request_update (SPCanvas *canvas);
108 static void track_latency(GdkEvent const *event);
109 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
110 static void sp_canvas_item_init (SPCanvasItem *item);
111 static void sp_canvas_item_dispose (GObject *object);
112 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
114 static int emit_event (SPCanvas *canvas, GdkEvent *event);
116 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
118 static GtkObjectClass *item_parent_class;
120 /**
121 * Registers the SPCanvasItem class with Glib and returns its type number.
122 */
123 GType
124 sp_canvas_item_get_type (void)
125 {
126 static GType type = 0;
127 if (!type) {
128 static GTypeInfo const info = {
129 sizeof (SPCanvasItemClass),
130 NULL, NULL,
131 (GClassInitFunc) sp_canvas_item_class_init,
132 NULL, NULL,
133 sizeof (SPCanvasItem),
134 0,
135 (GInstanceInitFunc) sp_canvas_item_init,
136 NULL
137 };
138 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
139 }
141 return type;
142 }
144 /**
145 * Initializes the SPCanvasItem vtable and the "event" signal.
146 */
147 static void
148 sp_canvas_item_class_init (SPCanvasItemClass *klass)
149 {
150 GObjectClass *object_class = (GObjectClass *) klass;
152 /* fixme: Derive from GObject */
153 item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
155 item_signals[ITEM_EVENT] = g_signal_new ("event",
156 G_TYPE_FROM_CLASS (klass),
157 G_SIGNAL_RUN_LAST,
158 ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
159 NULL, NULL,
160 sp_marshal_BOOLEAN__POINTER,
161 G_TYPE_BOOLEAN, 1,
162 GDK_TYPE_EVENT);
164 object_class->dispose = sp_canvas_item_dispose;
165 }
167 /**
168 * Callback for initialization of SPCanvasItem.
169 */
170 static void
171 sp_canvas_item_init (SPCanvasItem *item)
172 {
173 item->flags |= SP_CANVAS_ITEM_VISIBLE;
174 item->xform = NR::Matrix(NR::identity());
175 }
177 /**
178 * Constructs new SPCanvasItem on SPCanvasGroup.
179 */
180 SPCanvasItem *
181 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
182 {
183 va_list args;
185 g_return_val_if_fail (parent != NULL, NULL);
186 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
187 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
189 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
191 va_start (args, first_arg_name);
192 sp_canvas_item_construct (item, parent, first_arg_name, args);
193 va_end (args);
195 return item;
196 }
198 /**
199 * Sets up the newly created SPCanvasItem.
200 *
201 * We make it static for encapsulation reasons since it was nowhere used.
202 */
203 static void
204 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
205 {
206 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
207 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
209 item->parent = SP_CANVAS_ITEM (parent);
210 item->canvas = item->parent->canvas;
212 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
214 group_add (SP_CANVAS_GROUP (item->parent), item);
216 sp_canvas_item_request_update (item);
217 }
219 /**
220 * Helper function that requests redraw only if item's visible flag is set.
221 */
222 static void
223 redraw_if_visible (SPCanvasItem *item)
224 {
225 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
226 int x0 = (int)(item->x1);
227 int x1 = (int)(item->x2);
228 int y0 = (int)(item->y1);
229 int y1 = (int)(item->y2);
231 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
232 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
233 }
234 }
235 }
237 /**
238 * Callback that removes item from all referers and destroys it.
239 */
240 static void
241 sp_canvas_item_dispose (GObject *object)
242 {
243 SPCanvasItem *item = SP_CANVAS_ITEM (object);
245 // Hack: if this is a ctrlrect, move it to 0,0;
246 // this redraws only the stroke of the rect to be deleted,
247 // avoiding redraw of the entire area
248 if (SP_IS_CTRLRECT(item)) {
249 SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
250 SP_CTRLRECT(object)->update(item->xform, 0);
251 } else {
252 redraw_if_visible (item);
253 }
254 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
256 if (item == item->canvas->current_item) {
257 item->canvas->current_item = NULL;
258 item->canvas->need_repick = TRUE;
259 }
261 if (item == item->canvas->new_current_item) {
262 item->canvas->new_current_item = NULL;
263 item->canvas->need_repick = TRUE;
264 }
266 if (item == item->canvas->grabbed_item) {
267 item->canvas->grabbed_item = NULL;
268 gdk_pointer_ungrab (GDK_CURRENT_TIME);
269 }
271 if (item == item->canvas->focused_item)
272 item->canvas->focused_item = NULL;
274 if (item->parent) {
275 group_remove (SP_CANVAS_GROUP (item->parent), item);
276 }
278 G_OBJECT_CLASS (item_parent_class)->dispose (object);
279 }
281 /**
282 * Helper function to update item and its children.
283 *
284 * NB! affine is parent2canvas.
285 */
286 static void
287 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
288 {
289 /* Apply the child item's transform */
290 NR::Matrix child_affine = item->xform * affine;
292 /* apply object flags to child flags */
293 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
295 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
296 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
298 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
299 child_flags |= SP_CANVAS_UPDATE_AFFINE;
301 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
302 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
303 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
304 }
306 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
307 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
308 }
310 /**
311 * Helper function to invoke the point method of the item.
312 *
313 * The argument x, y should be in the parent's item-relative coordinate
314 * system. This routine applies the inverse of the item's transform,
315 * maintaining the affine invariant.
316 */
317 static double
318 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
319 {
320 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
321 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
323 return NR_HUGE;
324 }
326 /**
327 * Makes the item's affine transformation matrix be equal to the specified
328 * matrix.
329 *
330 * @item: A canvas item.
331 * @affine: An affine transformation matrix.
332 */
333 void
334 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine)
335 {
336 item->xform = affine;
338 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
339 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
340 if (item->parent != NULL) {
341 sp_canvas_item_request_update (item->parent);
342 } else {
343 sp_canvas_request_update (item->canvas);
344 }
345 }
347 item->canvas->need_repick = TRUE;
348 }
350 /**
351 * Convenience function to reorder items in a group's child list.
352 *
353 * This puts the specified link after the "before" link.
354 */
355 static void
356 put_item_after (GList *link, GList *before)
357 {
358 if (link == before)
359 return;
361 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
363 if (before == NULL) {
364 if (link == parent->items) return;
366 link->prev->next = link->next;
368 if (link->next) {
369 link->next->prev = link->prev;
370 } else {
371 parent->last = link->prev;
372 }
374 link->prev = before;
375 link->next = parent->items;
376 link->next->prev = link;
377 parent->items = link;
378 } else {
379 if ((link == parent->last) && (before == parent->last->prev))
380 return;
382 if (link->next)
383 link->next->prev = link->prev;
385 if (link->prev)
386 link->prev->next = link->next;
387 else {
388 parent->items = link->next;
389 parent->items->prev = NULL;
390 }
392 link->prev = before;
393 link->next = before->next;
395 link->prev->next = link;
397 if (link->next)
398 link->next->prev = link;
399 else
400 parent->last = link;
401 }
402 }
405 /**
406 * Raises the item in its parent's stack by the specified number of positions.
407 *
408 * \param item A canvas item.
409 * \param positions Number of steps to raise the item.
410 *
411 * If the number of positions is greater than the distance to the top of the
412 * stack, then the item is put at the top.
413 */
414 void
415 sp_canvas_item_raise (SPCanvasItem *item, int positions)
416 {
417 g_return_if_fail (item != NULL);
418 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
419 g_return_if_fail (positions >= 0);
421 if (!item->parent || positions == 0)
422 return;
424 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
425 GList *link = g_list_find (parent->items, item);
426 g_assert (link != NULL);
428 GList *before;
429 for (before = link; positions && before; positions--)
430 before = before->next;
432 if (!before)
433 before = parent->last;
435 put_item_after (link, before);
437 redraw_if_visible (item);
438 item->canvas->need_repick = TRUE;
439 }
442 /**
443 * Lowers the item in its parent's stack by the specified number of positions.
444 *
445 * \param item A canvas item.
446 * \param positions Number of steps to lower the item.
447 *
448 * If the number of positions is greater than the distance to the bottom of the
449 * stack, then the item is put at the bottom.
450 **/
451 void
452 sp_canvas_item_lower (SPCanvasItem *item, int positions)
453 {
454 g_return_if_fail (item != NULL);
455 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
456 g_return_if_fail (positions >= 1);
458 if (!item->parent || positions == 0)
459 return;
461 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
462 GList *link = g_list_find (parent->items, item);
463 g_assert (link != NULL);
465 GList *before;
466 if (link->prev)
467 for (before = link->prev; positions && before; positions--)
468 before = before->prev;
469 else
470 before = NULL;
472 put_item_after (link, before);
474 redraw_if_visible (item);
475 item->canvas->need_repick = TRUE;
476 }
478 /**
479 * Sets visible flag on item and requests a redraw.
480 */
481 void
482 sp_canvas_item_show (SPCanvasItem *item)
483 {
484 g_return_if_fail (item != NULL);
485 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
487 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
488 return;
490 item->flags |= SP_CANVAS_ITEM_VISIBLE;
492 int x0 = (int)(item->x1);
493 int x1 = (int)(item->x2);
494 int y0 = (int)(item->y1);
495 int y1 = (int)(item->y2);
497 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
498 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
499 item->canvas->need_repick = TRUE;
500 }
501 }
503 /**
504 * Clears visible flag on item and requests a redraw.
505 */
506 void
507 sp_canvas_item_hide (SPCanvasItem *item)
508 {
509 g_return_if_fail (item != NULL);
510 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
512 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
513 return;
515 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
517 int x0 = (int)(item->x1);
518 int x1 = (int)(item->x2);
519 int y0 = (int)(item->y1);
520 int y1 = (int)(item->y2);
522 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
523 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
524 item->canvas->need_repick = TRUE;
525 }
526 }
528 /**
529 * Grab item under cursor.
530 *
531 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
532 */
533 int
534 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
535 {
536 g_return_val_if_fail (item != NULL, -1);
537 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
538 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
540 if (item->canvas->grabbed_item)
541 return -1;
543 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
544 return -1;
546 if (HAS_BROKEN_MOTION_HINTS) {
547 event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
548 }
550 /* fixme: Top hack (Lauris) */
551 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
552 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
553 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
554 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
555 NULL, cursor, etime);
557 item->canvas->grabbed_item = item;
558 item->canvas->grabbed_event_mask = event_mask;
559 item->canvas->current_item = item; /* So that events go to the grabbed item */
561 return 0;
562 }
564 /**
565 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
566 * mouse.
567 *
568 * \param item A canvas item that holds a grab.
569 * \param etime The timestamp for ungrabbing the mouse.
570 */
571 void
572 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
573 {
574 g_return_if_fail (item != NULL);
575 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
577 if (item->canvas->grabbed_item != item)
578 return;
580 item->canvas->grabbed_item = NULL;
582 gdk_pointer_ungrab (etime);
583 }
585 /**
586 * Returns the product of all transformation matrices from the root item down
587 * to the item.
588 */
589 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
590 {
591 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
593 NR::Matrix affine = NR::identity();
595 while (item) {
596 affine *= item->xform;
597 item = item->parent;
598 }
599 return affine;
600 }
602 /**
603 * Helper that returns true iff item is descendant of parent.
604 */
605 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
606 {
607 while (item) {
608 if (item == parent)
609 return true;
610 item = item->parent;
611 }
613 return false;
614 }
616 /**
617 * Focus canvas, and item under cursor if it is not already focussed.
618 */
619 void
620 sp_canvas_item_grab_focus (SPCanvasItem *item)
621 {
622 g_return_if_fail (item != NULL);
623 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
624 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
626 SPCanvasItem *focused_item = item->canvas->focused_item;
628 if (focused_item) {
629 GdkEvent ev;
630 ev.focus_change.type = GDK_FOCUS_CHANGE;
631 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
632 ev.focus_change.send_event = FALSE;
633 ev.focus_change.in = FALSE;
635 emit_event (item->canvas, &ev);
636 }
638 item->canvas->focused_item = item;
639 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
641 if (focused_item) {
642 GdkEvent ev;
643 ev.focus_change.type = GDK_FOCUS_CHANGE;
644 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
645 ev.focus_change.send_event = FALSE;
646 ev.focus_change.in = TRUE;
648 emit_event (item->canvas, &ev);
649 }
650 }
652 /**
653 * Requests that the canvas queue an update for the specified item.
654 *
655 * To be used only by item implementations.
656 */
657 void
658 sp_canvas_item_request_update (SPCanvasItem *item)
659 {
660 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
661 return;
663 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
665 if (item->parent != NULL) {
666 /* Recurse up the tree */
667 sp_canvas_item_request_update (item->parent);
668 } else {
669 /* Have reached the top of the tree, make sure the update call gets scheduled. */
670 sp_canvas_request_update (item->canvas);
671 }
672 }
674 /**
675 * Returns position of item in group.
676 */
677 gint sp_canvas_item_order (SPCanvasItem * item)
678 {
679 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
680 }
682 /* SPCanvasGroup */
684 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
685 static void sp_canvas_group_init (SPCanvasGroup *group);
686 static void sp_canvas_group_destroy (GtkObject *object);
688 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
689 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
690 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
692 static SPCanvasItemClass *group_parent_class;
694 /**
695 * Registers SPCanvasGroup class with Gtk and returns its type number.
696 */
697 GType sp_canvas_group_get_type(void)
698 {
699 static GType type = 0;
700 if (!type) {
701 GTypeInfo info = {
702 sizeof(SPCanvasGroupClass),
703 0, // base_init
704 0, // base_finalize
705 (GClassInitFunc)sp_canvas_group_class_init,
706 0, // class_finalize
707 0, // class_data
708 sizeof(SPCanvasGroup),
709 0, // n_preallocs
710 (GInstanceInitFunc)sp_canvas_group_init,
711 0 // value_table
712 };
713 type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
714 }
715 return type;
716 }
718 /**
719 * Class initialization function for SPCanvasGroupClass
720 */
721 static void
722 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
723 {
724 GtkObjectClass *object_class = (GtkObjectClass *) klass;
725 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
727 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
729 object_class->destroy = sp_canvas_group_destroy;
731 item_class->update = sp_canvas_group_update;
732 item_class->render = sp_canvas_group_render;
733 item_class->point = sp_canvas_group_point;
734 }
736 /**
737 * Callback. Empty.
738 */
739 static void
740 sp_canvas_group_init (SPCanvasGroup */*group*/)
741 {
742 /* Nothing here */
743 }
745 /**
746 * Callback that destroys all items in group and calls group's virtual
747 * destroy() function.
748 */
749 static void
750 sp_canvas_group_destroy (GtkObject *object)
751 {
752 g_return_if_fail (object != NULL);
753 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
755 SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
757 GList *list = group->items;
758 while (list) {
759 SPCanvasItem *child = (SPCanvasItem *)list->data;
760 list = list->next;
762 gtk_object_destroy (GTK_OBJECT (child));
763 }
765 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
766 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
767 }
769 /**
770 * Update handler for canvas groups
771 */
772 static void
773 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
774 {
775 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
776 NR::ConvexHull corners(NR::Point(0, 0));
777 bool empty=true;
779 for (GList *list = group->items; list; list = list->next) {
780 SPCanvasItem *i = (SPCanvasItem *)list->data;
782 sp_canvas_item_invoke_update (i, affine, flags);
784 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
785 if (empty) {
786 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
787 empty = false;
788 } else {
789 corners.add(NR::Point(i->x1, i->y1));
790 }
791 corners.add(NR::Point(i->x2, i->y2));
792 }
793 }
795 NR::Maybe<NR::Rect> const bounds = corners.bounds();
796 if (bounds) {
797 item->x1 = bounds->min()[NR::X];
798 item->y1 = bounds->min()[NR::Y];
799 item->x2 = bounds->max()[NR::X];
800 item->y2 = bounds->max()[NR::Y];
801 } else {
802 // FIXME ?
803 item->x1 = item->x2 = item->y1 = item->y2 = 0;
804 }
805 }
807 /**
808 * Point handler for canvas groups.
809 */
810 static double
811 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
812 {
813 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
814 double const x = p[NR::X];
815 double const y = p[NR::Y];
816 int x1 = (int)(x - item->canvas->close_enough);
817 int y1 = (int)(y - item->canvas->close_enough);
818 int x2 = (int)(x + item->canvas->close_enough);
819 int y2 = (int)(y + item->canvas->close_enough);
821 double best = 0.0;
822 *actual_item = NULL;
824 double dist = 0.0;
826 for (GList *list = group->items; list; list = list->next) {
827 SPCanvasItem *child = (SPCanvasItem *)list->data;
829 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
830 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
832 int has_point;
833 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
834 dist = sp_canvas_item_invoke_point (child, p, &point_item);
835 has_point = TRUE;
836 } else
837 has_point = FALSE;
839 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
840 best = dist;
841 *actual_item = point_item;
842 }
843 }
844 }
846 return best;
847 }
849 /**
850 * Renders all visible canvas group items in buf rectangle.
851 */
852 static void
853 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
854 {
855 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
857 for (GList *list = group->items; list; list = list->next) {
858 SPCanvasItem *child = (SPCanvasItem *)list->data;
859 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
860 if ((child->x1 < buf->rect.x1) &&
861 (child->y1 < buf->rect.y1) &&
862 (child->x2 > buf->rect.x0) &&
863 (child->y2 > buf->rect.y0)) {
864 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
865 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
866 }
867 }
868 }
869 }
871 /**
872 * Adds an item to a canvas group.
873 */
874 static void
875 group_add (SPCanvasGroup *group, SPCanvasItem *item)
876 {
877 gtk_object_ref (GTK_OBJECT (item));
878 gtk_object_sink (GTK_OBJECT (item));
880 if (!group->items) {
881 group->items = g_list_append (group->items, item);
882 group->last = group->items;
883 } else {
884 group->last = g_list_append (group->last, item)->next;
885 }
887 sp_canvas_item_request_update (item);
888 }
890 /**
891 * Removes an item from a canvas group
892 */
893 static void
894 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
895 {
896 g_return_if_fail (group != NULL);
897 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
898 g_return_if_fail (item != NULL);
900 for (GList *children = group->items; children; children = children->next) {
901 if (children->data == item) {
903 /* Unparent the child */
905 item->parent = NULL;
906 gtk_object_unref (GTK_OBJECT (item));
908 /* Remove it from the list */
910 if (children == group->last) group->last = children->prev;
912 group->items = g_list_remove_link (group->items, children);
913 g_list_free (children);
914 break;
915 }
916 }
917 }
919 /* SPCanvas */
921 static void sp_canvas_class_init (SPCanvasClass *klass);
922 static void sp_canvas_init (SPCanvas *canvas);
923 static void sp_canvas_destroy (GtkObject *object);
925 static void sp_canvas_realize (GtkWidget *widget);
926 static void sp_canvas_unrealize (GtkWidget *widget);
928 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
929 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
931 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
932 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
933 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
934 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
935 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
936 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
937 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
938 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
940 static GtkWidgetClass *canvas_parent_class;
942 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
943 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
944 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
945 static int do_update (SPCanvas *canvas);
947 /**
948 * Registers the SPCanvas class if necessary, and returns the type ID
949 * associated to it.
950 *
951 * \return The type ID of the SPCanvas class.
952 **/
953 GType sp_canvas_get_type(void)
954 {
955 static GType type = 0;
956 if (!type) {
957 GTypeInfo info = {
958 sizeof(SPCanvasClass),
959 0, // base_init
960 0, // base_finalize
961 (GClassInitFunc)sp_canvas_class_init,
962 0, // class_finalize
963 0, // class_data
964 sizeof(SPCanvas),
965 0, // n_preallocs
966 (GInstanceInitFunc)sp_canvas_init,
967 0 // value_table
968 };
969 type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
970 }
971 return type;
972 }
974 /**
975 * Class initialization function for SPCanvasClass.
976 */
977 static void
978 sp_canvas_class_init (SPCanvasClass *klass)
979 {
980 GtkObjectClass *object_class = (GtkObjectClass *) klass;
981 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
983 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
985 object_class->destroy = sp_canvas_destroy;
987 widget_class->realize = sp_canvas_realize;
988 widget_class->unrealize = sp_canvas_unrealize;
989 widget_class->size_request = sp_canvas_size_request;
990 widget_class->size_allocate = sp_canvas_size_allocate;
991 widget_class->button_press_event = sp_canvas_button;
992 widget_class->button_release_event = sp_canvas_button;
993 widget_class->motion_notify_event = sp_canvas_motion;
994 widget_class->scroll_event = sp_canvas_scroll;
995 widget_class->expose_event = sp_canvas_expose;
996 widget_class->key_press_event = sp_canvas_key;
997 widget_class->key_release_event = sp_canvas_key;
998 widget_class->enter_notify_event = sp_canvas_crossing;
999 widget_class->leave_notify_event = sp_canvas_crossing;
1000 widget_class->focus_in_event = sp_canvas_focus_in;
1001 widget_class->focus_out_event = sp_canvas_focus_out;
1002 }
1004 /**
1005 * Callback: object initialization for SPCanvas.
1006 */
1007 static void
1008 sp_canvas_init (SPCanvas *canvas)
1009 {
1010 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1011 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1012 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1014 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1015 canvas->pick_event.crossing.x = 0;
1016 canvas->pick_event.crossing.y = 0;
1018 /* Create the root item as a special case */
1019 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1020 canvas->root->canvas = canvas;
1022 gtk_object_ref (GTK_OBJECT (canvas->root));
1023 gtk_object_sink (GTK_OBJECT (canvas->root));
1025 canvas->need_repick = TRUE;
1027 // See comment at in sp-canvas.h.
1028 canvas->gen_all_enter_events = false;
1030 canvas->tiles=NULL;
1031 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1032 canvas->tileH=canvas->tileV=0;
1034 canvas->forced_redraw_count = 0;
1035 canvas->forced_redraw_limit = -1;
1037 #if ENABLE_LCMS
1038 canvas->enable_cms_display_adj = false;
1039 canvas->cms_key = new Glib::ustring("");
1040 #endif // ENABLE_LCMS
1042 canvas->is_scrolling = false;
1044 }
1046 /**
1047 * Convenience function to remove the idle handler of a canvas.
1048 */
1049 static void
1050 remove_idle (SPCanvas *canvas)
1051 {
1052 if (canvas->idle_id) {
1053 gtk_idle_remove (canvas->idle_id);
1054 canvas->idle_id = 0;
1055 }
1056 }
1058 /*
1059 * Removes the transient state of the canvas (idle handler, grabs).
1060 */
1061 static void
1062 shutdown_transients (SPCanvas *canvas)
1063 {
1064 /* We turn off the need_redraw flag, since if the canvas is mapped again
1065 * it will request a redraw anyways. We do not turn off the need_update
1066 * flag, though, because updates are not queued when the canvas remaps
1067 * itself.
1068 */
1069 if (canvas->need_redraw) {
1070 canvas->need_redraw = FALSE;
1071 }
1072 if ( canvas->tiles ) g_free(canvas->tiles);
1073 canvas->tiles=NULL;
1074 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1075 canvas->tileH=canvas->tileV=0;
1077 if (canvas->grabbed_item) {
1078 canvas->grabbed_item = NULL;
1079 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1080 }
1082 remove_idle (canvas);
1083 }
1085 /**
1086 * Destroy handler for SPCanvas.
1087 */
1088 static void
1089 sp_canvas_destroy (GtkObject *object)
1090 {
1091 SPCanvas *canvas = SP_CANVAS (object);
1093 if (canvas->root) {
1094 gtk_object_unref (GTK_OBJECT (canvas->root));
1095 canvas->root = NULL;
1096 }
1098 shutdown_transients (canvas);
1100 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1101 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1102 }
1104 static void track_latency(GdkEvent const *event) {
1105 GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1106 boost::optional<double> latency = tracker.process(event);
1107 if (latency && *latency > 2.0) {
1108 g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1109 }
1110 }
1112 /**
1113 * Returns new canvas as widget.
1114 */
1115 GtkWidget *
1116 sp_canvas_new_aa (void)
1117 {
1118 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1120 return (GtkWidget *) canvas;
1121 }
1123 /**
1124 * The canvas widget's realize callback.
1125 */
1126 static void
1127 sp_canvas_realize (GtkWidget *widget)
1128 {
1129 SPCanvas *canvas = SP_CANVAS (widget);
1131 GdkWindowAttr attributes;
1132 attributes.window_type = GDK_WINDOW_CHILD;
1133 attributes.x = widget->allocation.x;
1134 attributes.y = widget->allocation.y;
1135 attributes.width = widget->allocation.width;
1136 attributes.height = widget->allocation.height;
1137 attributes.wclass = GDK_INPUT_OUTPUT;
1138 attributes.visual = gdk_rgb_get_visual ();
1139 attributes.colormap = gdk_rgb_get_cmap ();
1140 attributes.event_mask = (gtk_widget_get_events (widget) |
1141 GDK_EXPOSURE_MASK |
1142 GDK_BUTTON_PRESS_MASK |
1143 GDK_BUTTON_RELEASE_MASK |
1144 GDK_POINTER_MOTION_MASK |
1145 ( HAS_BROKEN_MOTION_HINTS ?
1146 0 : GDK_POINTER_MOTION_HINT_MASK ) |
1147 GDK_PROXIMITY_IN_MASK |
1148 GDK_PROXIMITY_OUT_MASK |
1149 GDK_KEY_PRESS_MASK |
1150 GDK_KEY_RELEASE_MASK |
1151 GDK_ENTER_NOTIFY_MASK |
1152 GDK_LEAVE_NOTIFY_MASK |
1153 GDK_FOCUS_CHANGE_MASK);
1154 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1156 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1157 gdk_window_set_user_data (widget->window, widget);
1159 if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1160 gtk_widget_set_events(widget, attributes.event_mask);
1162 widget->style = gtk_style_attach (widget->style, widget->window);
1164 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1166 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1167 }
1169 /**
1170 * The canvas widget's unrealize callback.
1171 */
1172 static void
1173 sp_canvas_unrealize (GtkWidget *widget)
1174 {
1175 SPCanvas *canvas = SP_CANVAS (widget);
1177 canvas->current_item = NULL;
1178 canvas->grabbed_item = NULL;
1179 canvas->focused_item = NULL;
1181 shutdown_transients (canvas);
1183 gdk_gc_destroy (canvas->pixmap_gc);
1184 canvas->pixmap_gc = NULL;
1186 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1187 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1188 }
1190 /**
1191 * The canvas widget's size_request callback.
1192 */
1193 static void
1194 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1195 {
1196 static_cast<void>(SP_CANVAS (widget));
1198 req->width = 256;
1199 req->height = 256;
1200 }
1202 /**
1203 * The canvas widget's size_allocate callback.
1204 */
1205 static void
1206 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1207 {
1208 SPCanvas *canvas = SP_CANVAS (widget);
1210 /* Schedule redraw of new region */
1211 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1212 if (allocation->width > widget->allocation.width) {
1213 sp_canvas_request_redraw (canvas,
1214 canvas->x0 + widget->allocation.width,
1215 0,
1216 canvas->x0 + allocation->width,
1217 canvas->y0 + allocation->height);
1218 }
1219 if (allocation->height > widget->allocation.height) {
1220 sp_canvas_request_redraw (canvas,
1221 0,
1222 canvas->y0 + widget->allocation.height,
1223 canvas->x0 + allocation->width,
1224 canvas->y0 + allocation->height);
1225 }
1227 widget->allocation = *allocation;
1229 if (GTK_WIDGET_REALIZED (widget)) {
1230 gdk_window_move_resize (widget->window,
1231 widget->allocation.x, widget->allocation.y,
1232 widget->allocation.width, widget->allocation.height);
1233 }
1234 }
1236 /**
1237 * Helper that emits an event for an item in the canvas, be it the current
1238 * item, grabbed item, or focused item, as appropriate.
1239 */
1240 static int
1241 emit_event (SPCanvas *canvas, GdkEvent *event)
1242 {
1243 guint mask;
1245 if (canvas->grabbed_item) {
1246 switch (event->type) {
1247 case GDK_ENTER_NOTIFY:
1248 mask = GDK_ENTER_NOTIFY_MASK;
1249 break;
1250 case GDK_LEAVE_NOTIFY:
1251 mask = GDK_LEAVE_NOTIFY_MASK;
1252 break;
1253 case GDK_MOTION_NOTIFY:
1254 mask = GDK_POINTER_MOTION_MASK;
1255 break;
1256 case GDK_BUTTON_PRESS:
1257 case GDK_2BUTTON_PRESS:
1258 case GDK_3BUTTON_PRESS:
1259 mask = GDK_BUTTON_PRESS_MASK;
1260 break;
1261 case GDK_BUTTON_RELEASE:
1262 mask = GDK_BUTTON_RELEASE_MASK;
1263 break;
1264 case GDK_KEY_PRESS:
1265 mask = GDK_KEY_PRESS_MASK;
1266 break;
1267 case GDK_KEY_RELEASE:
1268 mask = GDK_KEY_RELEASE_MASK;
1269 break;
1270 case GDK_SCROLL:
1271 mask = GDK_SCROLL;
1272 break;
1273 default:
1274 mask = 0;
1275 break;
1276 }
1278 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1279 }
1281 /* Convert to world coordinates -- we have two cases because of diferent
1282 * offsets of the fields in the event structures.
1283 */
1285 GdkEvent ev = *event;
1287 switch (ev.type) {
1288 case GDK_ENTER_NOTIFY:
1289 case GDK_LEAVE_NOTIFY:
1290 ev.crossing.x += canvas->x0;
1291 ev.crossing.y += canvas->y0;
1292 break;
1293 case GDK_MOTION_NOTIFY:
1294 case GDK_BUTTON_PRESS:
1295 case GDK_2BUTTON_PRESS:
1296 case GDK_3BUTTON_PRESS:
1297 case GDK_BUTTON_RELEASE:
1298 ev.motion.x += canvas->x0;
1299 ev.motion.y += canvas->y0;
1300 break;
1301 default:
1302 break;
1303 }
1305 /* Choose where we send the event */
1307 /* canvas->current_item becomes NULL in some cases under Win32
1308 ** (e.g. if the pointer leaves the window). So this is a hack that
1309 ** Lauris applied to SP to get around the problem.
1310 */
1311 SPCanvasItem* item = NULL;
1312 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1313 item = canvas->grabbed_item;
1314 } else {
1315 item = canvas->current_item;
1316 }
1318 if (canvas->focused_item &&
1319 ((event->type == GDK_KEY_PRESS) ||
1320 (event->type == GDK_KEY_RELEASE) ||
1321 (event->type == GDK_FOCUS_CHANGE))) {
1322 item = canvas->focused_item;
1323 }
1325 /* The event is propagated up the hierarchy (for if someone connected to
1326 * a group instead of a leaf event), and emission is stopped if a
1327 * handler returns TRUE, just like for GtkWidget events.
1328 */
1330 gint finished = FALSE;
1332 while (item && !finished) {
1333 gtk_object_ref (GTK_OBJECT (item));
1334 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1335 SPCanvasItem *parent = item->parent;
1336 gtk_object_unref (GTK_OBJECT (item));
1337 item = parent;
1338 }
1340 return finished;
1341 }
1343 /**
1344 * Helper that re-picks the current item in the canvas, based on the event's
1345 * coordinates and emits enter/leave events for items as appropriate.
1346 */
1347 static int
1348 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1349 {
1350 int button_down = 0;
1351 double x, y;
1353 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1354 return FALSE;
1356 int retval = FALSE;
1358 if (canvas->gen_all_enter_events == false) {
1359 // If a button is down, we'll perform enter and leave events on the
1360 // current item, but not enter on any other item. This is more or
1361 // less like X pointer grabbing for canvas items.
1362 //
1363 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1364 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1366 if (!button_down) canvas->left_grabbed_item = FALSE;
1367 }
1369 /* Save the event in the canvas. This is used to synthesize enter and
1370 * leave events in case the current item changes. It is also used to
1371 * re-pick the current item if the current one gets deleted. Also,
1372 * synthesize an enter event.
1373 */
1374 if (event != &canvas->pick_event) {
1375 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1376 /* these fields have the same offsets in both types of events */
1378 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1379 canvas->pick_event.crossing.window = event->motion.window;
1380 canvas->pick_event.crossing.send_event = event->motion.send_event;
1381 canvas->pick_event.crossing.subwindow = NULL;
1382 canvas->pick_event.crossing.x = event->motion.x;
1383 canvas->pick_event.crossing.y = event->motion.y;
1384 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1385 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1386 canvas->pick_event.crossing.focus = FALSE;
1387 canvas->pick_event.crossing.state = event->motion.state;
1389 /* these fields don't have the same offsets in both types of events */
1391 if (event->type == GDK_MOTION_NOTIFY) {
1392 canvas->pick_event.crossing.x_root = event->motion.x_root;
1393 canvas->pick_event.crossing.y_root = event->motion.y_root;
1394 } else {
1395 canvas->pick_event.crossing.x_root = event->button.x_root;
1396 canvas->pick_event.crossing.y_root = event->button.y_root;
1397 }
1398 } else {
1399 canvas->pick_event = *event;
1400 }
1401 }
1403 /* Don't do anything else if this is a recursive call */
1404 if (canvas->in_repick) return retval;
1406 /* LeaveNotify means that there is no current item, so we don't look for one */
1407 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1408 /* these fields don't have the same offsets in both types of events */
1410 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1411 x = canvas->pick_event.crossing.x;
1412 y = canvas->pick_event.crossing.y;
1413 } else {
1414 x = canvas->pick_event.motion.x;
1415 y = canvas->pick_event.motion.y;
1416 }
1418 /* world coords */
1419 x += canvas->x0;
1420 y += canvas->y0;
1422 /* find the closest item */
1423 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1424 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1425 } else {
1426 canvas->new_current_item = NULL;
1427 }
1428 } else {
1429 canvas->new_current_item = NULL;
1430 }
1432 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1433 return retval; /* current item did not change */
1434 }
1436 /* Synthesize events for old and new current items */
1438 if ((canvas->new_current_item != canvas->current_item)
1439 && (canvas->current_item != NULL)
1440 && !canvas->left_grabbed_item) {
1441 GdkEvent new_event;
1442 SPCanvasItem *item;
1444 item = canvas->current_item;
1446 new_event = canvas->pick_event;
1447 new_event.type = GDK_LEAVE_NOTIFY;
1449 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1450 new_event.crossing.subwindow = NULL;
1451 canvas->in_repick = TRUE;
1452 retval = emit_event (canvas, &new_event);
1453 canvas->in_repick = FALSE;
1454 }
1456 if (canvas->gen_all_enter_events == false) {
1457 // new_current_item may have been set to NULL during the call to
1458 // emit_event() above
1459 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1460 canvas->left_grabbed_item = TRUE;
1461 return retval;
1462 }
1463 }
1465 /* Handle the rest of cases */
1467 canvas->left_grabbed_item = FALSE;
1468 canvas->current_item = canvas->new_current_item;
1470 if (canvas->current_item != NULL) {
1471 GdkEvent new_event;
1473 new_event = canvas->pick_event;
1474 new_event.type = GDK_ENTER_NOTIFY;
1475 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1476 new_event.crossing.subwindow = NULL;
1477 retval = emit_event (canvas, &new_event);
1478 }
1480 return retval;
1481 }
1483 /**
1484 * Button event handler for the canvas.
1485 */
1486 static gint
1487 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1488 {
1489 SPCanvas *canvas = SP_CANVAS (widget);
1491 int retval = FALSE;
1493 /* dispatch normally regardless of the event's window if an item has
1494 has a pointer grab in effect */
1495 if (!canvas->grabbed_item &&
1496 event->window != SP_CANVAS_WINDOW (canvas))
1497 return retval;
1499 int mask;
1500 switch (event->button) {
1501 case 1:
1502 mask = GDK_BUTTON1_MASK;
1503 break;
1504 case 2:
1505 mask = GDK_BUTTON2_MASK;
1506 break;
1507 case 3:
1508 mask = GDK_BUTTON3_MASK;
1509 break;
1510 case 4:
1511 mask = GDK_BUTTON4_MASK;
1512 break;
1513 case 5:
1514 mask = GDK_BUTTON5_MASK;
1515 break;
1516 default:
1517 mask = 0;
1518 }
1520 switch (event->type) {
1521 case GDK_BUTTON_PRESS:
1522 case GDK_2BUTTON_PRESS:
1523 case GDK_3BUTTON_PRESS:
1524 /* Pick the current item as if the button were not pressed, and
1525 * then process the event.
1526 */
1527 canvas->state = event->state;
1528 pick_current_item (canvas, (GdkEvent *) event);
1529 canvas->state ^= mask;
1530 retval = emit_event (canvas, (GdkEvent *) event);
1531 break;
1533 case GDK_BUTTON_RELEASE:
1534 /* Process the event as if the button were pressed, then repick
1535 * after the button has been released
1536 */
1537 canvas->state = event->state;
1538 retval = emit_event (canvas, (GdkEvent *) event);
1539 event->state ^= mask;
1540 canvas->state = event->state;
1541 pick_current_item (canvas, (GdkEvent *) event);
1542 event->state ^= mask;
1543 break;
1545 default:
1546 g_assert_not_reached ();
1547 }
1549 return retval;
1550 }
1552 /**
1553 * Scroll event handler for the canvas.
1554 *
1555 * \todo FIXME: generate motion events to re-select items.
1556 */
1557 static gint
1558 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1559 {
1560 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1561 }
1563 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1564 gdk_window_get_pointer(w, NULL, NULL, NULL);
1565 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1566 gdk_event_request_motions(event);
1567 #endif
1568 }
1570 /**
1571 * Motion event handler for the canvas.
1572 */
1573 static int
1574 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1575 {
1576 int status;
1577 SPCanvas *canvas = SP_CANVAS (widget);
1579 track_latency((GdkEvent *)event);
1581 if (event->window != SP_CANVAS_WINDOW (canvas))
1582 return FALSE;
1584 if (canvas->pixmap_gc == NULL) // canvas being deleted
1585 return FALSE;
1587 canvas->state = event->state;
1588 pick_current_item (canvas, (GdkEvent *) event);
1590 status = emit_event (canvas, (GdkEvent *) event);
1592 if (event->is_hint) {
1593 request_motions(widget->window, event);
1594 }
1596 return status;
1597 }
1599 static void
1600 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)
1601 {
1602 GtkWidget *widget = GTK_WIDGET (canvas);
1604 SPCanvasBuf buf;
1605 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1606 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1607 } else {
1608 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1609 }
1611 // Mark the region clean
1612 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1614 buf.buf_rowstride = sw * 4;
1615 buf.rect.x0 = x0;
1616 buf.rect.y0 = y0;
1617 buf.rect.x1 = x1;
1618 buf.rect.y1 = y1;
1619 buf.visible_rect.x0 = draw_x1;
1620 buf.visible_rect.y0 = draw_y1;
1621 buf.visible_rect.x1 = draw_x2;
1622 buf.visible_rect.y1 = draw_y2;
1623 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1624 buf.bg_color = (((color->red & 0xff00) << 8)
1625 | (color->green & 0xff00)
1626 | (color->blue >> 8));
1627 buf.is_empty = true;
1629 buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1631 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1632 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1633 }
1635 #if ENABLE_LCMS
1636 cmsHTRANSFORM transf = 0;
1637 long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1638 if ( fromDisplay ) {
1639 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1640 } else {
1641 transf = Inkscape::colorprofile_get_display_transform();
1642 }
1643 #endif // ENABLE_LCMS
1645 if (buf.is_empty) {
1646 #if ENABLE_LCMS
1647 if ( transf && canvas->enable_cms_display_adj ) {
1648 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1649 }
1650 #endif // ENABLE_LCMS
1651 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1652 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1653 canvas->pixmap_gc,
1654 TRUE,
1655 x0 - canvas->x0, y0 - canvas->y0,
1656 x1 - x0, y1 - y0);
1657 } else {
1659 #if ENABLE_LCMS
1660 if ( transf && canvas->enable_cms_display_adj ) {
1661 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1662 guchar* p = buf.buf + (sw * 3) * yy;
1663 cmsDoTransform( transf, p, p, (x1 - x0) );
1664 }
1665 }
1666 #endif // ENABLE_LCMS
1668 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1669 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1670 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1671 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1672 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1673 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1675 ///#define CANVAS_OUTPUT_VIA_CAIRO
1677 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1679 buf.cst = cairo_image_surface_create_for_data (
1680 buf.buf,
1681 CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
1682 x1 - x0, y1 - y0,
1683 buf.buf_rowstride
1684 );
1685 cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1686 cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1687 cairo_paint (window_ct);
1688 cairo_destroy (window_ct);
1689 cairo_surface_finish (buf.cst);
1690 cairo_surface_destroy (buf.cst);
1692 #else
1694 NRPixBlock b3;
1695 nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1697 NRPixBlock b4;
1698 nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1699 buf.buf,
1700 buf.buf_rowstride,
1701 FALSE, FALSE);
1703 // this does the 32->24 squishing, using an assembler routine:
1704 nr_blit_pixblock_pixblock (&b3, &b4);
1706 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1707 canvas->pixmap_gc,
1708 x0 - canvas->x0, y0 - canvas->y0,
1709 x1 - x0, y1 - y0,
1710 GDK_RGB_DITHER_MAX,
1711 NR_PIXBLOCK_PX(&b3),
1712 sw * 3,
1713 x0 - canvas->x0, y0 - canvas->y0);
1715 nr_pixblock_release (&b3);
1716 nr_pixblock_release (&b4);
1717 #endif
1718 }
1720 cairo_surface_t *cst = cairo_get_target(buf.ct);
1721 cairo_destroy (buf.ct);
1722 cairo_surface_finish (cst);
1723 cairo_surface_destroy (cst);
1725 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1726 nr_pixelstore_256K_free (buf.buf);
1727 } else {
1728 nr_pixelstore_1M_free (buf.buf);
1729 }
1730 }
1732 struct PaintRectSetup {
1733 SPCanvas* canvas;
1734 NRRectL big_rect;
1735 GTimeVal start_time;
1736 int max_pixels;
1737 NR::Point mouse_loc;
1738 };
1740 /**
1741 * Paint the given rect, recursively subdividing the region until it is the size of a single
1742 * buffer.
1743 *
1744 * @return true if the drawing completes
1745 */
1746 static int
1747 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1748 {
1749 GTimeVal now;
1750 g_get_current_time (&now);
1752 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1753 + (now.tv_usec - setup->start_time.tv_usec);
1755 // Allow only very fast buffers to be run together;
1756 // as soon as the total redraw time exceeds 1ms, cancel;
1757 // this returns control to the idle loop and allows Inkscape to process user input
1758 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1759 // it will get back and finish painting what remains to paint.
1760 if (elapsed > 1000) {
1762 // Interrupting redraw isn't always good.
1763 // For example, when you drag one node of a big path, only the buffer containing
1764 // the mouse cursor will be redrawn again and again, and the rest of the path
1765 // will remain stale because Inkscape never has enough idle time to redraw all
1766 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1767 // If this limit is set, and if we have aborted redraw more times than is allowed,
1768 // interrupting is blocked and we're forced to redraw full screen once
1769 // (after which we can again interrupt forced_redraw_limit times).
1770 if (setup->canvas->forced_redraw_limit < 0 ||
1771 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1773 if (setup->canvas->forced_redraw_limit != -1) {
1774 setup->canvas->forced_redraw_count++;
1775 }
1777 return false;
1778 }
1779 }
1781 // Find the optimal buffer dimensions
1782 int bw = this_rect.x1 - this_rect.x0;
1783 int bh = this_rect.y1 - this_rect.y0;
1784 if ((bw < 1) || (bh < 1))
1785 return 0;
1787 if (bw * bh < setup->max_pixels) {
1788 // We are small enough
1789 sp_canvas_paint_single_buffer (setup->canvas,
1790 this_rect.x0, this_rect.y0,
1791 this_rect.x1, this_rect.y1,
1792 setup->big_rect.x0, setup->big_rect.y0,
1793 setup->big_rect.x1, setup->big_rect.y1, bw);
1794 return 1;
1795 }
1797 NRRectL lo = this_rect;
1798 NRRectL hi = this_rect;
1800 /*
1801 This test determines the redraw strategy:
1803 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1804 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1805 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1806 and seems to be faster for drawings with many smaller objects at zoom-out.
1808 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1809 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1810 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1811 faster.
1813 The default for now is the strips mode.
1814 */
1815 if (bw < bh || bh < 2 * TILE_SIZE) {
1816 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1817 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1818 // Make sure that mid lies on a tile boundary
1819 mid = (mid / TILE_SIZE) * TILE_SIZE;
1821 lo.x1 = mid;
1822 hi.x0 = mid;
1824 if (setup->mouse_loc[NR::X] < mid) {
1825 // Always paint towards the mouse first
1826 return sp_canvas_paint_rect_internal(setup, lo)
1827 && sp_canvas_paint_rect_internal(setup, hi);
1828 } else {
1829 return sp_canvas_paint_rect_internal(setup, hi)
1830 && sp_canvas_paint_rect_internal(setup, lo);
1831 }
1832 } else {
1833 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1834 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1835 // Make sure that mid lies on a tile boundary
1836 mid = (mid / TILE_SIZE) * TILE_SIZE;
1838 lo.y1 = mid;
1839 hi.y0 = mid;
1841 if (setup->mouse_loc[NR::Y] < mid) {
1842 // Always paint towards the mouse first
1843 return sp_canvas_paint_rect_internal(setup, lo)
1844 && sp_canvas_paint_rect_internal(setup, hi);
1845 } else {
1846 return sp_canvas_paint_rect_internal(setup, hi)
1847 && sp_canvas_paint_rect_internal(setup, lo);
1848 }
1849 }
1850 }
1853 /**
1854 * Helper that draws a specific rectangular part of the canvas.
1855 *
1856 * @return true if the rectangle painting succeeds.
1857 */
1858 static bool
1859 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1860 {
1861 g_return_val_if_fail (!canvas->need_update, false);
1863 NRRectL rect;
1864 rect.x0 = xx0;
1865 rect.x1 = xx1;
1866 rect.y0 = yy0;
1867 rect.y1 = yy1;
1869 // Clip rect-to-draw by the current visible area
1870 rect.x0 = MAX (rect.x0, canvas->x0);
1871 rect.y0 = MAX (rect.y0, canvas->y0);
1872 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1873 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1875 #ifdef DEBUG_REDRAW
1876 // paint the area to redraw yellow
1877 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1878 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1879 canvas->pixmap_gc,
1880 TRUE,
1881 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1882 rect.x1 - rect.x0, rect.y1 - rect.y0);
1883 #endif
1885 PaintRectSetup setup;
1887 setup.canvas = canvas;
1888 setup.big_rect = rect;
1890 // Save the mouse location
1891 gint x, y;
1892 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1893 setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1895 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1896 // use 256K as a compromise to not slow down gradients
1897 // 256K is the cached buffer and we need 4 channels
1898 setup.max_pixels = 65536; // 256K/4
1899 } else {
1900 // paths only, so 1M works faster
1901 // 1M is the cached buffer and we need 4 channels
1902 setup.max_pixels = 262144;
1903 }
1905 // Start the clock
1906 g_get_current_time(&(setup.start_time));
1908 // Go
1909 return sp_canvas_paint_rect_internal(&setup, rect);
1910 }
1912 /**
1913 * Force a full redraw after a specified number of interrupted redraws
1914 */
1915 void
1916 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1917 g_return_if_fail(canvas != NULL);
1919 canvas->forced_redraw_limit = count;
1920 canvas->forced_redraw_count = 0;
1921 }
1923 /**
1924 * End forced full redraw requests
1925 */
1926 void
1927 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1928 g_return_if_fail(canvas != NULL);
1930 canvas->forced_redraw_limit = -1;
1931 }
1933 /**
1934 * The canvas widget's expose callback.
1935 */
1936 static gint
1937 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1938 {
1939 SPCanvas *canvas = SP_CANVAS (widget);
1941 if (!GTK_WIDGET_DRAWABLE (widget) ||
1942 (event->window != SP_CANVAS_WINDOW (canvas)))
1943 return FALSE;
1945 int n_rects;
1946 GdkRectangle *rects;
1947 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1949 for (int i = 0; i < n_rects; i++) {
1950 NRRectL rect;
1952 rect.x0 = rects[i].x + canvas->x0;
1953 rect.y0 = rects[i].y + canvas->y0;
1954 rect.x1 = rect.x0 + rects[i].width;
1955 rect.y1 = rect.y0 + rects[i].height;
1957 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1958 }
1960 if (n_rects > 0)
1961 g_free (rects);
1963 return FALSE;
1964 }
1966 /**
1967 * The canvas widget's keypress callback.
1968 */
1969 static gint
1970 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1971 {
1972 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1973 }
1975 /**
1976 * Crossing event handler for the canvas.
1977 */
1978 static gint
1979 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1980 {
1981 SPCanvas *canvas = SP_CANVAS (widget);
1983 if (event->window != SP_CANVAS_WINDOW (canvas))
1984 return FALSE;
1986 canvas->state = event->state;
1987 return pick_current_item (canvas, (GdkEvent *) event);
1988 }
1990 /**
1991 * Focus in handler for the canvas.
1992 */
1993 static gint
1994 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1995 {
1996 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1998 SPCanvas *canvas = SP_CANVAS (widget);
2000 if (canvas->focused_item) {
2001 return emit_event (canvas, (GdkEvent *) event);
2002 } else {
2003 return FALSE;
2004 }
2005 }
2007 /**
2008 * Focus out handler for the canvas.
2009 */
2010 static gint
2011 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2012 {
2013 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2015 SPCanvas *canvas = SP_CANVAS (widget);
2017 if (canvas->focused_item)
2018 return emit_event (canvas, (GdkEvent *) event);
2019 else
2020 return FALSE;
2021 }
2023 /**
2024 * Helper that repaints the areas in the canvas that need it.
2025 *
2026 * @return true if all the dirty parts have been redrawn
2027 */
2028 static int
2029 paint (SPCanvas *canvas)
2030 {
2031 if (canvas->need_update) {
2032 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2033 canvas->need_update = FALSE;
2034 }
2036 if (!canvas->need_redraw)
2037 return TRUE;
2039 Gdk::Region to_paint;
2041 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2042 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2043 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2045 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2046 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2047 TILE_SIZE, TILE_SIZE));
2048 }
2050 }
2051 }
2053 if (!to_paint.empty()) {
2054 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2055 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2056 for (Iter i=rect.begin(); i != rect.end(); ++i) {
2057 int x0 = (*i).get_x();
2058 int y0 = (*i).get_y();
2059 int x1 = x0 + (*i).get_width();
2060 int y1 = y0 + (*i).get_height();
2061 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2062 // Aborted
2063 return FALSE;
2064 };
2065 }
2066 }
2068 canvas->need_redraw = FALSE;
2070 // we've had a full unaborted redraw, reset the full redraw counter
2071 if (canvas->forced_redraw_limit != -1) {
2072 canvas->forced_redraw_count = 0;
2073 }
2075 return TRUE;
2076 }
2078 /**
2079 * Helper that invokes update, paint, and repick on canvas.
2080 */
2081 static int
2082 do_update (SPCanvas *canvas)
2083 {
2084 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2085 return TRUE;
2087 /* Cause the update if necessary */
2088 if (canvas->need_update) {
2089 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2090 canvas->need_update = FALSE;
2091 }
2093 /* Paint if able to */
2094 if (GTK_WIDGET_DRAWABLE (canvas)) {
2095 return paint (canvas);
2096 }
2098 /* Pick new current item */
2099 while (canvas->need_repick) {
2100 canvas->need_repick = FALSE;
2101 pick_current_item (canvas, &canvas->pick_event);
2102 }
2104 return TRUE;
2105 }
2107 /**
2108 * Idle handler for the canvas that deals with pending updates and redraws.
2109 */
2110 static gint
2111 idle_handler (gpointer data)
2112 {
2113 GDK_THREADS_ENTER ();
2115 SPCanvas *canvas = SP_CANVAS (data);
2117 int const ret = do_update (canvas);
2119 if (ret) {
2120 /* Reset idle id */
2121 canvas->idle_id = 0;
2122 }
2124 GDK_THREADS_LEAVE ();
2126 return !ret;
2127 }
2129 /**
2130 * Convenience function to add an idle handler to a canvas.
2131 */
2132 static void
2133 add_idle (SPCanvas *canvas)
2134 {
2135 if (canvas->idle_id != 0)
2136 return;
2138 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2139 }
2141 /**
2142 * Returns the root group of the specified canvas.
2143 */
2144 SPCanvasGroup *
2145 sp_canvas_root (SPCanvas *canvas)
2146 {
2147 g_return_val_if_fail (canvas != NULL, NULL);
2148 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2150 return SP_CANVAS_GROUP (canvas->root);
2151 }
2153 /**
2154 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2155 */
2156 void
2157 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2158 {
2159 g_return_if_fail (canvas != NULL);
2160 g_return_if_fail (SP_IS_CANVAS (canvas));
2162 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2163 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2164 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2165 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2167 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2168 canvas->dy0 = cy;
2169 canvas->x0 = ix;
2170 canvas->y0 = iy;
2172 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2174 if (!clear) {
2175 // scrolling without zoom; redraw only the newly exposed areas
2176 if ((dx != 0) || (dy != 0)) {
2177 canvas->is_scrolling = is_scrolling;
2178 if (GTK_WIDGET_REALIZED (canvas)) {
2179 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2180 }
2181 }
2182 } else {
2183 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2184 }
2185 }
2187 /**
2188 * Updates canvas if necessary.
2189 */
2190 void
2191 sp_canvas_update_now (SPCanvas *canvas)
2192 {
2193 g_return_if_fail (canvas != NULL);
2194 g_return_if_fail (SP_IS_CANVAS (canvas));
2196 if (!(canvas->need_update ||
2197 canvas->need_redraw))
2198 return;
2200 do_update (canvas);
2201 }
2203 /**
2204 * Update callback for canvas widget.
2205 */
2206 static void
2207 sp_canvas_request_update (SPCanvas *canvas)
2208 {
2209 canvas->need_update = TRUE;
2210 add_idle (canvas);
2211 }
2213 /**
2214 * Forces redraw of rectangular canvas area.
2215 */
2216 void
2217 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2218 {
2219 NRRectL bbox;
2220 NRRectL visible;
2221 NRRectL clip;
2223 g_return_if_fail (canvas != NULL);
2224 g_return_if_fail (SP_IS_CANVAS (canvas));
2226 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2227 if ((x0 >= x1) || (y0 >= y1)) return;
2229 bbox.x0 = x0;
2230 bbox.y0 = y0;
2231 bbox.x1 = x1;
2232 bbox.y1 = y1;
2234 visible.x0 = canvas->x0;
2235 visible.y0 = canvas->y0;
2236 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2237 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2239 nr_rect_l_intersect (&clip, &bbox, &visible);
2241 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2242 add_idle (canvas);
2243 }
2245 /**
2246 * Sets world coordinates from win and canvas.
2247 */
2248 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2249 {
2250 g_return_if_fail (canvas != NULL);
2251 g_return_if_fail (SP_IS_CANVAS (canvas));
2253 if (worldx) *worldx = canvas->x0 + winx;
2254 if (worldy) *worldy = canvas->y0 + winy;
2255 }
2257 /**
2258 * Sets win coordinates from world and canvas.
2259 */
2260 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2261 {
2262 g_return_if_fail (canvas != NULL);
2263 g_return_if_fail (SP_IS_CANVAS (canvas));
2265 if (winx) *winx = worldx - canvas->x0;
2266 if (winy) *winy = worldy - canvas->y0;
2267 }
2269 /**
2270 * Converts point from win to world coordinates.
2271 */
2272 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2273 {
2274 g_assert (canvas != NULL);
2275 g_assert (SP_IS_CANVAS (canvas));
2277 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2278 }
2280 /**
2281 * Converts point from world to win coordinates.
2282 */
2283 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2284 {
2285 g_assert (canvas != NULL);
2286 g_assert (SP_IS_CANVAS (canvas));
2288 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2289 }
2291 /**
2292 * Returns true if point given in world coordinates is inside window.
2293 */
2294 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2295 {
2296 g_assert( canvas != NULL );
2297 g_assert(SP_IS_CANVAS(canvas));
2299 using NR::X;
2300 using NR::Y;
2301 GtkWidget const &w = *GTK_WIDGET(canvas);
2302 return ( ( canvas->x0 <= world[X] ) &&
2303 ( canvas->y0 <= world[Y] ) &&
2304 ( world[X] < canvas->x0 + w.allocation.width ) &&
2305 ( world[Y] < canvas->y0 + w.allocation.height ) );
2306 }
2308 /**
2309 * Return canvas window coordinates as NR::Rect.
2310 */
2311 NR::Rect SPCanvas::getViewbox() const
2312 {
2313 GtkWidget const *w = GTK_WIDGET(this);
2314 return NR::Rect(NR::Point(dx0, dy0),
2315 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2316 }
2318 /**
2319 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2320 */
2321 NR::IRect SPCanvas::getViewboxIntegers() const
2322 {
2323 GtkWidget const *w = GTK_WIDGET(this);
2324 return NR::IRect(NR::IPoint(x0, y0),
2325 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2326 }
2328 inline int sp_canvas_tile_floor(int x)
2329 {
2330 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2331 }
2333 inline int sp_canvas_tile_ceil(int x)
2334 {
2335 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2336 }
2338 /**
2339 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2340 */
2341 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2342 {
2343 if ( nl >= nr || nt >= nb ) {
2344 if ( canvas->tiles ) g_free(canvas->tiles);
2345 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2346 canvas->tileH=canvas->tileV=0;
2347 canvas->tiles=NULL;
2348 return;
2349 }
2350 int tl=sp_canvas_tile_floor(nl);
2351 int tt=sp_canvas_tile_floor(nt);
2352 int tr=sp_canvas_tile_ceil(nr);
2353 int tb=sp_canvas_tile_ceil(nb);
2355 int nh = tr-tl, nv = tb-tt;
2356 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2357 for (int i=tl; i<tr; i++) {
2358 for (int j=tt; j<tb; j++) {
2359 int ind = (i-tl) + (j-tt)*nh;
2360 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2361 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2362 } else {
2363 ntiles[ind]=0; // newly exposed areas get 0
2364 }
2365 }
2366 }
2367 if ( canvas->tiles ) g_free(canvas->tiles);
2368 canvas->tiles=ntiles;
2369 canvas->tLeft=tl;
2370 canvas->tTop=tt;
2371 canvas->tRight=tr;
2372 canvas->tBottom=tb;
2373 canvas->tileH=nh;
2374 canvas->tileV=nv;
2375 }
2377 /*
2378 * Helper that queues a canvas rectangle for redraw
2379 */
2380 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2381 canvas->need_redraw = TRUE;
2383 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2384 }
2386 /**
2387 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2388 */
2389 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2390 {
2391 if ( nl >= nr || nt >= nb ) {
2392 return;
2393 }
2394 int tl=sp_canvas_tile_floor(nl);
2395 int tt=sp_canvas_tile_floor(nt);
2396 int tr=sp_canvas_tile_ceil(nr);
2397 int tb=sp_canvas_tile_ceil(nb);
2398 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2399 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2400 if ( tr > canvas->tRight ) tr=canvas->tRight;
2401 if ( tt < canvas->tTop ) tt=canvas->tTop;
2402 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2404 for (int i=tl; i<tr; i++) {
2405 for (int j=tt; j<tb; j++) {
2406 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2407 }
2408 }
2409 }
2412 /*
2413 Local Variables:
2414 mode:c++
2415 c-file-style:"stroustrup"
2416 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2417 indent-tabs-mode:nil
2418 fill-column:99
2419 End:
2420 */
2421 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :