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 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 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1552 static inline void request_motions(GdkWindow *, GdkEventMotion *event) {
1553 gdk_event_request_motions(event);
1554 }
1555 #else
1556 static inline void request_motions(GdkWindow *w, GdkEventMotion *) {
1557 gdk_window_get_pointer(w, NULL, NULL, NULL);
1558 }
1559 #endif
1561 /**
1562 * Motion event handler for the canvas.
1563 */
1564 static int
1565 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1566 {
1567 int status;
1568 SPCanvas *canvas = SP_CANVAS (widget);
1570 if (event->window != SP_CANVAS_WINDOW (canvas))
1571 return FALSE;
1573 if (canvas->pixmap_gc == NULL) // canvas being deleted
1574 return FALSE;
1576 canvas->state = event->state;
1577 pick_current_item (canvas, (GdkEvent *) event);
1579 status = emit_event (canvas, (GdkEvent *) event);
1581 if (event->is_hint) {
1582 request_motions(widget->window, event);
1583 }
1585 return status;
1586 }
1588 static void
1589 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)
1590 {
1591 GtkWidget *widget = GTK_WIDGET (canvas);
1593 SPCanvasBuf buf;
1594 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1595 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1596 } else {
1597 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1598 }
1600 // Mark the region clean
1601 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1603 buf.buf_rowstride = sw * 4;
1604 buf.rect.x0 = x0;
1605 buf.rect.y0 = y0;
1606 buf.rect.x1 = x1;
1607 buf.rect.y1 = y1;
1608 buf.visible_rect.x0 = draw_x1;
1609 buf.visible_rect.y0 = draw_y1;
1610 buf.visible_rect.x1 = draw_x2;
1611 buf.visible_rect.y1 = draw_y2;
1612 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1613 buf.bg_color = (((color->red & 0xff00) << 8)
1614 | (color->green & 0xff00)
1615 | (color->blue >> 8));
1616 buf.is_empty = true;
1618 buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1620 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1621 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1622 }
1624 #if ENABLE_LCMS
1625 cmsHTRANSFORM transf = 0;
1626 long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1627 if ( fromDisplay ) {
1628 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1629 } else {
1630 transf = Inkscape::colorprofile_get_display_transform();
1631 }
1632 #endif // ENABLE_LCMS
1634 if (buf.is_empty) {
1635 #if ENABLE_LCMS
1636 if ( transf && canvas->enable_cms_display_adj ) {
1637 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1638 }
1639 #endif // ENABLE_LCMS
1640 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1641 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1642 canvas->pixmap_gc,
1643 TRUE,
1644 x0 - canvas->x0, y0 - canvas->y0,
1645 x1 - x0, y1 - y0);
1646 } else {
1648 #if ENABLE_LCMS
1649 if ( transf && canvas->enable_cms_display_adj ) {
1650 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1651 guchar* p = buf.buf + (sw * 3) * yy;
1652 cmsDoTransform( transf, p, p, (x1 - x0) );
1653 }
1654 }
1655 #endif // ENABLE_LCMS
1657 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1658 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1659 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1660 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1661 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1662 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1664 ///#define CANVAS_OUTPUT_VIA_CAIRO
1666 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1668 buf.cst = cairo_image_surface_create_for_data (
1669 buf.buf,
1670 CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
1671 x1 - x0, y1 - y0,
1672 buf.buf_rowstride
1673 );
1674 cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1675 cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1676 cairo_paint (window_ct);
1677 cairo_destroy (window_ct);
1678 cairo_surface_finish (buf.cst);
1679 cairo_surface_destroy (buf.cst);
1681 #else
1683 NRPixBlock b3;
1684 nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1686 NRPixBlock b4;
1687 nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1688 buf.buf,
1689 buf.buf_rowstride,
1690 FALSE, FALSE);
1692 // this does the 32->24 squishing, using an assembler routine:
1693 nr_blit_pixblock_pixblock (&b3, &b4);
1695 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1696 canvas->pixmap_gc,
1697 x0 - canvas->x0, y0 - canvas->y0,
1698 x1 - x0, y1 - y0,
1699 GDK_RGB_DITHER_MAX,
1700 b3.data.px,
1701 sw * 3,
1702 x0 - canvas->x0, y0 - canvas->y0);
1704 nr_pixblock_release (&b3);
1705 nr_pixblock_release (&b4);
1706 #endif
1707 }
1709 cairo_surface_t *cst = cairo_get_target(buf.ct);
1710 cairo_destroy (buf.ct);
1711 cairo_surface_finish (cst);
1712 cairo_surface_destroy (cst);
1714 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1715 nr_pixelstore_256K_free (buf.buf);
1716 } else {
1717 nr_pixelstore_1M_free (buf.buf);
1718 }
1719 }
1721 struct PaintRectSetup {
1722 SPCanvas* canvas;
1723 NRRectL big_rect;
1724 GTimeVal start_time;
1725 int max_pixels;
1726 NR::Point mouse_loc;
1727 };
1729 /**
1730 * Paint the given rect, recursively subdividing the region until it is the size of a single
1731 * buffer.
1732 *
1733 * @return true if the drawing completes
1734 */
1735 static int
1736 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1737 {
1738 GTimeVal now;
1739 g_get_current_time (&now);
1741 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1742 + (now.tv_usec - setup->start_time.tv_usec);
1744 // Allow only very fast buffers to be run together;
1745 // as soon as the total redraw time exceeds 1ms, cancel;
1746 // this returns control to the idle loop and allows Inkscape to process user input
1747 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1748 // it will get back and finish painting what remains to paint.
1749 if (elapsed > 1000) {
1751 // Interrupting redraw isn't always good.
1752 // For example, when you drag one node of a big path, only the buffer containing
1753 // the mouse cursor will be redrawn again and again, and the rest of the path
1754 // will remain stale because Inkscape never has enough idle time to redraw all
1755 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1756 // If this limit is set, and if we have aborted redraw more times than is allowed,
1757 // interrupting is blocked and we're forced to redraw full screen once
1758 // (after which we can again interrupt forced_redraw_limit times).
1759 if (setup->canvas->forced_redraw_limit < 0 ||
1760 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1762 if (setup->canvas->forced_redraw_limit != -1) {
1763 setup->canvas->forced_redraw_count++;
1764 }
1766 return false;
1767 }
1768 }
1770 // Find the optimal buffer dimensions
1771 int bw = this_rect.x1 - this_rect.x0;
1772 int bh = this_rect.y1 - this_rect.y0;
1773 if ((bw < 1) || (bh < 1))
1774 return 0;
1776 if (bw * bh < setup->max_pixels) {
1777 // We are small enough
1778 sp_canvas_paint_single_buffer (setup->canvas,
1779 this_rect.x0, this_rect.y0,
1780 this_rect.x1, this_rect.y1,
1781 setup->big_rect.x0, setup->big_rect.y0,
1782 setup->big_rect.x1, setup->big_rect.y1, bw);
1783 return 1;
1784 }
1786 NRRectL lo = this_rect;
1787 NRRectL hi = this_rect;
1789 /*
1790 This test determines the redraw strategy:
1792 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1793 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1794 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1795 and seems to be faster for drawings with many smaller objects at zoom-out.
1797 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1798 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1799 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1800 faster.
1802 The default for now is the strips mode.
1803 */
1804 if (bw < bh || bh < 2 * TILE_SIZE) {
1805 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1806 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1807 // Make sure that mid lies on a tile boundary
1808 mid = (mid / TILE_SIZE) * TILE_SIZE;
1810 lo.x1 = mid;
1811 hi.x0 = mid;
1813 if (setup->mouse_loc[NR::X] < mid) {
1814 // Always paint towards the mouse first
1815 return sp_canvas_paint_rect_internal(setup, lo)
1816 && sp_canvas_paint_rect_internal(setup, hi);
1817 } else {
1818 return sp_canvas_paint_rect_internal(setup, hi)
1819 && sp_canvas_paint_rect_internal(setup, lo);
1820 }
1821 } else {
1822 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1823 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1824 // Make sure that mid lies on a tile boundary
1825 mid = (mid / TILE_SIZE) * TILE_SIZE;
1827 lo.y1 = mid;
1828 hi.y0 = mid;
1830 if (setup->mouse_loc[NR::Y] < mid) {
1831 // Always paint towards the mouse first
1832 return sp_canvas_paint_rect_internal(setup, lo)
1833 && sp_canvas_paint_rect_internal(setup, hi);
1834 } else {
1835 return sp_canvas_paint_rect_internal(setup, hi)
1836 && sp_canvas_paint_rect_internal(setup, lo);
1837 }
1838 }
1839 }
1842 /**
1843 * Helper that draws a specific rectangular part of the canvas.
1844 *
1845 * @return true if the rectangle painting succeeds.
1846 */
1847 static bool
1848 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1849 {
1850 g_return_val_if_fail (!canvas->need_update, false);
1852 NRRectL rect;
1853 rect.x0 = xx0;
1854 rect.x1 = xx1;
1855 rect.y0 = yy0;
1856 rect.y1 = yy1;
1858 // Clip rect-to-draw by the current visible area
1859 rect.x0 = MAX (rect.x0, canvas->x0);
1860 rect.y0 = MAX (rect.y0, canvas->y0);
1861 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1862 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1864 #ifdef DEBUG_REDRAW
1865 // paint the area to redraw yellow
1866 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1867 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1868 canvas->pixmap_gc,
1869 TRUE,
1870 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1871 rect.x1 - rect.x0, rect.y1 - rect.y0);
1872 #endif
1874 PaintRectSetup setup;
1876 setup.canvas = canvas;
1877 setup.big_rect = rect;
1879 // Save the mouse location
1880 gint x, y;
1881 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1882 setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1884 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1885 // use 256K as a compromise to not slow down gradients
1886 // 256K is the cached buffer and we need 4 channels
1887 setup.max_pixels = 65536; // 256K/4
1888 } else {
1889 // paths only, so 1M works faster
1890 // 1M is the cached buffer and we need 4 channels
1891 setup.max_pixels = 262144;
1892 }
1894 // Start the clock
1895 g_get_current_time(&(setup.start_time));
1897 // Go
1898 return sp_canvas_paint_rect_internal(&setup, rect);
1899 }
1901 /**
1902 * Force a full redraw after a specified number of interrupted redraws
1903 */
1904 void
1905 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1906 g_return_if_fail(canvas != NULL);
1908 canvas->forced_redraw_limit = count;
1909 canvas->forced_redraw_count = 0;
1910 }
1912 /**
1913 * End forced full redraw requests
1914 */
1915 void
1916 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1917 g_return_if_fail(canvas != NULL);
1919 canvas->forced_redraw_limit = -1;
1920 }
1922 /**
1923 * The canvas widget's expose callback.
1924 */
1925 static gint
1926 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1927 {
1928 SPCanvas *canvas = SP_CANVAS (widget);
1930 if (!GTK_WIDGET_DRAWABLE (widget) ||
1931 (event->window != SP_CANVAS_WINDOW (canvas)))
1932 return FALSE;
1934 int n_rects;
1935 GdkRectangle *rects;
1936 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1938 for (int i = 0; i < n_rects; i++) {
1939 NRRectL rect;
1941 rect.x0 = rects[i].x + canvas->x0;
1942 rect.y0 = rects[i].y + canvas->y0;
1943 rect.x1 = rect.x0 + rects[i].width;
1944 rect.y1 = rect.y0 + rects[i].height;
1946 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1947 }
1949 if (n_rects > 0)
1950 g_free (rects);
1952 return FALSE;
1953 }
1955 /**
1956 * The canvas widget's keypress callback.
1957 */
1958 static gint
1959 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1960 {
1961 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1962 }
1964 /**
1965 * Crossing event handler for the canvas.
1966 */
1967 static gint
1968 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1969 {
1970 SPCanvas *canvas = SP_CANVAS (widget);
1972 if (event->window != SP_CANVAS_WINDOW (canvas))
1973 return FALSE;
1975 canvas->state = event->state;
1976 return pick_current_item (canvas, (GdkEvent *) event);
1977 }
1979 /**
1980 * Focus in handler for the canvas.
1981 */
1982 static gint
1983 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1984 {
1985 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1987 SPCanvas *canvas = SP_CANVAS (widget);
1989 if (canvas->focused_item) {
1990 return emit_event (canvas, (GdkEvent *) event);
1991 } else {
1992 return FALSE;
1993 }
1994 }
1996 /**
1997 * Focus out handler for the canvas.
1998 */
1999 static gint
2000 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2001 {
2002 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2004 SPCanvas *canvas = SP_CANVAS (widget);
2006 if (canvas->focused_item)
2007 return emit_event (canvas, (GdkEvent *) event);
2008 else
2009 return FALSE;
2010 }
2012 /**
2013 * Helper that repaints the areas in the canvas that need it.
2014 *
2015 * @return true if all the dirty parts have been redrawn
2016 */
2017 static int
2018 paint (SPCanvas *canvas)
2019 {
2020 if (canvas->need_update) {
2021 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2022 canvas->need_update = FALSE;
2023 }
2025 if (!canvas->need_redraw)
2026 return TRUE;
2028 Gdk::Region to_paint;
2030 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2031 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2032 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2034 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2035 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2036 TILE_SIZE, TILE_SIZE));
2037 }
2039 }
2040 }
2042 if (!to_paint.empty()) {
2043 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2044 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2045 for (Iter i=rect.begin(); i != rect.end(); ++i) {
2046 int x0 = (*i).get_x();
2047 int y0 = (*i).get_y();
2048 int x1 = x0 + (*i).get_width();
2049 int y1 = y0 + (*i).get_height();
2050 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2051 // Aborted
2052 return FALSE;
2053 };
2054 }
2055 }
2057 canvas->need_redraw = FALSE;
2059 // we've had a full unaborted redraw, reset the full redraw counter
2060 if (canvas->forced_redraw_limit != -1) {
2061 canvas->forced_redraw_count = 0;
2062 }
2064 return TRUE;
2065 }
2067 /**
2068 * Helper that invokes update, paint, and repick on canvas.
2069 */
2070 static int
2071 do_update (SPCanvas *canvas)
2072 {
2073 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2074 return TRUE;
2076 /* Cause the update if necessary */
2077 if (canvas->need_update) {
2078 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2079 canvas->need_update = FALSE;
2080 }
2082 /* Paint if able to */
2083 if (GTK_WIDGET_DRAWABLE (canvas)) {
2084 return paint (canvas);
2085 }
2087 /* Pick new current item */
2088 while (canvas->need_repick) {
2089 canvas->need_repick = FALSE;
2090 pick_current_item (canvas, &canvas->pick_event);
2091 }
2093 return TRUE;
2094 }
2096 /**
2097 * Idle handler for the canvas that deals with pending updates and redraws.
2098 */
2099 static gint
2100 idle_handler (gpointer data)
2101 {
2102 GDK_THREADS_ENTER ();
2104 SPCanvas *canvas = SP_CANVAS (data);
2106 int const ret = do_update (canvas);
2108 if (ret) {
2109 /* Reset idle id */
2110 canvas->idle_id = 0;
2111 }
2113 GDK_THREADS_LEAVE ();
2115 return !ret;
2116 }
2118 /**
2119 * Convenience function to add an idle handler to a canvas.
2120 */
2121 static void
2122 add_idle (SPCanvas *canvas)
2123 {
2124 if (canvas->idle_id != 0)
2125 return;
2127 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2128 }
2130 /**
2131 * Returns the root group of the specified canvas.
2132 */
2133 SPCanvasGroup *
2134 sp_canvas_root (SPCanvas *canvas)
2135 {
2136 g_return_val_if_fail (canvas != NULL, NULL);
2137 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2139 return SP_CANVAS_GROUP (canvas->root);
2140 }
2142 /**
2143 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2144 */
2145 void
2146 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2147 {
2148 g_return_if_fail (canvas != NULL);
2149 g_return_if_fail (SP_IS_CANVAS (canvas));
2151 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2152 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2153 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2154 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2156 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2157 canvas->dy0 = cy;
2158 canvas->x0 = ix;
2159 canvas->y0 = iy;
2161 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2163 if (!clear) {
2164 // scrolling without zoom; redraw only the newly exposed areas
2165 if ((dx != 0) || (dy != 0)) {
2166 canvas->is_scrolling = is_scrolling;
2167 if (GTK_WIDGET_REALIZED (canvas)) {
2168 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2169 }
2170 }
2171 } else {
2172 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2173 }
2174 }
2176 /**
2177 * Updates canvas if necessary.
2178 */
2179 void
2180 sp_canvas_update_now (SPCanvas *canvas)
2181 {
2182 g_return_if_fail (canvas != NULL);
2183 g_return_if_fail (SP_IS_CANVAS (canvas));
2185 if (!(canvas->need_update ||
2186 canvas->need_redraw))
2187 return;
2189 do_update (canvas);
2190 }
2192 /**
2193 * Update callback for canvas widget.
2194 */
2195 static void
2196 sp_canvas_request_update (SPCanvas *canvas)
2197 {
2198 canvas->need_update = TRUE;
2199 add_idle (canvas);
2200 }
2202 /**
2203 * Forces redraw of rectangular canvas area.
2204 */
2205 void
2206 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2207 {
2208 NRRectL bbox;
2209 NRRectL visible;
2210 NRRectL clip;
2212 g_return_if_fail (canvas != NULL);
2213 g_return_if_fail (SP_IS_CANVAS (canvas));
2215 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2216 if ((x0 >= x1) || (y0 >= y1)) return;
2218 bbox.x0 = x0;
2219 bbox.y0 = y0;
2220 bbox.x1 = x1;
2221 bbox.y1 = y1;
2223 visible.x0 = canvas->x0;
2224 visible.y0 = canvas->y0;
2225 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2226 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2228 nr_rect_l_intersect (&clip, &bbox, &visible);
2230 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2231 add_idle (canvas);
2232 }
2234 /**
2235 * Sets world coordinates from win and canvas.
2236 */
2237 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2238 {
2239 g_return_if_fail (canvas != NULL);
2240 g_return_if_fail (SP_IS_CANVAS (canvas));
2242 if (worldx) *worldx = canvas->x0 + winx;
2243 if (worldy) *worldy = canvas->y0 + winy;
2244 }
2246 /**
2247 * Sets win coordinates from world and canvas.
2248 */
2249 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2250 {
2251 g_return_if_fail (canvas != NULL);
2252 g_return_if_fail (SP_IS_CANVAS (canvas));
2254 if (winx) *winx = worldx - canvas->x0;
2255 if (winy) *winy = worldy - canvas->y0;
2256 }
2258 /**
2259 * Converts point from win to world coordinates.
2260 */
2261 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2262 {
2263 g_assert (canvas != NULL);
2264 g_assert (SP_IS_CANVAS (canvas));
2266 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2267 }
2269 /**
2270 * Converts point from world to win coordinates.
2271 */
2272 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2273 {
2274 g_assert (canvas != NULL);
2275 g_assert (SP_IS_CANVAS (canvas));
2277 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2278 }
2280 /**
2281 * Returns true if point given in world coordinates is inside window.
2282 */
2283 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2284 {
2285 g_assert( canvas != NULL );
2286 g_assert(SP_IS_CANVAS(canvas));
2288 using NR::X;
2289 using NR::Y;
2290 GtkWidget const &w = *GTK_WIDGET(canvas);
2291 return ( ( canvas->x0 <= world[X] ) &&
2292 ( canvas->y0 <= world[Y] ) &&
2293 ( world[X] < canvas->x0 + w.allocation.width ) &&
2294 ( world[Y] < canvas->y0 + w.allocation.height ) );
2295 }
2297 /**
2298 * Return canvas window coordinates as NR::Rect.
2299 */
2300 NR::Rect SPCanvas::getViewbox() const
2301 {
2302 GtkWidget const *w = GTK_WIDGET(this);
2303 return NR::Rect(NR::Point(dx0, dy0),
2304 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2305 }
2307 /**
2308 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2309 */
2310 NR::IRect SPCanvas::getViewboxIntegers() const
2311 {
2312 GtkWidget const *w = GTK_WIDGET(this);
2313 return NR::IRect(NR::IPoint(x0, y0),
2314 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2315 }
2317 inline int sp_canvas_tile_floor(int x)
2318 {
2319 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2320 }
2322 inline int sp_canvas_tile_ceil(int x)
2323 {
2324 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2325 }
2327 /**
2328 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2329 */
2330 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2331 {
2332 if ( nl >= nr || nt >= nb ) {
2333 if ( canvas->tiles ) g_free(canvas->tiles);
2334 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2335 canvas->tileH=canvas->tileV=0;
2336 canvas->tiles=NULL;
2337 return;
2338 }
2339 int tl=sp_canvas_tile_floor(nl);
2340 int tt=sp_canvas_tile_floor(nt);
2341 int tr=sp_canvas_tile_ceil(nr);
2342 int tb=sp_canvas_tile_ceil(nb);
2344 int nh = tr-tl, nv = tb-tt;
2345 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2346 for (int i=tl; i<tr; i++) {
2347 for (int j=tt; j<tb; j++) {
2348 int ind = (i-tl) + (j-tt)*nh;
2349 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2350 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2351 } else {
2352 ntiles[ind]=0; // newly exposed areas get 0
2353 }
2354 }
2355 }
2356 if ( canvas->tiles ) g_free(canvas->tiles);
2357 canvas->tiles=ntiles;
2358 canvas->tLeft=tl;
2359 canvas->tTop=tt;
2360 canvas->tRight=tr;
2361 canvas->tBottom=tb;
2362 canvas->tileH=nh;
2363 canvas->tileV=nv;
2364 }
2366 /*
2367 * Helper that queues a canvas rectangle for redraw
2368 */
2369 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2370 canvas->need_redraw = TRUE;
2372 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2373 }
2375 /**
2376 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2377 */
2378 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2379 {
2380 if ( nl >= nr || nt >= nb ) {
2381 return;
2382 }
2383 int tl=sp_canvas_tile_floor(nl);
2384 int tt=sp_canvas_tile_floor(nt);
2385 int tr=sp_canvas_tile_ceil(nr);
2386 int tb=sp_canvas_tile_ceil(nb);
2387 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2388 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2389 if ( tr > canvas->tRight ) tr=canvas->tRight;
2390 if ( tt < canvas->tTop ) tt=canvas->tTop;
2391 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2393 for (int i=tl; i<tr; i++) {
2394 for (int j=tt; j<tb; j++) {
2395 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2396 }
2397 }
2398 }
2401 /*
2402 Local Variables:
2403 mode:c++
2404 c-file-style:"stroustrup"
2405 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2406 indent-tabs-mode:nil
2407 fill-column:99
2408 End:
2409 */
2410 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :