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"
47 // GTK_CHECK_VERSION returns false on failure
48 #define HAS_GDK_EVENT_REQUEST_MOTIONS GTK_CHECK_VERSION(2, 12, 0)
50 // gtk_check_version returns non-NULL on failure
51 static bool const HAS_BROKEN_MOTION_HINTS =
52 true || gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS;
54 // Define this to visualize the regions to be redrawn
55 //#define DEBUG_REDRAW 1;
57 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
58 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
59 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
60 #define TILE_SIZE 16
62 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
64 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
66 enum {
67 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
68 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
69 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
70 };
72 /**
73 * A group of Items.
74 */
75 struct SPCanvasGroup {
76 SPCanvasItem item;
78 GList *items, *last;
79 };
81 /**
82 * The SPCanvasGroup vtable.
83 */
84 struct SPCanvasGroupClass {
85 SPCanvasItemClass parent_class;
86 };
88 /**
89 * The SPCanvas vtable.
90 */
91 struct SPCanvasClass {
92 GtkWidgetClass parent_class;
93 };
95 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
96 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
98 /* SPCanvasItem */
100 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
103 static void sp_canvas_request_update (SPCanvas *canvas);
105 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
106 static void sp_canvas_item_init (SPCanvasItem *item);
107 static void sp_canvas_item_dispose (GObject *object);
108 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
110 static int emit_event (SPCanvas *canvas, GdkEvent *event);
112 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
114 static GtkObjectClass *item_parent_class;
116 /**
117 * Registers the SPCanvasItem class with Glib and returns its type number.
118 */
119 GType
120 sp_canvas_item_get_type (void)
121 {
122 static GType type = 0;
123 if (!type) {
124 static GTypeInfo const info = {
125 sizeof (SPCanvasItemClass),
126 NULL, NULL,
127 (GClassInitFunc) sp_canvas_item_class_init,
128 NULL, NULL,
129 sizeof (SPCanvasItem),
130 0,
131 (GInstanceInitFunc) sp_canvas_item_init,
132 NULL
133 };
134 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
135 }
137 return type;
138 }
140 /**
141 * Initializes the SPCanvasItem vtable and the "event" signal.
142 */
143 static void
144 sp_canvas_item_class_init (SPCanvasItemClass *klass)
145 {
146 GObjectClass *object_class = (GObjectClass *) klass;
148 /* fixme: Derive from GObject */
149 item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
151 item_signals[ITEM_EVENT] = g_signal_new ("event",
152 G_TYPE_FROM_CLASS (klass),
153 G_SIGNAL_RUN_LAST,
154 ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
155 NULL, NULL,
156 sp_marshal_BOOLEAN__POINTER,
157 G_TYPE_BOOLEAN, 1,
158 GDK_TYPE_EVENT);
160 object_class->dispose = sp_canvas_item_dispose;
161 }
163 /**
164 * Callback for initialization of SPCanvasItem.
165 */
166 static void
167 sp_canvas_item_init (SPCanvasItem *item)
168 {
169 item->flags |= SP_CANVAS_ITEM_VISIBLE;
170 item->xform = NR::Matrix(NR::identity());
171 }
173 /**
174 * Constructs new SPCanvasItem on SPCanvasGroup.
175 */
176 SPCanvasItem *
177 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
178 {
179 va_list args;
181 g_return_val_if_fail (parent != NULL, NULL);
182 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
183 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
185 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
187 va_start (args, first_arg_name);
188 sp_canvas_item_construct (item, parent, first_arg_name, args);
189 va_end (args);
191 return item;
192 }
194 /**
195 * Sets up the newly created SPCanvasItem.
196 *
197 * We make it static for encapsulation reasons since it was nowhere used.
198 */
199 static void
200 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
201 {
202 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
203 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
205 item->parent = SP_CANVAS_ITEM (parent);
206 item->canvas = item->parent->canvas;
208 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
210 group_add (SP_CANVAS_GROUP (item->parent), item);
212 sp_canvas_item_request_update (item);
213 }
215 /**
216 * Helper function that requests redraw only if item's visible flag is set.
217 */
218 static void
219 redraw_if_visible (SPCanvasItem *item)
220 {
221 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
222 int x0 = (int)(item->x1);
223 int x1 = (int)(item->x2);
224 int y0 = (int)(item->y1);
225 int y1 = (int)(item->y2);
227 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
228 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
229 }
230 }
231 }
233 /**
234 * Callback that removes item from all referers and destroys it.
235 */
236 static void
237 sp_canvas_item_dispose (GObject *object)
238 {
239 SPCanvasItem *item = SP_CANVAS_ITEM (object);
241 // Hack: if this is a ctrlrect, move it to 0,0;
242 // this redraws only the stroke of the rect to be deleted,
243 // avoiding redraw of the entire area
244 if (SP_IS_CTRLRECT(item)) {
245 SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
246 SP_CTRLRECT(object)->update(item->xform, 0);
247 } else {
248 redraw_if_visible (item);
249 }
250 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
252 if (item == item->canvas->current_item) {
253 item->canvas->current_item = NULL;
254 item->canvas->need_repick = TRUE;
255 }
257 if (item == item->canvas->new_current_item) {
258 item->canvas->new_current_item = NULL;
259 item->canvas->need_repick = TRUE;
260 }
262 if (item == item->canvas->grabbed_item) {
263 item->canvas->grabbed_item = NULL;
264 gdk_pointer_ungrab (GDK_CURRENT_TIME);
265 }
267 if (item == item->canvas->focused_item)
268 item->canvas->focused_item = NULL;
270 if (item->parent) {
271 group_remove (SP_CANVAS_GROUP (item->parent), item);
272 }
274 G_OBJECT_CLASS (item_parent_class)->dispose (object);
275 }
277 /**
278 * Helper function to update item and its children.
279 *
280 * NB! affine is parent2canvas.
281 */
282 static void
283 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
284 {
285 /* Apply the child item's transform */
286 NR::Matrix child_affine = item->xform * affine;
288 /* apply object flags to child flags */
289 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
291 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
292 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
294 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
295 child_flags |= SP_CANVAS_UPDATE_AFFINE;
297 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
298 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
299 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
300 }
302 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
303 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
304 }
306 /**
307 * Helper function to invoke the point method of the item.
308 *
309 * The argument x, y should be in the parent's item-relative coordinate
310 * system. This routine applies the inverse of the item's transform,
311 * maintaining the affine invariant.
312 */
313 static double
314 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
315 {
316 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
317 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
319 return NR_HUGE;
320 }
322 /**
323 * Makes the item's affine transformation matrix be equal to the specified
324 * matrix.
325 *
326 * @item: A canvas item.
327 * @affine: An affine transformation matrix.
328 */
329 void
330 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine)
331 {
332 item->xform = affine;
334 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
335 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
336 if (item->parent != NULL) {
337 sp_canvas_item_request_update (item->parent);
338 } else {
339 sp_canvas_request_update (item->canvas);
340 }
341 }
343 item->canvas->need_repick = TRUE;
344 }
346 /**
347 * Convenience function to reorder items in a group's child list.
348 *
349 * This puts the specified link after the "before" link.
350 */
351 static void
352 put_item_after (GList *link, GList *before)
353 {
354 if (link == before)
355 return;
357 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
359 if (before == NULL) {
360 if (link == parent->items) return;
362 link->prev->next = link->next;
364 if (link->next) {
365 link->next->prev = link->prev;
366 } else {
367 parent->last = link->prev;
368 }
370 link->prev = before;
371 link->next = parent->items;
372 link->next->prev = link;
373 parent->items = link;
374 } else {
375 if ((link == parent->last) && (before == parent->last->prev))
376 return;
378 if (link->next)
379 link->next->prev = link->prev;
381 if (link->prev)
382 link->prev->next = link->next;
383 else {
384 parent->items = link->next;
385 parent->items->prev = NULL;
386 }
388 link->prev = before;
389 link->next = before->next;
391 link->prev->next = link;
393 if (link->next)
394 link->next->prev = link;
395 else
396 parent->last = link;
397 }
398 }
401 /**
402 * Raises the item in its parent's stack by the specified number of positions.
403 *
404 * \param item A canvas item.
405 * \param positions Number of steps to raise the item.
406 *
407 * If the number of positions is greater than the distance to the top of the
408 * stack, then the item is put at the top.
409 */
410 void
411 sp_canvas_item_raise (SPCanvasItem *item, int positions)
412 {
413 g_return_if_fail (item != NULL);
414 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
415 g_return_if_fail (positions >= 0);
417 if (!item->parent || positions == 0)
418 return;
420 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
421 GList *link = g_list_find (parent->items, item);
422 g_assert (link != NULL);
424 GList *before;
425 for (before = link; positions && before; positions--)
426 before = before->next;
428 if (!before)
429 before = parent->last;
431 put_item_after (link, before);
433 redraw_if_visible (item);
434 item->canvas->need_repick = TRUE;
435 }
438 /**
439 * Lowers the item in its parent's stack by the specified number of positions.
440 *
441 * \param item A canvas item.
442 * \param positions Number of steps to lower the item.
443 *
444 * If the number of positions is greater than the distance to the bottom of the
445 * stack, then the item is put at the bottom.
446 **/
447 void
448 sp_canvas_item_lower (SPCanvasItem *item, int positions)
449 {
450 g_return_if_fail (item != NULL);
451 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
452 g_return_if_fail (positions >= 1);
454 if (!item->parent || positions == 0)
455 return;
457 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
458 GList *link = g_list_find (parent->items, item);
459 g_assert (link != NULL);
461 GList *before;
462 if (link->prev)
463 for (before = link->prev; positions && before; positions--)
464 before = before->prev;
465 else
466 before = NULL;
468 put_item_after (link, before);
470 redraw_if_visible (item);
471 item->canvas->need_repick = TRUE;
472 }
474 /**
475 * Sets visible flag on item and requests a redraw.
476 */
477 void
478 sp_canvas_item_show (SPCanvasItem *item)
479 {
480 g_return_if_fail (item != NULL);
481 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
483 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
484 return;
486 item->flags |= SP_CANVAS_ITEM_VISIBLE;
488 int x0 = (int)(item->x1);
489 int x1 = (int)(item->x2);
490 int y0 = (int)(item->y1);
491 int y1 = (int)(item->y2);
493 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
494 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
495 item->canvas->need_repick = TRUE;
496 }
497 }
499 /**
500 * Clears visible flag on item and requests a redraw.
501 */
502 void
503 sp_canvas_item_hide (SPCanvasItem *item)
504 {
505 g_return_if_fail (item != NULL);
506 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
508 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
509 return;
511 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
513 int x0 = (int)(item->x1);
514 int x1 = (int)(item->x2);
515 int y0 = (int)(item->y1);
516 int y1 = (int)(item->y2);
518 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
519 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
520 item->canvas->need_repick = TRUE;
521 }
522 }
524 /**
525 * Grab item under cursor.
526 *
527 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
528 */
529 int
530 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
531 {
532 g_return_val_if_fail (item != NULL, -1);
533 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
534 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
536 if (item->canvas->grabbed_item)
537 return -1;
539 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
540 return -1;
542 if (HAS_BROKEN_MOTION_HINTS) {
543 event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
544 }
546 /* fixme: Top hack (Lauris) */
547 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
548 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
549 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
550 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
551 NULL, cursor, etime);
553 item->canvas->grabbed_item = item;
554 item->canvas->grabbed_event_mask = event_mask;
555 item->canvas->current_item = item; /* So that events go to the grabbed item */
557 return 0;
558 }
560 /**
561 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
562 * mouse.
563 *
564 * \param item A canvas item that holds a grab.
565 * \param etime The timestamp for ungrabbing the mouse.
566 */
567 void
568 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
569 {
570 g_return_if_fail (item != NULL);
571 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
573 if (item->canvas->grabbed_item != item)
574 return;
576 item->canvas->grabbed_item = NULL;
578 gdk_pointer_ungrab (etime);
579 }
581 /**
582 * Returns the product of all transformation matrices from the root item down
583 * to the item.
584 */
585 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
586 {
587 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
589 NR::Matrix affine = NR::identity();
591 while (item) {
592 affine *= item->xform;
593 item = item->parent;
594 }
595 return affine;
596 }
598 /**
599 * Helper that returns true iff item is descendant of parent.
600 */
601 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
602 {
603 while (item) {
604 if (item == parent)
605 return true;
606 item = item->parent;
607 }
609 return false;
610 }
612 /**
613 * Focus canvas, and item under cursor if it is not already focussed.
614 */
615 void
616 sp_canvas_item_grab_focus (SPCanvasItem *item)
617 {
618 g_return_if_fail (item != NULL);
619 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
620 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
622 SPCanvasItem *focused_item = item->canvas->focused_item;
624 if (focused_item) {
625 GdkEvent ev;
626 ev.focus_change.type = GDK_FOCUS_CHANGE;
627 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
628 ev.focus_change.send_event = FALSE;
629 ev.focus_change.in = FALSE;
631 emit_event (item->canvas, &ev);
632 }
634 item->canvas->focused_item = item;
635 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
637 if (focused_item) {
638 GdkEvent ev;
639 ev.focus_change.type = GDK_FOCUS_CHANGE;
640 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
641 ev.focus_change.send_event = FALSE;
642 ev.focus_change.in = TRUE;
644 emit_event (item->canvas, &ev);
645 }
646 }
648 /**
649 * Requests that the canvas queue an update for the specified item.
650 *
651 * To be used only by item implementations.
652 */
653 void
654 sp_canvas_item_request_update (SPCanvasItem *item)
655 {
656 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
657 return;
659 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
661 if (item->parent != NULL) {
662 /* Recurse up the tree */
663 sp_canvas_item_request_update (item->parent);
664 } else {
665 /* Have reached the top of the tree, make sure the update call gets scheduled. */
666 sp_canvas_request_update (item->canvas);
667 }
668 }
670 /**
671 * Returns position of item in group.
672 */
673 gint sp_canvas_item_order (SPCanvasItem * item)
674 {
675 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
676 }
678 /* SPCanvasGroup */
680 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
681 static void sp_canvas_group_init (SPCanvasGroup *group);
682 static void sp_canvas_group_destroy (GtkObject *object);
684 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
685 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
686 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
688 static SPCanvasItemClass *group_parent_class;
690 /**
691 * Registers SPCanvasGroup class with Gtk and returns its type number.
692 */
693 GType sp_canvas_group_get_type(void)
694 {
695 static GType type = 0;
696 if (!type) {
697 GTypeInfo info = {
698 sizeof(SPCanvasGroupClass),
699 0, // base_init
700 0, // base_finalize
701 (GClassInitFunc)sp_canvas_group_class_init,
702 0, // class_finalize
703 0, // class_data
704 sizeof(SPCanvasGroup),
705 0, // n_preallocs
706 (GInstanceInitFunc)sp_canvas_group_init,
707 0 // value_table
708 };
709 type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
710 }
711 return type;
712 }
714 /**
715 * Class initialization function for SPCanvasGroupClass
716 */
717 static void
718 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
719 {
720 GtkObjectClass *object_class = (GtkObjectClass *) klass;
721 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
723 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
725 object_class->destroy = sp_canvas_group_destroy;
727 item_class->update = sp_canvas_group_update;
728 item_class->render = sp_canvas_group_render;
729 item_class->point = sp_canvas_group_point;
730 }
732 /**
733 * Callback. Empty.
734 */
735 static void
736 sp_canvas_group_init (SPCanvasGroup */*group*/)
737 {
738 /* Nothing here */
739 }
741 /**
742 * Callback that destroys all items in group and calls group's virtual
743 * destroy() function.
744 */
745 static void
746 sp_canvas_group_destroy (GtkObject *object)
747 {
748 g_return_if_fail (object != NULL);
749 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
751 SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
753 GList *list = group->items;
754 while (list) {
755 SPCanvasItem *child = (SPCanvasItem *)list->data;
756 list = list->next;
758 gtk_object_destroy (GTK_OBJECT (child));
759 }
761 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
762 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
763 }
765 /**
766 * Update handler for canvas groups
767 */
768 static void
769 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
770 {
771 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
772 NR::ConvexHull corners(NR::Point(0, 0));
773 bool empty=true;
775 for (GList *list = group->items; list; list = list->next) {
776 SPCanvasItem *i = (SPCanvasItem *)list->data;
778 sp_canvas_item_invoke_update (i, affine, flags);
780 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
781 if (empty) {
782 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
783 empty = false;
784 } else {
785 corners.add(NR::Point(i->x1, i->y1));
786 }
787 corners.add(NR::Point(i->x2, i->y2));
788 }
789 }
791 NR::Maybe<NR::Rect> const bounds = corners.bounds();
792 if (bounds) {
793 item->x1 = bounds->min()[NR::X];
794 item->y1 = bounds->min()[NR::Y];
795 item->x2 = bounds->max()[NR::X];
796 item->y2 = bounds->max()[NR::Y];
797 } else {
798 // FIXME ?
799 item->x1 = item->x2 = item->y1 = item->y2 = 0;
800 }
801 }
803 /**
804 * Point handler for canvas groups.
805 */
806 static double
807 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
808 {
809 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
810 double const x = p[NR::X];
811 double const y = p[NR::Y];
812 int x1 = (int)(x - item->canvas->close_enough);
813 int y1 = (int)(y - item->canvas->close_enough);
814 int x2 = (int)(x + item->canvas->close_enough);
815 int y2 = (int)(y + item->canvas->close_enough);
817 double best = 0.0;
818 *actual_item = NULL;
820 double dist = 0.0;
822 for (GList *list = group->items; list; list = list->next) {
823 SPCanvasItem *child = (SPCanvasItem *)list->data;
825 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
826 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
828 int has_point;
829 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
830 dist = sp_canvas_item_invoke_point (child, p, &point_item);
831 has_point = TRUE;
832 } else
833 has_point = FALSE;
835 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
836 best = dist;
837 *actual_item = point_item;
838 }
839 }
840 }
842 return best;
843 }
845 /**
846 * Renders all visible canvas group items in buf rectangle.
847 */
848 static void
849 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
850 {
851 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
853 for (GList *list = group->items; list; list = list->next) {
854 SPCanvasItem *child = (SPCanvasItem *)list->data;
855 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
856 if ((child->x1 < buf->rect.x1) &&
857 (child->y1 < buf->rect.y1) &&
858 (child->x2 > buf->rect.x0) &&
859 (child->y2 > buf->rect.y0)) {
860 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
861 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
862 }
863 }
864 }
865 }
867 /**
868 * Adds an item to a canvas group.
869 */
870 static void
871 group_add (SPCanvasGroup *group, SPCanvasItem *item)
872 {
873 gtk_object_ref (GTK_OBJECT (item));
874 gtk_object_sink (GTK_OBJECT (item));
876 if (!group->items) {
877 group->items = g_list_append (group->items, item);
878 group->last = group->items;
879 } else {
880 group->last = g_list_append (group->last, item)->next;
881 }
883 sp_canvas_item_request_update (item);
884 }
886 /**
887 * Removes an item from a canvas group
888 */
889 static void
890 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
891 {
892 g_return_if_fail (group != NULL);
893 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
894 g_return_if_fail (item != NULL);
896 for (GList *children = group->items; children; children = children->next) {
897 if (children->data == item) {
899 /* Unparent the child */
901 item->parent = NULL;
902 gtk_object_unref (GTK_OBJECT (item));
904 /* Remove it from the list */
906 if (children == group->last) group->last = children->prev;
908 group->items = g_list_remove_link (group->items, children);
909 g_list_free (children);
910 break;
911 }
912 }
913 }
915 /* SPCanvas */
917 static void sp_canvas_class_init (SPCanvasClass *klass);
918 static void sp_canvas_init (SPCanvas *canvas);
919 static void sp_canvas_destroy (GtkObject *object);
921 static void sp_canvas_realize (GtkWidget *widget);
922 static void sp_canvas_unrealize (GtkWidget *widget);
924 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
925 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
927 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
928 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
929 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
930 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
931 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
932 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
933 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
934 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
936 static GtkWidgetClass *canvas_parent_class;
938 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
939 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
940 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
941 static int do_update (SPCanvas *canvas);
943 /**
944 * Registers the SPCanvas class if necessary, and returns the type ID
945 * associated to it.
946 *
947 * \return The type ID of the SPCanvas class.
948 **/
949 GType sp_canvas_get_type(void)
950 {
951 static GType type = 0;
952 if (!type) {
953 GTypeInfo info = {
954 sizeof(SPCanvasClass),
955 0, // base_init
956 0, // base_finalize
957 (GClassInitFunc)sp_canvas_class_init,
958 0, // class_finalize
959 0, // class_data
960 sizeof(SPCanvas),
961 0, // n_preallocs
962 (GInstanceInitFunc)sp_canvas_init,
963 0 // value_table
964 };
965 type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
966 }
967 return type;
968 }
970 /**
971 * Class initialization function for SPCanvasClass.
972 */
973 static void
974 sp_canvas_class_init (SPCanvasClass *klass)
975 {
976 GtkObjectClass *object_class = (GtkObjectClass *) klass;
977 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
979 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
981 object_class->destroy = sp_canvas_destroy;
983 widget_class->realize = sp_canvas_realize;
984 widget_class->unrealize = sp_canvas_unrealize;
985 widget_class->size_request = sp_canvas_size_request;
986 widget_class->size_allocate = sp_canvas_size_allocate;
987 widget_class->button_press_event = sp_canvas_button;
988 widget_class->button_release_event = sp_canvas_button;
989 widget_class->motion_notify_event = sp_canvas_motion;
990 widget_class->scroll_event = sp_canvas_scroll;
991 widget_class->expose_event = sp_canvas_expose;
992 widget_class->key_press_event = sp_canvas_key;
993 widget_class->key_release_event = sp_canvas_key;
994 widget_class->enter_notify_event = sp_canvas_crossing;
995 widget_class->leave_notify_event = sp_canvas_crossing;
996 widget_class->focus_in_event = sp_canvas_focus_in;
997 widget_class->focus_out_event = sp_canvas_focus_out;
998 }
1000 /**
1001 * Callback: object initialization for SPCanvas.
1002 */
1003 static void
1004 sp_canvas_init (SPCanvas *canvas)
1005 {
1006 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1007 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1008 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1010 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1011 canvas->pick_event.crossing.x = 0;
1012 canvas->pick_event.crossing.y = 0;
1014 /* Create the root item as a special case */
1015 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1016 canvas->root->canvas = canvas;
1018 gtk_object_ref (GTK_OBJECT (canvas->root));
1019 gtk_object_sink (GTK_OBJECT (canvas->root));
1021 canvas->need_repick = TRUE;
1023 // See comment at in sp-canvas.h.
1024 canvas->gen_all_enter_events = false;
1026 canvas->tiles=NULL;
1027 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1028 canvas->tileH=canvas->tileV=0;
1030 canvas->forced_redraw_count = 0;
1031 canvas->forced_redraw_limit = -1;
1033 #if ENABLE_LCMS
1034 canvas->enable_cms_display_adj = false;
1035 canvas->cms_key = new Glib::ustring("");
1036 #endif // ENABLE_LCMS
1038 canvas->is_scrolling = false;
1040 }
1042 /**
1043 * Convenience function to remove the idle handler of a canvas.
1044 */
1045 static void
1046 remove_idle (SPCanvas *canvas)
1047 {
1048 if (canvas->idle_id) {
1049 gtk_idle_remove (canvas->idle_id);
1050 canvas->idle_id = 0;
1051 }
1052 }
1054 /*
1055 * Removes the transient state of the canvas (idle handler, grabs).
1056 */
1057 static void
1058 shutdown_transients (SPCanvas *canvas)
1059 {
1060 /* We turn off the need_redraw flag, since if the canvas is mapped again
1061 * it will request a redraw anyways. We do not turn off the need_update
1062 * flag, though, because updates are not queued when the canvas remaps
1063 * itself.
1064 */
1065 if (canvas->need_redraw) {
1066 canvas->need_redraw = FALSE;
1067 }
1068 if ( canvas->tiles ) g_free(canvas->tiles);
1069 canvas->tiles=NULL;
1070 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1071 canvas->tileH=canvas->tileV=0;
1073 if (canvas->grabbed_item) {
1074 canvas->grabbed_item = NULL;
1075 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1076 }
1078 remove_idle (canvas);
1079 }
1081 /**
1082 * Destroy handler for SPCanvas.
1083 */
1084 static void
1085 sp_canvas_destroy (GtkObject *object)
1086 {
1087 SPCanvas *canvas = SP_CANVAS (object);
1089 if (canvas->root) {
1090 gtk_object_unref (GTK_OBJECT (canvas->root));
1091 canvas->root = NULL;
1092 }
1094 shutdown_transients (canvas);
1096 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1097 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1098 }
1100 /**
1101 * Returns new canvas as widget.
1102 */
1103 GtkWidget *
1104 sp_canvas_new_aa (void)
1105 {
1106 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1108 return (GtkWidget *) canvas;
1109 }
1111 /**
1112 * The canvas widget's realize callback.
1113 */
1114 static void
1115 sp_canvas_realize (GtkWidget *widget)
1116 {
1117 SPCanvas *canvas = SP_CANVAS (widget);
1119 GdkWindowAttr attributes;
1120 attributes.window_type = GDK_WINDOW_CHILD;
1121 attributes.x = widget->allocation.x;
1122 attributes.y = widget->allocation.y;
1123 attributes.width = widget->allocation.width;
1124 attributes.height = widget->allocation.height;
1125 attributes.wclass = GDK_INPUT_OUTPUT;
1126 attributes.visual = gdk_rgb_get_visual ();
1127 attributes.colormap = gdk_rgb_get_cmap ();
1128 attributes.event_mask = (gtk_widget_get_events (widget) |
1129 GDK_EXPOSURE_MASK |
1130 GDK_BUTTON_PRESS_MASK |
1131 GDK_BUTTON_RELEASE_MASK |
1132 GDK_POINTER_MOTION_MASK |
1133 ( HAS_BROKEN_MOTION_HINTS ?
1134 0 : GDK_POINTER_MOTION_HINT_MASK ) |
1135 GDK_PROXIMITY_IN_MASK |
1136 GDK_PROXIMITY_OUT_MASK |
1137 GDK_KEY_PRESS_MASK |
1138 GDK_KEY_RELEASE_MASK |
1139 GDK_ENTER_NOTIFY_MASK |
1140 GDK_LEAVE_NOTIFY_MASK |
1141 GDK_FOCUS_CHANGE_MASK);
1142 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1144 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1145 gdk_window_set_user_data (widget->window, widget);
1147 if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1148 gtk_widget_set_events(widget, attributes.event_mask);
1150 widget->style = gtk_style_attach (widget->style, widget->window);
1152 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1154 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1155 }
1157 /**
1158 * The canvas widget's unrealize callback.
1159 */
1160 static void
1161 sp_canvas_unrealize (GtkWidget *widget)
1162 {
1163 SPCanvas *canvas = SP_CANVAS (widget);
1165 canvas->current_item = NULL;
1166 canvas->grabbed_item = NULL;
1167 canvas->focused_item = NULL;
1169 shutdown_transients (canvas);
1171 gdk_gc_destroy (canvas->pixmap_gc);
1172 canvas->pixmap_gc = NULL;
1174 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1175 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1176 }
1178 /**
1179 * The canvas widget's size_request callback.
1180 */
1181 static void
1182 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1183 {
1184 static_cast<void>(SP_CANVAS (widget));
1186 req->width = 256;
1187 req->height = 256;
1188 }
1190 /**
1191 * The canvas widget's size_allocate callback.
1192 */
1193 static void
1194 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1195 {
1196 SPCanvas *canvas = SP_CANVAS (widget);
1198 /* Schedule redraw of new region */
1199 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1200 if (allocation->width > widget->allocation.width) {
1201 sp_canvas_request_redraw (canvas,
1202 canvas->x0 + widget->allocation.width,
1203 0,
1204 canvas->x0 + allocation->width,
1205 canvas->y0 + allocation->height);
1206 }
1207 if (allocation->height > widget->allocation.height) {
1208 sp_canvas_request_redraw (canvas,
1209 0,
1210 canvas->y0 + widget->allocation.height,
1211 canvas->x0 + allocation->width,
1212 canvas->y0 + allocation->height);
1213 }
1215 widget->allocation = *allocation;
1217 if (GTK_WIDGET_REALIZED (widget)) {
1218 gdk_window_move_resize (widget->window,
1219 widget->allocation.x, widget->allocation.y,
1220 widget->allocation.width, widget->allocation.height);
1221 }
1222 }
1224 /**
1225 * Helper that emits an event for an item in the canvas, be it the current
1226 * item, grabbed item, or focused item, as appropriate.
1227 */
1228 static int
1229 emit_event (SPCanvas *canvas, GdkEvent *event)
1230 {
1231 guint mask;
1233 if (canvas->grabbed_item) {
1234 switch (event->type) {
1235 case GDK_ENTER_NOTIFY:
1236 mask = GDK_ENTER_NOTIFY_MASK;
1237 break;
1238 case GDK_LEAVE_NOTIFY:
1239 mask = GDK_LEAVE_NOTIFY_MASK;
1240 break;
1241 case GDK_MOTION_NOTIFY:
1242 mask = GDK_POINTER_MOTION_MASK;
1243 break;
1244 case GDK_BUTTON_PRESS:
1245 case GDK_2BUTTON_PRESS:
1246 case GDK_3BUTTON_PRESS:
1247 mask = GDK_BUTTON_PRESS_MASK;
1248 break;
1249 case GDK_BUTTON_RELEASE:
1250 mask = GDK_BUTTON_RELEASE_MASK;
1251 break;
1252 case GDK_KEY_PRESS:
1253 mask = GDK_KEY_PRESS_MASK;
1254 break;
1255 case GDK_KEY_RELEASE:
1256 mask = GDK_KEY_RELEASE_MASK;
1257 break;
1258 case GDK_SCROLL:
1259 mask = GDK_SCROLL;
1260 break;
1261 default:
1262 mask = 0;
1263 break;
1264 }
1266 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1267 }
1269 /* Convert to world coordinates -- we have two cases because of diferent
1270 * offsets of the fields in the event structures.
1271 */
1273 GdkEvent ev = *event;
1275 switch (ev.type) {
1276 case GDK_ENTER_NOTIFY:
1277 case GDK_LEAVE_NOTIFY:
1278 ev.crossing.x += canvas->x0;
1279 ev.crossing.y += canvas->y0;
1280 break;
1281 case GDK_MOTION_NOTIFY:
1282 case GDK_BUTTON_PRESS:
1283 case GDK_2BUTTON_PRESS:
1284 case GDK_3BUTTON_PRESS:
1285 case GDK_BUTTON_RELEASE:
1286 ev.motion.x += canvas->x0;
1287 ev.motion.y += canvas->y0;
1288 break;
1289 default:
1290 break;
1291 }
1293 /* Choose where we send the event */
1295 /* canvas->current_item becomes NULL in some cases under Win32
1296 ** (e.g. if the pointer leaves the window). So this is a hack that
1297 ** Lauris applied to SP to get around the problem.
1298 */
1299 SPCanvasItem* item = NULL;
1300 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1301 item = canvas->grabbed_item;
1302 } else {
1303 item = canvas->current_item;
1304 }
1306 if (canvas->focused_item &&
1307 ((event->type == GDK_KEY_PRESS) ||
1308 (event->type == GDK_KEY_RELEASE) ||
1309 (event->type == GDK_FOCUS_CHANGE))) {
1310 item = canvas->focused_item;
1311 }
1313 /* The event is propagated up the hierarchy (for if someone connected to
1314 * a group instead of a leaf event), and emission is stopped if a
1315 * handler returns TRUE, just like for GtkWidget events.
1316 */
1318 gint finished = FALSE;
1320 while (item && !finished) {
1321 gtk_object_ref (GTK_OBJECT (item));
1322 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1323 SPCanvasItem *parent = item->parent;
1324 gtk_object_unref (GTK_OBJECT (item));
1325 item = parent;
1326 }
1328 return finished;
1329 }
1331 /**
1332 * Helper that re-picks the current item in the canvas, based on the event's
1333 * coordinates and emits enter/leave events for items as appropriate.
1334 */
1335 static int
1336 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1337 {
1338 int button_down = 0;
1339 double x, y;
1341 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1342 return FALSE;
1344 int retval = FALSE;
1346 if (canvas->gen_all_enter_events == false) {
1347 // If a button is down, we'll perform enter and leave events on the
1348 // current item, but not enter on any other item. This is more or
1349 // less like X pointer grabbing for canvas items.
1350 //
1351 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1352 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1354 if (!button_down) canvas->left_grabbed_item = FALSE;
1355 }
1357 /* Save the event in the canvas. This is used to synthesize enter and
1358 * leave events in case the current item changes. It is also used to
1359 * re-pick the current item if the current one gets deleted. Also,
1360 * synthesize an enter event.
1361 */
1362 if (event != &canvas->pick_event) {
1363 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1364 /* these fields have the same offsets in both types of events */
1366 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1367 canvas->pick_event.crossing.window = event->motion.window;
1368 canvas->pick_event.crossing.send_event = event->motion.send_event;
1369 canvas->pick_event.crossing.subwindow = NULL;
1370 canvas->pick_event.crossing.x = event->motion.x;
1371 canvas->pick_event.crossing.y = event->motion.y;
1372 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1373 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1374 canvas->pick_event.crossing.focus = FALSE;
1375 canvas->pick_event.crossing.state = event->motion.state;
1377 /* these fields don't have the same offsets in both types of events */
1379 if (event->type == GDK_MOTION_NOTIFY) {
1380 canvas->pick_event.crossing.x_root = event->motion.x_root;
1381 canvas->pick_event.crossing.y_root = event->motion.y_root;
1382 } else {
1383 canvas->pick_event.crossing.x_root = event->button.x_root;
1384 canvas->pick_event.crossing.y_root = event->button.y_root;
1385 }
1386 } else {
1387 canvas->pick_event = *event;
1388 }
1389 }
1391 /* Don't do anything else if this is a recursive call */
1392 if (canvas->in_repick) return retval;
1394 /* LeaveNotify means that there is no current item, so we don't look for one */
1395 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1396 /* these fields don't have the same offsets in both types of events */
1398 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1399 x = canvas->pick_event.crossing.x;
1400 y = canvas->pick_event.crossing.y;
1401 } else {
1402 x = canvas->pick_event.motion.x;
1403 y = canvas->pick_event.motion.y;
1404 }
1406 /* world coords */
1407 x += canvas->x0;
1408 y += canvas->y0;
1410 /* find the closest item */
1411 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1412 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1413 } else {
1414 canvas->new_current_item = NULL;
1415 }
1416 } else {
1417 canvas->new_current_item = NULL;
1418 }
1420 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1421 return retval; /* current item did not change */
1422 }
1424 /* Synthesize events for old and new current items */
1426 if ((canvas->new_current_item != canvas->current_item)
1427 && (canvas->current_item != NULL)
1428 && !canvas->left_grabbed_item) {
1429 GdkEvent new_event;
1430 SPCanvasItem *item;
1432 item = canvas->current_item;
1434 new_event = canvas->pick_event;
1435 new_event.type = GDK_LEAVE_NOTIFY;
1437 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1438 new_event.crossing.subwindow = NULL;
1439 canvas->in_repick = TRUE;
1440 retval = emit_event (canvas, &new_event);
1441 canvas->in_repick = FALSE;
1442 }
1444 if (canvas->gen_all_enter_events == false) {
1445 // new_current_item may have been set to NULL during the call to
1446 // emit_event() above
1447 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1448 canvas->left_grabbed_item = TRUE;
1449 return retval;
1450 }
1451 }
1453 /* Handle the rest of cases */
1455 canvas->left_grabbed_item = FALSE;
1456 canvas->current_item = canvas->new_current_item;
1458 if (canvas->current_item != NULL) {
1459 GdkEvent new_event;
1461 new_event = canvas->pick_event;
1462 new_event.type = GDK_ENTER_NOTIFY;
1463 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1464 new_event.crossing.subwindow = NULL;
1465 retval = emit_event (canvas, &new_event);
1466 }
1468 return retval;
1469 }
1471 /**
1472 * Button event handler for the canvas.
1473 */
1474 static gint
1475 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1476 {
1477 SPCanvas *canvas = SP_CANVAS (widget);
1479 int retval = FALSE;
1481 /* dispatch normally regardless of the event's window if an item has
1482 has a pointer grab in effect */
1483 if (!canvas->grabbed_item &&
1484 event->window != SP_CANVAS_WINDOW (canvas))
1485 return retval;
1487 int mask;
1488 switch (event->button) {
1489 case 1:
1490 mask = GDK_BUTTON1_MASK;
1491 break;
1492 case 2:
1493 mask = GDK_BUTTON2_MASK;
1494 break;
1495 case 3:
1496 mask = GDK_BUTTON3_MASK;
1497 break;
1498 case 4:
1499 mask = GDK_BUTTON4_MASK;
1500 break;
1501 case 5:
1502 mask = GDK_BUTTON5_MASK;
1503 break;
1504 default:
1505 mask = 0;
1506 }
1508 switch (event->type) {
1509 case GDK_BUTTON_PRESS:
1510 case GDK_2BUTTON_PRESS:
1511 case GDK_3BUTTON_PRESS:
1512 /* Pick the current item as if the button were not pressed, and
1513 * then process the event.
1514 */
1515 canvas->state = event->state;
1516 pick_current_item (canvas, (GdkEvent *) event);
1517 canvas->state ^= mask;
1518 retval = emit_event (canvas, (GdkEvent *) event);
1519 break;
1521 case GDK_BUTTON_RELEASE:
1522 /* Process the event as if the button were pressed, then repick
1523 * after the button has been released
1524 */
1525 canvas->state = event->state;
1526 retval = emit_event (canvas, (GdkEvent *) event);
1527 event->state ^= mask;
1528 canvas->state = event->state;
1529 pick_current_item (canvas, (GdkEvent *) event);
1530 event->state ^= mask;
1531 break;
1533 default:
1534 g_assert_not_reached ();
1535 }
1537 return retval;
1538 }
1540 /**
1541 * Scroll event handler for the canvas.
1542 *
1543 * \todo FIXME: generate motion events to re-select items.
1544 */
1545 static gint
1546 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1547 {
1548 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1549 }
1551 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1552 gdk_window_get_pointer(w, NULL, NULL, NULL);
1553 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1554 gdk_event_request_motions(event);
1555 #endif
1556 }
1558 /**
1559 * Motion event handler for the canvas.
1560 */
1561 static int
1562 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1563 {
1564 int status;
1565 SPCanvas *canvas = SP_CANVAS (widget);
1567 if (event->window != SP_CANVAS_WINDOW (canvas))
1568 return FALSE;
1570 if (canvas->pixmap_gc == NULL) // canvas being deleted
1571 return FALSE;
1573 canvas->state = event->state;
1574 pick_current_item (canvas, (GdkEvent *) event);
1576 status = emit_event (canvas, (GdkEvent *) event);
1578 if (event->is_hint) {
1579 request_motions(widget->window, event);
1580 }
1582 return status;
1583 }
1585 static void
1586 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)
1587 {
1588 GtkWidget *widget = GTK_WIDGET (canvas);
1590 SPCanvasBuf buf;
1591 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1592 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1593 } else {
1594 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1595 }
1597 // Mark the region clean
1598 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1600 buf.buf_rowstride = sw * 4;
1601 buf.rect.x0 = x0;
1602 buf.rect.y0 = y0;
1603 buf.rect.x1 = x1;
1604 buf.rect.y1 = y1;
1605 buf.visible_rect.x0 = draw_x1;
1606 buf.visible_rect.y0 = draw_y1;
1607 buf.visible_rect.x1 = draw_x2;
1608 buf.visible_rect.y1 = draw_y2;
1609 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1610 buf.bg_color = (((color->red & 0xff00) << 8)
1611 | (color->green & 0xff00)
1612 | (color->blue >> 8));
1613 buf.is_empty = true;
1615 buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1617 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1618 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1619 }
1621 #if ENABLE_LCMS
1622 cmsHTRANSFORM transf = 0;
1623 long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1624 if ( fromDisplay ) {
1625 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1626 } else {
1627 transf = Inkscape::colorprofile_get_display_transform();
1628 }
1629 #endif // ENABLE_LCMS
1631 if (buf.is_empty) {
1632 #if ENABLE_LCMS
1633 if ( transf && canvas->enable_cms_display_adj ) {
1634 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1635 }
1636 #endif // ENABLE_LCMS
1637 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1638 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1639 canvas->pixmap_gc,
1640 TRUE,
1641 x0 - canvas->x0, y0 - canvas->y0,
1642 x1 - x0, y1 - y0);
1643 } else {
1645 #if ENABLE_LCMS
1646 if ( transf && canvas->enable_cms_display_adj ) {
1647 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1648 guchar* p = buf.buf + (sw * 3) * yy;
1649 cmsDoTransform( transf, p, p, (x1 - x0) );
1650 }
1651 }
1652 #endif // ENABLE_LCMS
1654 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1655 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1656 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1657 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1658 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1659 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1661 ///#define CANVAS_OUTPUT_VIA_CAIRO
1663 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1665 buf.cst = cairo_image_surface_create_for_data (
1666 buf.buf,
1667 CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
1668 x1 - x0, y1 - y0,
1669 buf.buf_rowstride
1670 );
1671 cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1672 cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1673 cairo_paint (window_ct);
1674 cairo_destroy (window_ct);
1675 cairo_surface_finish (buf.cst);
1676 cairo_surface_destroy (buf.cst);
1678 #else
1680 NRPixBlock b3;
1681 nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1683 NRPixBlock b4;
1684 nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1685 buf.buf,
1686 buf.buf_rowstride,
1687 FALSE, FALSE);
1689 // this does the 32->24 squishing, using an assembler routine:
1690 nr_blit_pixblock_pixblock (&b3, &b4);
1692 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1693 canvas->pixmap_gc,
1694 x0 - canvas->x0, y0 - canvas->y0,
1695 x1 - x0, y1 - y0,
1696 GDK_RGB_DITHER_MAX,
1697 b3.data.px,
1698 sw * 3,
1699 x0 - canvas->x0, y0 - canvas->y0);
1701 nr_pixblock_release (&b3);
1702 nr_pixblock_release (&b4);
1703 #endif
1704 }
1706 cairo_surface_t *cst = cairo_get_target(buf.ct);
1707 cairo_destroy (buf.ct);
1708 cairo_surface_finish (cst);
1709 cairo_surface_destroy (cst);
1711 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1712 nr_pixelstore_256K_free (buf.buf);
1713 } else {
1714 nr_pixelstore_1M_free (buf.buf);
1715 }
1716 }
1718 struct PaintRectSetup {
1719 SPCanvas* canvas;
1720 NRRectL big_rect;
1721 GTimeVal start_time;
1722 int max_pixels;
1723 NR::Point mouse_loc;
1724 };
1726 /**
1727 * Paint the given rect, recursively subdividing the region until it is the size of a single
1728 * buffer.
1729 *
1730 * @return true if the drawing completes
1731 */
1732 static int
1733 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1734 {
1735 GTimeVal now;
1736 g_get_current_time (&now);
1738 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1739 + (now.tv_usec - setup->start_time.tv_usec);
1741 // Allow only very fast buffers to be run together;
1742 // as soon as the total redraw time exceeds 1ms, cancel;
1743 // this returns control to the idle loop and allows Inkscape to process user input
1744 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1745 // it will get back and finish painting what remains to paint.
1746 if (elapsed > 1000) {
1748 // Interrupting redraw isn't always good.
1749 // For example, when you drag one node of a big path, only the buffer containing
1750 // the mouse cursor will be redrawn again and again, and the rest of the path
1751 // will remain stale because Inkscape never has enough idle time to redraw all
1752 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1753 // If this limit is set, and if we have aborted redraw more times than is allowed,
1754 // interrupting is blocked and we're forced to redraw full screen once
1755 // (after which we can again interrupt forced_redraw_limit times).
1756 if (setup->canvas->forced_redraw_limit < 0 ||
1757 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1759 if (setup->canvas->forced_redraw_limit != -1) {
1760 setup->canvas->forced_redraw_count++;
1761 }
1763 return false;
1764 }
1765 }
1767 // Find the optimal buffer dimensions
1768 int bw = this_rect.x1 - this_rect.x0;
1769 int bh = this_rect.y1 - this_rect.y0;
1770 if ((bw < 1) || (bh < 1))
1771 return 0;
1773 if (bw * bh < setup->max_pixels) {
1774 // We are small enough
1775 sp_canvas_paint_single_buffer (setup->canvas,
1776 this_rect.x0, this_rect.y0,
1777 this_rect.x1, this_rect.y1,
1778 setup->big_rect.x0, setup->big_rect.y0,
1779 setup->big_rect.x1, setup->big_rect.y1, bw);
1780 return 1;
1781 }
1783 NRRectL lo = this_rect;
1784 NRRectL hi = this_rect;
1786 /*
1787 This test determines the redraw strategy:
1789 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1790 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1791 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1792 and seems to be faster for drawings with many smaller objects at zoom-out.
1794 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1795 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1796 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1797 faster.
1799 The default for now is the strips mode.
1800 */
1801 if (bw < bh || bh < 2 * TILE_SIZE) {
1802 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1803 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1804 // Make sure that mid lies on a tile boundary
1805 mid = (mid / TILE_SIZE) * TILE_SIZE;
1807 lo.x1 = mid;
1808 hi.x0 = mid;
1810 if (setup->mouse_loc[NR::X] < mid) {
1811 // Always paint towards the mouse first
1812 return sp_canvas_paint_rect_internal(setup, lo)
1813 && sp_canvas_paint_rect_internal(setup, hi);
1814 } else {
1815 return sp_canvas_paint_rect_internal(setup, hi)
1816 && sp_canvas_paint_rect_internal(setup, lo);
1817 }
1818 } else {
1819 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1820 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1821 // Make sure that mid lies on a tile boundary
1822 mid = (mid / TILE_SIZE) * TILE_SIZE;
1824 lo.y1 = mid;
1825 hi.y0 = mid;
1827 if (setup->mouse_loc[NR::Y] < mid) {
1828 // Always paint towards the mouse first
1829 return sp_canvas_paint_rect_internal(setup, lo)
1830 && sp_canvas_paint_rect_internal(setup, hi);
1831 } else {
1832 return sp_canvas_paint_rect_internal(setup, hi)
1833 && sp_canvas_paint_rect_internal(setup, lo);
1834 }
1835 }
1836 }
1839 /**
1840 * Helper that draws a specific rectangular part of the canvas.
1841 *
1842 * @return true if the rectangle painting succeeds.
1843 */
1844 static bool
1845 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1846 {
1847 g_return_val_if_fail (!canvas->need_update, false);
1849 NRRectL rect;
1850 rect.x0 = xx0;
1851 rect.x1 = xx1;
1852 rect.y0 = yy0;
1853 rect.y1 = yy1;
1855 // Clip rect-to-draw by the current visible area
1856 rect.x0 = MAX (rect.x0, canvas->x0);
1857 rect.y0 = MAX (rect.y0, canvas->y0);
1858 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1859 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1861 #ifdef DEBUG_REDRAW
1862 // paint the area to redraw yellow
1863 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1864 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1865 canvas->pixmap_gc,
1866 TRUE,
1867 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1868 rect.x1 - rect.x0, rect.y1 - rect.y0);
1869 #endif
1871 PaintRectSetup setup;
1873 setup.canvas = canvas;
1874 setup.big_rect = rect;
1876 // Save the mouse location
1877 gint x, y;
1878 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1879 setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1881 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1882 // use 256K as a compromise to not slow down gradients
1883 // 256K is the cached buffer and we need 4 channels
1884 setup.max_pixels = 65536; // 256K/4
1885 } else {
1886 // paths only, so 1M works faster
1887 // 1M is the cached buffer and we need 4 channels
1888 setup.max_pixels = 262144;
1889 }
1891 // Start the clock
1892 g_get_current_time(&(setup.start_time));
1894 // Go
1895 return sp_canvas_paint_rect_internal(&setup, rect);
1896 }
1898 /**
1899 * Force a full redraw after a specified number of interrupted redraws
1900 */
1901 void
1902 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1903 g_return_if_fail(canvas != NULL);
1905 canvas->forced_redraw_limit = count;
1906 canvas->forced_redraw_count = 0;
1907 }
1909 /**
1910 * End forced full redraw requests
1911 */
1912 void
1913 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1914 g_return_if_fail(canvas != NULL);
1916 canvas->forced_redraw_limit = -1;
1917 }
1919 /**
1920 * The canvas widget's expose callback.
1921 */
1922 static gint
1923 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1924 {
1925 SPCanvas *canvas = SP_CANVAS (widget);
1927 if (!GTK_WIDGET_DRAWABLE (widget) ||
1928 (event->window != SP_CANVAS_WINDOW (canvas)))
1929 return FALSE;
1931 int n_rects;
1932 GdkRectangle *rects;
1933 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1935 for (int i = 0; i < n_rects; i++) {
1936 NRRectL rect;
1938 rect.x0 = rects[i].x + canvas->x0;
1939 rect.y0 = rects[i].y + canvas->y0;
1940 rect.x1 = rect.x0 + rects[i].width;
1941 rect.y1 = rect.y0 + rects[i].height;
1943 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1944 }
1946 if (n_rects > 0)
1947 g_free (rects);
1949 return FALSE;
1950 }
1952 /**
1953 * The canvas widget's keypress callback.
1954 */
1955 static gint
1956 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1957 {
1958 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1959 }
1961 /**
1962 * Crossing event handler for the canvas.
1963 */
1964 static gint
1965 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1966 {
1967 SPCanvas *canvas = SP_CANVAS (widget);
1969 if (event->window != SP_CANVAS_WINDOW (canvas))
1970 return FALSE;
1972 canvas->state = event->state;
1973 return pick_current_item (canvas, (GdkEvent *) event);
1974 }
1976 /**
1977 * Focus in handler for the canvas.
1978 */
1979 static gint
1980 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1981 {
1982 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1984 SPCanvas *canvas = SP_CANVAS (widget);
1986 if (canvas->focused_item) {
1987 return emit_event (canvas, (GdkEvent *) event);
1988 } else {
1989 return FALSE;
1990 }
1991 }
1993 /**
1994 * Focus out handler for the canvas.
1995 */
1996 static gint
1997 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1998 {
1999 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2001 SPCanvas *canvas = SP_CANVAS (widget);
2003 if (canvas->focused_item)
2004 return emit_event (canvas, (GdkEvent *) event);
2005 else
2006 return FALSE;
2007 }
2009 /**
2010 * Helper that repaints the areas in the canvas that need it.
2011 *
2012 * @return true if all the dirty parts have been redrawn
2013 */
2014 static int
2015 paint (SPCanvas *canvas)
2016 {
2017 if (canvas->need_update) {
2018 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2019 canvas->need_update = FALSE;
2020 }
2022 if (!canvas->need_redraw)
2023 return TRUE;
2025 Gdk::Region to_paint;
2027 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2028 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2029 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2031 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2032 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2033 TILE_SIZE, TILE_SIZE));
2034 }
2036 }
2037 }
2039 if (!to_paint.empty()) {
2040 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2041 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2042 for (Iter i=rect.begin(); i != rect.end(); ++i) {
2043 int x0 = (*i).get_x();
2044 int y0 = (*i).get_y();
2045 int x1 = x0 + (*i).get_width();
2046 int y1 = y0 + (*i).get_height();
2047 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2048 // Aborted
2049 return FALSE;
2050 };
2051 }
2052 }
2054 canvas->need_redraw = FALSE;
2056 // we've had a full unaborted redraw, reset the full redraw counter
2057 if (canvas->forced_redraw_limit != -1) {
2058 canvas->forced_redraw_count = 0;
2059 }
2061 return TRUE;
2062 }
2064 /**
2065 * Helper that invokes update, paint, and repick on canvas.
2066 */
2067 static int
2068 do_update (SPCanvas *canvas)
2069 {
2070 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2071 return TRUE;
2073 /* Cause the update if necessary */
2074 if (canvas->need_update) {
2075 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2076 canvas->need_update = FALSE;
2077 }
2079 /* Paint if able to */
2080 if (GTK_WIDGET_DRAWABLE (canvas)) {
2081 return paint (canvas);
2082 }
2084 /* Pick new current item */
2085 while (canvas->need_repick) {
2086 canvas->need_repick = FALSE;
2087 pick_current_item (canvas, &canvas->pick_event);
2088 }
2090 return TRUE;
2091 }
2093 /**
2094 * Idle handler for the canvas that deals with pending updates and redraws.
2095 */
2096 static gint
2097 idle_handler (gpointer data)
2098 {
2099 GDK_THREADS_ENTER ();
2101 SPCanvas *canvas = SP_CANVAS (data);
2103 int const ret = do_update (canvas);
2105 if (ret) {
2106 /* Reset idle id */
2107 canvas->idle_id = 0;
2108 }
2110 GDK_THREADS_LEAVE ();
2112 return !ret;
2113 }
2115 /**
2116 * Convenience function to add an idle handler to a canvas.
2117 */
2118 static void
2119 add_idle (SPCanvas *canvas)
2120 {
2121 if (canvas->idle_id != 0)
2122 return;
2124 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2125 }
2127 /**
2128 * Returns the root group of the specified canvas.
2129 */
2130 SPCanvasGroup *
2131 sp_canvas_root (SPCanvas *canvas)
2132 {
2133 g_return_val_if_fail (canvas != NULL, NULL);
2134 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2136 return SP_CANVAS_GROUP (canvas->root);
2137 }
2139 /**
2140 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2141 */
2142 void
2143 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2144 {
2145 g_return_if_fail (canvas != NULL);
2146 g_return_if_fail (SP_IS_CANVAS (canvas));
2148 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2149 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2150 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2151 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2153 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2154 canvas->dy0 = cy;
2155 canvas->x0 = ix;
2156 canvas->y0 = iy;
2158 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2160 if (!clear) {
2161 // scrolling without zoom; redraw only the newly exposed areas
2162 if ((dx != 0) || (dy != 0)) {
2163 canvas->is_scrolling = is_scrolling;
2164 if (GTK_WIDGET_REALIZED (canvas)) {
2165 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2166 }
2167 }
2168 } else {
2169 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2170 }
2171 }
2173 /**
2174 * Updates canvas if necessary.
2175 */
2176 void
2177 sp_canvas_update_now (SPCanvas *canvas)
2178 {
2179 g_return_if_fail (canvas != NULL);
2180 g_return_if_fail (SP_IS_CANVAS (canvas));
2182 if (!(canvas->need_update ||
2183 canvas->need_redraw))
2184 return;
2186 do_update (canvas);
2187 }
2189 /**
2190 * Update callback for canvas widget.
2191 */
2192 static void
2193 sp_canvas_request_update (SPCanvas *canvas)
2194 {
2195 canvas->need_update = TRUE;
2196 add_idle (canvas);
2197 }
2199 /**
2200 * Forces redraw of rectangular canvas area.
2201 */
2202 void
2203 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2204 {
2205 NRRectL bbox;
2206 NRRectL visible;
2207 NRRectL clip;
2209 g_return_if_fail (canvas != NULL);
2210 g_return_if_fail (SP_IS_CANVAS (canvas));
2212 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2213 if ((x0 >= x1) || (y0 >= y1)) return;
2215 bbox.x0 = x0;
2216 bbox.y0 = y0;
2217 bbox.x1 = x1;
2218 bbox.y1 = y1;
2220 visible.x0 = canvas->x0;
2221 visible.y0 = canvas->y0;
2222 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2223 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2225 nr_rect_l_intersect (&clip, &bbox, &visible);
2227 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2228 add_idle (canvas);
2229 }
2231 /**
2232 * Sets world coordinates from win and canvas.
2233 */
2234 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2235 {
2236 g_return_if_fail (canvas != NULL);
2237 g_return_if_fail (SP_IS_CANVAS (canvas));
2239 if (worldx) *worldx = canvas->x0 + winx;
2240 if (worldy) *worldy = canvas->y0 + winy;
2241 }
2243 /**
2244 * Sets win coordinates from world and canvas.
2245 */
2246 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2247 {
2248 g_return_if_fail (canvas != NULL);
2249 g_return_if_fail (SP_IS_CANVAS (canvas));
2251 if (winx) *winx = worldx - canvas->x0;
2252 if (winy) *winy = worldy - canvas->y0;
2253 }
2255 /**
2256 * Converts point from win to world coordinates.
2257 */
2258 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2259 {
2260 g_assert (canvas != NULL);
2261 g_assert (SP_IS_CANVAS (canvas));
2263 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2264 }
2266 /**
2267 * Converts point from world to win coordinates.
2268 */
2269 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2270 {
2271 g_assert (canvas != NULL);
2272 g_assert (SP_IS_CANVAS (canvas));
2274 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2275 }
2277 /**
2278 * Returns true if point given in world coordinates is inside window.
2279 */
2280 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2281 {
2282 g_assert( canvas != NULL );
2283 g_assert(SP_IS_CANVAS(canvas));
2285 using NR::X;
2286 using NR::Y;
2287 GtkWidget const &w = *GTK_WIDGET(canvas);
2288 return ( ( canvas->x0 <= world[X] ) &&
2289 ( canvas->y0 <= world[Y] ) &&
2290 ( world[X] < canvas->x0 + w.allocation.width ) &&
2291 ( world[Y] < canvas->y0 + w.allocation.height ) );
2292 }
2294 /**
2295 * Return canvas window coordinates as NR::Rect.
2296 */
2297 NR::Rect SPCanvas::getViewbox() const
2298 {
2299 GtkWidget const *w = GTK_WIDGET(this);
2300 return NR::Rect(NR::Point(dx0, dy0),
2301 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2302 }
2304 /**
2305 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2306 */
2307 NR::IRect SPCanvas::getViewboxIntegers() const
2308 {
2309 GtkWidget const *w = GTK_WIDGET(this);
2310 return NR::IRect(NR::IPoint(x0, y0),
2311 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2312 }
2314 inline int sp_canvas_tile_floor(int x)
2315 {
2316 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2317 }
2319 inline int sp_canvas_tile_ceil(int x)
2320 {
2321 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2322 }
2324 /**
2325 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2326 */
2327 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2328 {
2329 if ( nl >= nr || nt >= nb ) {
2330 if ( canvas->tiles ) g_free(canvas->tiles);
2331 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2332 canvas->tileH=canvas->tileV=0;
2333 canvas->tiles=NULL;
2334 return;
2335 }
2336 int tl=sp_canvas_tile_floor(nl);
2337 int tt=sp_canvas_tile_floor(nt);
2338 int tr=sp_canvas_tile_ceil(nr);
2339 int tb=sp_canvas_tile_ceil(nb);
2341 int nh = tr-tl, nv = tb-tt;
2342 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2343 for (int i=tl; i<tr; i++) {
2344 for (int j=tt; j<tb; j++) {
2345 int ind = (i-tl) + (j-tt)*nh;
2346 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2347 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2348 } else {
2349 ntiles[ind]=0; // newly exposed areas get 0
2350 }
2351 }
2352 }
2353 if ( canvas->tiles ) g_free(canvas->tiles);
2354 canvas->tiles=ntiles;
2355 canvas->tLeft=tl;
2356 canvas->tTop=tt;
2357 canvas->tRight=tr;
2358 canvas->tBottom=tb;
2359 canvas->tileH=nh;
2360 canvas->tileV=nv;
2361 }
2363 /*
2364 * Helper that queues a canvas rectangle for redraw
2365 */
2366 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2367 canvas->need_redraw = TRUE;
2369 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2370 }
2372 /**
2373 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2374 */
2375 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2376 {
2377 if ( nl >= nr || nt >= nb ) {
2378 return;
2379 }
2380 int tl=sp_canvas_tile_floor(nl);
2381 int tt=sp_canvas_tile_floor(nt);
2382 int tr=sp_canvas_tile_ceil(nr);
2383 int tb=sp_canvas_tile_ceil(nb);
2384 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2385 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2386 if ( tr > canvas->tRight ) tr=canvas->tRight;
2387 if ( tt < canvas->tTop ) tt=canvas->tTop;
2388 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2390 for (int i=tl; i<tr; i++) {
2391 for (int j=tt; j<tb; j++) {
2392 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2393 }
2394 }
2395 }
2398 /*
2399 Local Variables:
2400 mode:c++
2401 c-file-style:"stroustrup"
2402 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2403 indent-tabs-mode:nil
2404 fill-column:99
2405 End:
2406 */
2407 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :