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 FALSE && 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 && ( event_mask & GDK_POINTER_MOTION_HINT_MASK )) {
543 event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
544 event_mask |= GDK_POINTER_MOTION_MASK;
545 }
547 /* fixme: Top hack (Lauris) */
548 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
549 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
550 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
551 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
552 NULL, cursor, etime);
554 item->canvas->grabbed_item = item;
555 item->canvas->grabbed_event_mask = event_mask;
556 item->canvas->current_item = item; /* So that events go to the grabbed item */
558 return 0;
559 }
561 /**
562 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
563 * mouse.
564 *
565 * \param item A canvas item that holds a grab.
566 * \param etime The timestamp for ungrabbing the mouse.
567 */
568 void
569 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
570 {
571 g_return_if_fail (item != NULL);
572 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
574 if (item->canvas->grabbed_item != item)
575 return;
577 item->canvas->grabbed_item = NULL;
579 gdk_pointer_ungrab (etime);
580 }
582 /**
583 * Returns the product of all transformation matrices from the root item down
584 * to the item.
585 */
586 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
587 {
588 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
590 NR::Matrix affine = NR::identity();
592 while (item) {
593 affine *= item->xform;
594 item = item->parent;
595 }
596 return affine;
597 }
599 /**
600 * Helper that returns true iff item is descendant of parent.
601 */
602 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
603 {
604 while (item) {
605 if (item == parent)
606 return true;
607 item = item->parent;
608 }
610 return false;
611 }
613 /**
614 * Focus canvas, and item under cursor if it is not already focussed.
615 */
616 void
617 sp_canvas_item_grab_focus (SPCanvasItem *item)
618 {
619 g_return_if_fail (item != NULL);
620 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
621 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
623 SPCanvasItem *focused_item = item->canvas->focused_item;
625 if (focused_item) {
626 GdkEvent ev;
627 ev.focus_change.type = GDK_FOCUS_CHANGE;
628 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
629 ev.focus_change.send_event = FALSE;
630 ev.focus_change.in = FALSE;
632 emit_event (item->canvas, &ev);
633 }
635 item->canvas->focused_item = item;
636 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
638 if (focused_item) {
639 GdkEvent ev;
640 ev.focus_change.type = GDK_FOCUS_CHANGE;
641 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
642 ev.focus_change.send_event = FALSE;
643 ev.focus_change.in = TRUE;
645 emit_event (item->canvas, &ev);
646 }
647 }
649 /**
650 * Requests that the canvas queue an update for the specified item.
651 *
652 * To be used only by item implementations.
653 */
654 void
655 sp_canvas_item_request_update (SPCanvasItem *item)
656 {
657 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
658 return;
660 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
662 if (item->parent != NULL) {
663 /* Recurse up the tree */
664 sp_canvas_item_request_update (item->parent);
665 } else {
666 /* Have reached the top of the tree, make sure the update call gets scheduled. */
667 sp_canvas_request_update (item->canvas);
668 }
669 }
671 /**
672 * Returns position of item in group.
673 */
674 gint sp_canvas_item_order (SPCanvasItem * item)
675 {
676 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
677 }
679 /* SPCanvasGroup */
681 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
682 static void sp_canvas_group_init (SPCanvasGroup *group);
683 static void sp_canvas_group_destroy (GtkObject *object);
685 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
686 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
687 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
689 static SPCanvasItemClass *group_parent_class;
691 /**
692 * Registers SPCanvasGroup class with Gtk and returns its type number.
693 */
694 GType sp_canvas_group_get_type(void)
695 {
696 static GType type = 0;
697 if (!type) {
698 GTypeInfo info = {
699 sizeof(SPCanvasGroupClass),
700 0, // base_init
701 0, // base_finalize
702 (GClassInitFunc)sp_canvas_group_class_init,
703 0, // class_finalize
704 0, // class_data
705 sizeof(SPCanvasGroup),
706 0, // n_preallocs
707 (GInstanceInitFunc)sp_canvas_group_init,
708 0 // value_table
709 };
710 type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
711 }
712 return type;
713 }
715 /**
716 * Class initialization function for SPCanvasGroupClass
717 */
718 static void
719 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
720 {
721 GtkObjectClass *object_class = (GtkObjectClass *) klass;
722 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
724 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
726 object_class->destroy = sp_canvas_group_destroy;
728 item_class->update = sp_canvas_group_update;
729 item_class->render = sp_canvas_group_render;
730 item_class->point = sp_canvas_group_point;
731 }
733 /**
734 * Callback. Empty.
735 */
736 static void
737 sp_canvas_group_init (SPCanvasGroup */*group*/)
738 {
739 /* Nothing here */
740 }
742 /**
743 * Callback that destroys all items in group and calls group's virtual
744 * destroy() function.
745 */
746 static void
747 sp_canvas_group_destroy (GtkObject *object)
748 {
749 g_return_if_fail (object != NULL);
750 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
752 SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
754 GList *list = group->items;
755 while (list) {
756 SPCanvasItem *child = (SPCanvasItem *)list->data;
757 list = list->next;
759 gtk_object_destroy (GTK_OBJECT (child));
760 }
762 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
763 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
764 }
766 /**
767 * Update handler for canvas groups
768 */
769 static void
770 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
771 {
772 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
773 NR::ConvexHull corners(NR::Point(0, 0));
774 bool empty=true;
776 for (GList *list = group->items; list; list = list->next) {
777 SPCanvasItem *i = (SPCanvasItem *)list->data;
779 sp_canvas_item_invoke_update (i, affine, flags);
781 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
782 if (empty) {
783 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
784 empty = false;
785 } else {
786 corners.add(NR::Point(i->x1, i->y1));
787 }
788 corners.add(NR::Point(i->x2, i->y2));
789 }
790 }
792 NR::Maybe<NR::Rect> const bounds = corners.bounds();
793 if (bounds) {
794 item->x1 = bounds->min()[NR::X];
795 item->y1 = bounds->min()[NR::Y];
796 item->x2 = bounds->max()[NR::X];
797 item->y2 = bounds->max()[NR::Y];
798 } else {
799 // FIXME ?
800 item->x1 = item->x2 = item->y1 = item->y2 = 0;
801 }
802 }
804 /**
805 * Point handler for canvas groups.
806 */
807 static double
808 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
809 {
810 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
811 double const x = p[NR::X];
812 double const y = p[NR::Y];
813 int x1 = (int)(x - item->canvas->close_enough);
814 int y1 = (int)(y - item->canvas->close_enough);
815 int x2 = (int)(x + item->canvas->close_enough);
816 int y2 = (int)(y + item->canvas->close_enough);
818 double best = 0.0;
819 *actual_item = NULL;
821 double dist = 0.0;
823 for (GList *list = group->items; list; list = list->next) {
824 SPCanvasItem *child = (SPCanvasItem *)list->data;
826 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
827 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
829 int has_point;
830 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
831 dist = sp_canvas_item_invoke_point (child, p, &point_item);
832 has_point = TRUE;
833 } else
834 has_point = FALSE;
836 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
837 best = dist;
838 *actual_item = point_item;
839 }
840 }
841 }
843 return best;
844 }
846 /**
847 * Renders all visible canvas group items in buf rectangle.
848 */
849 static void
850 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
851 {
852 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
854 for (GList *list = group->items; list; list = list->next) {
855 SPCanvasItem *child = (SPCanvasItem *)list->data;
856 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
857 if ((child->x1 < buf->rect.x1) &&
858 (child->y1 < buf->rect.y1) &&
859 (child->x2 > buf->rect.x0) &&
860 (child->y2 > buf->rect.y0)) {
861 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
862 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
863 }
864 }
865 }
866 }
868 /**
869 * Adds an item to a canvas group.
870 */
871 static void
872 group_add (SPCanvasGroup *group, SPCanvasItem *item)
873 {
874 gtk_object_ref (GTK_OBJECT (item));
875 gtk_object_sink (GTK_OBJECT (item));
877 if (!group->items) {
878 group->items = g_list_append (group->items, item);
879 group->last = group->items;
880 } else {
881 group->last = g_list_append (group->last, item)->next;
882 }
884 sp_canvas_item_request_update (item);
885 }
887 /**
888 * Removes an item from a canvas group
889 */
890 static void
891 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
892 {
893 g_return_if_fail (group != NULL);
894 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
895 g_return_if_fail (item != NULL);
897 for (GList *children = group->items; children; children = children->next) {
898 if (children->data == item) {
900 /* Unparent the child */
902 item->parent = NULL;
903 gtk_object_unref (GTK_OBJECT (item));
905 /* Remove it from the list */
907 if (children == group->last) group->last = children->prev;
909 group->items = g_list_remove_link (group->items, children);
910 g_list_free (children);
911 break;
912 }
913 }
914 }
916 /* SPCanvas */
918 static void sp_canvas_class_init (SPCanvasClass *klass);
919 static void sp_canvas_init (SPCanvas *canvas);
920 static void sp_canvas_destroy (GtkObject *object);
922 static void sp_canvas_realize (GtkWidget *widget);
923 static void sp_canvas_unrealize (GtkWidget *widget);
925 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
926 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
928 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
929 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
930 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
931 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
932 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
933 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
934 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
935 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
937 static GtkWidgetClass *canvas_parent_class;
939 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
940 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
941 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
942 static int do_update (SPCanvas *canvas);
944 /**
945 * Registers the SPCanvas class if necessary, and returns the type ID
946 * associated to it.
947 *
948 * \return The type ID of the SPCanvas class.
949 **/
950 GType sp_canvas_get_type(void)
951 {
952 static GType type = 0;
953 if (!type) {
954 GTypeInfo info = {
955 sizeof(SPCanvasClass),
956 0, // base_init
957 0, // base_finalize
958 (GClassInitFunc)sp_canvas_class_init,
959 0, // class_finalize
960 0, // class_data
961 sizeof(SPCanvas),
962 0, // n_preallocs
963 (GInstanceInitFunc)sp_canvas_init,
964 0 // value_table
965 };
966 type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
967 }
968 return type;
969 }
971 /**
972 * Class initialization function for SPCanvasClass.
973 */
974 static void
975 sp_canvas_class_init (SPCanvasClass *klass)
976 {
977 GtkObjectClass *object_class = (GtkObjectClass *) klass;
978 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
980 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
982 object_class->destroy = sp_canvas_destroy;
984 widget_class->realize = sp_canvas_realize;
985 widget_class->unrealize = sp_canvas_unrealize;
986 widget_class->size_request = sp_canvas_size_request;
987 widget_class->size_allocate = sp_canvas_size_allocate;
988 widget_class->button_press_event = sp_canvas_button;
989 widget_class->button_release_event = sp_canvas_button;
990 widget_class->motion_notify_event = sp_canvas_motion;
991 widget_class->scroll_event = sp_canvas_scroll;
992 widget_class->expose_event = sp_canvas_expose;
993 widget_class->key_press_event = sp_canvas_key;
994 widget_class->key_release_event = sp_canvas_key;
995 widget_class->enter_notify_event = sp_canvas_crossing;
996 widget_class->leave_notify_event = sp_canvas_crossing;
997 widget_class->focus_in_event = sp_canvas_focus_in;
998 widget_class->focus_out_event = sp_canvas_focus_out;
999 }
1001 /**
1002 * Callback: object initialization for SPCanvas.
1003 */
1004 static void
1005 sp_canvas_init (SPCanvas *canvas)
1006 {
1007 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1008 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1009 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1011 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1012 canvas->pick_event.crossing.x = 0;
1013 canvas->pick_event.crossing.y = 0;
1015 /* Create the root item as a special case */
1016 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1017 canvas->root->canvas = canvas;
1019 gtk_object_ref (GTK_OBJECT (canvas->root));
1020 gtk_object_sink (GTK_OBJECT (canvas->root));
1022 canvas->need_repick = TRUE;
1024 // See comment at in sp-canvas.h.
1025 canvas->gen_all_enter_events = false;
1027 canvas->tiles=NULL;
1028 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1029 canvas->tileH=canvas->tileV=0;
1031 canvas->forced_redraw_count = 0;
1032 canvas->forced_redraw_limit = -1;
1034 #if ENABLE_LCMS
1035 canvas->enable_cms_display_adj = false;
1036 canvas->cms_key = new Glib::ustring("");
1037 #endif // ENABLE_LCMS
1039 canvas->is_scrolling = false;
1041 }
1043 /**
1044 * Convenience function to remove the idle handler of a canvas.
1045 */
1046 static void
1047 remove_idle (SPCanvas *canvas)
1048 {
1049 if (canvas->idle_id) {
1050 gtk_idle_remove (canvas->idle_id);
1051 canvas->idle_id = 0;
1052 }
1053 }
1055 /*
1056 * Removes the transient state of the canvas (idle handler, grabs).
1057 */
1058 static void
1059 shutdown_transients (SPCanvas *canvas)
1060 {
1061 /* We turn off the need_redraw flag, since if the canvas is mapped again
1062 * it will request a redraw anyways. We do not turn off the need_update
1063 * flag, though, because updates are not queued when the canvas remaps
1064 * itself.
1065 */
1066 if (canvas->need_redraw) {
1067 canvas->need_redraw = FALSE;
1068 }
1069 if ( canvas->tiles ) g_free(canvas->tiles);
1070 canvas->tiles=NULL;
1071 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1072 canvas->tileH=canvas->tileV=0;
1074 if (canvas->grabbed_item) {
1075 canvas->grabbed_item = NULL;
1076 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1077 }
1079 remove_idle (canvas);
1080 }
1082 /**
1083 * Destroy handler for SPCanvas.
1084 */
1085 static void
1086 sp_canvas_destroy (GtkObject *object)
1087 {
1088 SPCanvas *canvas = SP_CANVAS (object);
1090 if (canvas->root) {
1091 gtk_object_unref (GTK_OBJECT (canvas->root));
1092 canvas->root = NULL;
1093 }
1095 shutdown_transients (canvas);
1097 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1098 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1099 }
1101 /**
1102 * Returns new canvas as widget.
1103 */
1104 GtkWidget *
1105 sp_canvas_new_aa (void)
1106 {
1107 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1109 return (GtkWidget *) canvas;
1110 }
1112 /**
1113 * The canvas widget's realize callback.
1114 */
1115 static void
1116 sp_canvas_realize (GtkWidget *widget)
1117 {
1118 SPCanvas *canvas = SP_CANVAS (widget);
1120 GdkWindowAttr attributes;
1121 attributes.window_type = GDK_WINDOW_CHILD;
1122 attributes.x = widget->allocation.x;
1123 attributes.y = widget->allocation.y;
1124 attributes.width = widget->allocation.width;
1125 attributes.height = widget->allocation.height;
1126 attributes.wclass = GDK_INPUT_OUTPUT;
1127 attributes.visual = gdk_rgb_get_visual ();
1128 attributes.colormap = gdk_rgb_get_cmap ();
1129 attributes.event_mask = (gtk_widget_get_events (widget) |
1130 GDK_EXPOSURE_MASK |
1131 GDK_BUTTON_PRESS_MASK |
1132 GDK_BUTTON_RELEASE_MASK |
1133 ( HAS_BROKEN_MOTION_HINTS ? GDK_POINTER_MOTION_MASK : GDK_POINTER_MOTION_HINT_MASK ) |
1134 GDK_PROXIMITY_IN_MASK |
1135 GDK_PROXIMITY_OUT_MASK |
1136 GDK_KEY_PRESS_MASK |
1137 GDK_KEY_RELEASE_MASK |
1138 GDK_ENTER_NOTIFY_MASK |
1139 GDK_LEAVE_NOTIFY_MASK |
1140 GDK_FOCUS_CHANGE_MASK);
1141 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1143 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1144 gdk_window_set_user_data (widget->window, widget);
1146 if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1147 gtk_widget_set_events(widget, attributes.event_mask);
1149 widget->style = gtk_style_attach (widget->style, widget->window);
1151 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1153 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1154 }
1156 /**
1157 * The canvas widget's unrealize callback.
1158 */
1159 static void
1160 sp_canvas_unrealize (GtkWidget *widget)
1161 {
1162 SPCanvas *canvas = SP_CANVAS (widget);
1164 canvas->current_item = NULL;
1165 canvas->grabbed_item = NULL;
1166 canvas->focused_item = NULL;
1168 shutdown_transients (canvas);
1170 gdk_gc_destroy (canvas->pixmap_gc);
1171 canvas->pixmap_gc = NULL;
1173 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1174 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1175 }
1177 /**
1178 * The canvas widget's size_request callback.
1179 */
1180 static void
1181 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1182 {
1183 static_cast<void>(SP_CANVAS (widget));
1185 req->width = 256;
1186 req->height = 256;
1187 }
1189 /**
1190 * The canvas widget's size_allocate callback.
1191 */
1192 static void
1193 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1194 {
1195 SPCanvas *canvas = SP_CANVAS (widget);
1197 /* Schedule redraw of new region */
1198 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1199 if (allocation->width > widget->allocation.width) {
1200 sp_canvas_request_redraw (canvas,
1201 canvas->x0 + widget->allocation.width,
1202 0,
1203 canvas->x0 + allocation->width,
1204 canvas->y0 + allocation->height);
1205 }
1206 if (allocation->height > widget->allocation.height) {
1207 sp_canvas_request_redraw (canvas,
1208 0,
1209 canvas->y0 + widget->allocation.height,
1210 canvas->x0 + allocation->width,
1211 canvas->y0 + allocation->height);
1212 }
1214 widget->allocation = *allocation;
1216 if (GTK_WIDGET_REALIZED (widget)) {
1217 gdk_window_move_resize (widget->window,
1218 widget->allocation.x, widget->allocation.y,
1219 widget->allocation.width, widget->allocation.height);
1220 }
1221 }
1223 /**
1224 * Helper that emits an event for an item in the canvas, be it the current
1225 * item, grabbed item, or focused item, as appropriate.
1226 */
1227 static int
1228 emit_event (SPCanvas *canvas, GdkEvent *event)
1229 {
1230 guint mask;
1232 if (canvas->grabbed_item) {
1233 switch (event->type) {
1234 case GDK_ENTER_NOTIFY:
1235 mask = GDK_ENTER_NOTIFY_MASK;
1236 break;
1237 case GDK_LEAVE_NOTIFY:
1238 mask = GDK_LEAVE_NOTIFY_MASK;
1239 break;
1240 case GDK_MOTION_NOTIFY:
1241 mask = GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
1242 break;
1243 case GDK_BUTTON_PRESS:
1244 case GDK_2BUTTON_PRESS:
1245 case GDK_3BUTTON_PRESS:
1246 mask = GDK_BUTTON_PRESS_MASK;
1247 break;
1248 case GDK_BUTTON_RELEASE:
1249 mask = GDK_BUTTON_RELEASE_MASK;
1250 break;
1251 case GDK_KEY_PRESS:
1252 mask = GDK_KEY_PRESS_MASK;
1253 break;
1254 case GDK_KEY_RELEASE:
1255 mask = GDK_KEY_RELEASE_MASK;
1256 break;
1257 case GDK_SCROLL:
1258 mask = GDK_SCROLL;
1259 break;
1260 default:
1261 mask = 0;
1262 break;
1263 }
1265 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1266 }
1268 /* Convert to world coordinates -- we have two cases because of diferent
1269 * offsets of the fields in the event structures.
1270 */
1272 GdkEvent ev = *event;
1274 switch (ev.type) {
1275 case GDK_ENTER_NOTIFY:
1276 case GDK_LEAVE_NOTIFY:
1277 ev.crossing.x += canvas->x0;
1278 ev.crossing.y += canvas->y0;
1279 break;
1280 case GDK_MOTION_NOTIFY:
1281 case GDK_BUTTON_PRESS:
1282 case GDK_2BUTTON_PRESS:
1283 case GDK_3BUTTON_PRESS:
1284 case GDK_BUTTON_RELEASE:
1285 ev.motion.x += canvas->x0;
1286 ev.motion.y += canvas->y0;
1287 break;
1288 default:
1289 break;
1290 }
1292 /* Choose where we send the event */
1294 /* canvas->current_item becomes NULL in some cases under Win32
1295 ** (e.g. if the pointer leaves the window). So this is a hack that
1296 ** Lauris applied to SP to get around the problem.
1297 */
1298 SPCanvasItem* item = NULL;
1299 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1300 item = canvas->grabbed_item;
1301 } else {
1302 item = canvas->current_item;
1303 }
1305 if (canvas->focused_item &&
1306 ((event->type == GDK_KEY_PRESS) ||
1307 (event->type == GDK_KEY_RELEASE) ||
1308 (event->type == GDK_FOCUS_CHANGE))) {
1309 item = canvas->focused_item;
1310 }
1312 /* The event is propagated up the hierarchy (for if someone connected to
1313 * a group instead of a leaf event), and emission is stopped if a
1314 * handler returns TRUE, just like for GtkWidget events.
1315 */
1317 gint finished = FALSE;
1319 while (item && !finished) {
1320 gtk_object_ref (GTK_OBJECT (item));
1321 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1322 SPCanvasItem *parent = item->parent;
1323 gtk_object_unref (GTK_OBJECT (item));
1324 item = parent;
1325 }
1327 return finished;
1328 }
1330 /**
1331 * Helper that re-picks the current item in the canvas, based on the event's
1332 * coordinates and emits enter/leave events for items as appropriate.
1333 */
1334 static int
1335 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1336 {
1337 int button_down = 0;
1338 double x, y;
1340 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1341 return FALSE;
1343 int retval = FALSE;
1345 if (canvas->gen_all_enter_events == false) {
1346 // If a button is down, we'll perform enter and leave events on the
1347 // current item, but not enter on any other item. This is more or
1348 // less like X pointer grabbing for canvas items.
1349 //
1350 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1351 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1353 if (!button_down) canvas->left_grabbed_item = FALSE;
1354 }
1356 /* Save the event in the canvas. This is used to synthesize enter and
1357 * leave events in case the current item changes. It is also used to
1358 * re-pick the current item if the current one gets deleted. Also,
1359 * synthesize an enter event.
1360 */
1361 if (event != &canvas->pick_event) {
1362 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1363 /* these fields have the same offsets in both types of events */
1365 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1366 canvas->pick_event.crossing.window = event->motion.window;
1367 canvas->pick_event.crossing.send_event = event->motion.send_event;
1368 canvas->pick_event.crossing.subwindow = NULL;
1369 canvas->pick_event.crossing.x = event->motion.x;
1370 canvas->pick_event.crossing.y = event->motion.y;
1371 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1372 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1373 canvas->pick_event.crossing.focus = FALSE;
1374 canvas->pick_event.crossing.state = event->motion.state;
1376 /* these fields don't have the same offsets in both types of events */
1378 if (event->type == GDK_MOTION_NOTIFY) {
1379 canvas->pick_event.crossing.x_root = event->motion.x_root;
1380 canvas->pick_event.crossing.y_root = event->motion.y_root;
1381 } else {
1382 canvas->pick_event.crossing.x_root = event->button.x_root;
1383 canvas->pick_event.crossing.y_root = event->button.y_root;
1384 }
1385 } else {
1386 canvas->pick_event = *event;
1387 }
1388 }
1390 /* Don't do anything else if this is a recursive call */
1391 if (canvas->in_repick) return retval;
1393 /* LeaveNotify means that there is no current item, so we don't look for one */
1394 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1395 /* these fields don't have the same offsets in both types of events */
1397 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1398 x = canvas->pick_event.crossing.x;
1399 y = canvas->pick_event.crossing.y;
1400 } else {
1401 x = canvas->pick_event.motion.x;
1402 y = canvas->pick_event.motion.y;
1403 }
1405 /* world coords */
1406 x += canvas->x0;
1407 y += canvas->y0;
1409 /* find the closest item */
1410 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1411 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1412 } else {
1413 canvas->new_current_item = NULL;
1414 }
1415 } else {
1416 canvas->new_current_item = NULL;
1417 }
1419 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1420 return retval; /* current item did not change */
1421 }
1423 /* Synthesize events for old and new current items */
1425 if ((canvas->new_current_item != canvas->current_item)
1426 && (canvas->current_item != NULL)
1427 && !canvas->left_grabbed_item) {
1428 GdkEvent new_event;
1429 SPCanvasItem *item;
1431 item = canvas->current_item;
1433 new_event = canvas->pick_event;
1434 new_event.type = GDK_LEAVE_NOTIFY;
1436 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1437 new_event.crossing.subwindow = NULL;
1438 canvas->in_repick = TRUE;
1439 retval = emit_event (canvas, &new_event);
1440 canvas->in_repick = FALSE;
1441 }
1443 if (canvas->gen_all_enter_events == false) {
1444 // new_current_item may have been set to NULL during the call to
1445 // emit_event() above
1446 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1447 canvas->left_grabbed_item = TRUE;
1448 return retval;
1449 }
1450 }
1452 /* Handle the rest of cases */
1454 canvas->left_grabbed_item = FALSE;
1455 canvas->current_item = canvas->new_current_item;
1457 if (canvas->current_item != NULL) {
1458 GdkEvent new_event;
1460 new_event = canvas->pick_event;
1461 new_event.type = GDK_ENTER_NOTIFY;
1462 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1463 new_event.crossing.subwindow = NULL;
1464 retval = emit_event (canvas, &new_event);
1465 }
1467 return retval;
1468 }
1470 /**
1471 * Button event handler for the canvas.
1472 */
1473 static gint
1474 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1475 {
1476 SPCanvas *canvas = SP_CANVAS (widget);
1478 int retval = FALSE;
1480 /* dispatch normally regardless of the event's window if an item has
1481 has a pointer grab in effect */
1482 if (!canvas->grabbed_item &&
1483 event->window != SP_CANVAS_WINDOW (canvas))
1484 return retval;
1486 int mask;
1487 switch (event->button) {
1488 case 1:
1489 mask = GDK_BUTTON1_MASK;
1490 break;
1491 case 2:
1492 mask = GDK_BUTTON2_MASK;
1493 break;
1494 case 3:
1495 mask = GDK_BUTTON3_MASK;
1496 break;
1497 case 4:
1498 mask = GDK_BUTTON4_MASK;
1499 break;
1500 case 5:
1501 mask = GDK_BUTTON5_MASK;
1502 break;
1503 default:
1504 mask = 0;
1505 }
1507 switch (event->type) {
1508 case GDK_BUTTON_PRESS:
1509 case GDK_2BUTTON_PRESS:
1510 case GDK_3BUTTON_PRESS:
1511 /* Pick the current item as if the button were not pressed, and
1512 * then process the event.
1513 */
1514 canvas->state = event->state;
1515 pick_current_item (canvas, (GdkEvent *) event);
1516 canvas->state ^= mask;
1517 retval = emit_event (canvas, (GdkEvent *) event);
1518 break;
1520 case GDK_BUTTON_RELEASE:
1521 /* Process the event as if the button were pressed, then repick
1522 * after the button has been released
1523 */
1524 canvas->state = event->state;
1525 retval = emit_event (canvas, (GdkEvent *) event);
1526 event->state ^= mask;
1527 canvas->state = event->state;
1528 pick_current_item (canvas, (GdkEvent *) event);
1529 event->state ^= mask;
1530 break;
1532 default:
1533 g_assert_not_reached ();
1534 }
1536 return retval;
1537 }
1539 /**
1540 * Scroll event handler for the canvas.
1541 *
1542 * \todo FIXME: generate motion events to re-select items.
1543 */
1544 static gint
1545 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1546 {
1547 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1548 }
1550 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1551 static inline void request_motions(GdkWindow *, GdkEventMotion *event) {
1552 gdk_event_request_motions(event);
1553 }
1554 #else
1555 static inline void request_motions(GdkWindow *w, GdkEventMotion *) {
1556 gdk_window_get_pointer(w, NULL, NULL, NULL);
1557 }
1558 #endif
1560 /**
1561 * Motion event handler for the canvas.
1562 */
1563 static int
1564 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1565 {
1566 int status;
1567 SPCanvas *canvas = SP_CANVAS (widget);
1569 if (event->window != SP_CANVAS_WINDOW (canvas))
1570 return FALSE;
1572 if (canvas->pixmap_gc == NULL) // canvas being deleted
1573 return FALSE;
1575 canvas->state = event->state;
1576 pick_current_item (canvas, (GdkEvent *) event);
1578 status = emit_event (canvas, (GdkEvent *) event);
1580 if (event->is_hint) {
1581 request_motions(widget->window, event);
1582 }
1584 return status;
1585 }
1587 static void
1588 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)
1589 {
1590 GtkWidget *widget = GTK_WIDGET (canvas);
1592 SPCanvasBuf buf;
1593 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1594 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1595 } else {
1596 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1597 }
1599 // Mark the region clean
1600 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1602 buf.buf_rowstride = sw * 4;
1603 buf.rect.x0 = x0;
1604 buf.rect.y0 = y0;
1605 buf.rect.x1 = x1;
1606 buf.rect.y1 = y1;
1607 buf.visible_rect.x0 = draw_x1;
1608 buf.visible_rect.y0 = draw_y1;
1609 buf.visible_rect.x1 = draw_x2;
1610 buf.visible_rect.y1 = draw_y2;
1611 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1612 buf.bg_color = (((color->red & 0xff00) << 8)
1613 | (color->green & 0xff00)
1614 | (color->blue >> 8));
1615 buf.is_empty = true;
1617 buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1619 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1620 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1621 }
1623 #if ENABLE_LCMS
1624 cmsHTRANSFORM transf = 0;
1625 long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1626 if ( fromDisplay ) {
1627 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1628 } else {
1629 transf = Inkscape::colorprofile_get_display_transform();
1630 }
1631 #endif // ENABLE_LCMS
1633 if (buf.is_empty) {
1634 #if ENABLE_LCMS
1635 if ( transf && canvas->enable_cms_display_adj ) {
1636 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1637 }
1638 #endif // ENABLE_LCMS
1639 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1640 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1641 canvas->pixmap_gc,
1642 TRUE,
1643 x0 - canvas->x0, y0 - canvas->y0,
1644 x1 - x0, y1 - y0);
1645 } else {
1647 #if ENABLE_LCMS
1648 if ( transf && canvas->enable_cms_display_adj ) {
1649 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1650 guchar* p = buf.buf + (sw * 3) * yy;
1651 cmsDoTransform( transf, p, p, (x1 - x0) );
1652 }
1653 }
1654 #endif // ENABLE_LCMS
1656 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1657 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1658 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1659 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1660 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1661 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1663 ///#define CANVAS_OUTPUT_VIA_CAIRO
1665 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1667 buf.cst = cairo_image_surface_create_for_data (
1668 buf.buf,
1669 CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
1670 x1 - x0, y1 - y0,
1671 buf.buf_rowstride
1672 );
1673 cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1674 cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1675 cairo_paint (window_ct);
1676 cairo_destroy (window_ct);
1677 cairo_surface_finish (buf.cst);
1678 cairo_surface_destroy (buf.cst);
1680 #else
1682 NRPixBlock b3;
1683 nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1685 NRPixBlock b4;
1686 nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1687 buf.buf,
1688 buf.buf_rowstride,
1689 FALSE, FALSE);
1691 // this does the 32->24 squishing, using an assembler routine:
1692 nr_blit_pixblock_pixblock (&b3, &b4);
1694 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1695 canvas->pixmap_gc,
1696 x0 - canvas->x0, y0 - canvas->y0,
1697 x1 - x0, y1 - y0,
1698 GDK_RGB_DITHER_MAX,
1699 b3.data.px,
1700 sw * 3,
1701 x0 - canvas->x0, y0 - canvas->y0);
1703 nr_pixblock_release (&b3);
1704 nr_pixblock_release (&b4);
1705 #endif
1706 }
1708 cairo_surface_t *cst = cairo_get_target(buf.ct);
1709 cairo_destroy (buf.ct);
1710 cairo_surface_finish (cst);
1711 cairo_surface_destroy (cst);
1713 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1714 nr_pixelstore_256K_free (buf.buf);
1715 } else {
1716 nr_pixelstore_1M_free (buf.buf);
1717 }
1718 }
1720 struct PaintRectSetup {
1721 SPCanvas* canvas;
1722 NRRectL big_rect;
1723 GTimeVal start_time;
1724 int max_pixels;
1725 NR::Point mouse_loc;
1726 };
1728 /**
1729 * Paint the given rect, recursively subdividing the region until it is the size of a single
1730 * buffer.
1731 *
1732 * @return true if the drawing completes
1733 */
1734 static int
1735 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1736 {
1737 GTimeVal now;
1738 g_get_current_time (&now);
1740 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1741 + (now.tv_usec - setup->start_time.tv_usec);
1743 // Allow only very fast buffers to be run together;
1744 // as soon as the total redraw time exceeds 1ms, cancel;
1745 // this returns control to the idle loop and allows Inkscape to process user input
1746 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1747 // it will get back and finish painting what remains to paint.
1748 if (elapsed > 1000) {
1750 // Interrupting redraw isn't always good.
1751 // For example, when you drag one node of a big path, only the buffer containing
1752 // the mouse cursor will be redrawn again and again, and the rest of the path
1753 // will remain stale because Inkscape never has enough idle time to redraw all
1754 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1755 // If this limit is set, and if we have aborted redraw more times than is allowed,
1756 // interrupting is blocked and we're forced to redraw full screen once
1757 // (after which we can again interrupt forced_redraw_limit times).
1758 if (setup->canvas->forced_redraw_limit < 0 ||
1759 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1761 if (setup->canvas->forced_redraw_limit != -1) {
1762 setup->canvas->forced_redraw_count++;
1763 }
1765 return false;
1766 }
1767 }
1769 // Find the optimal buffer dimensions
1770 int bw = this_rect.x1 - this_rect.x0;
1771 int bh = this_rect.y1 - this_rect.y0;
1772 if ((bw < 1) || (bh < 1))
1773 return 0;
1775 if (bw * bh < setup->max_pixels) {
1776 // We are small enough
1777 sp_canvas_paint_single_buffer (setup->canvas,
1778 this_rect.x0, this_rect.y0,
1779 this_rect.x1, this_rect.y1,
1780 setup->big_rect.x0, setup->big_rect.y0,
1781 setup->big_rect.x1, setup->big_rect.y1, bw);
1782 return 1;
1783 }
1785 NRRectL lo = this_rect;
1786 NRRectL hi = this_rect;
1788 /*
1789 This test determines the redraw strategy:
1791 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1792 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1793 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1794 and seems to be faster for drawings with many smaller objects at zoom-out.
1796 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1797 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1798 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1799 faster.
1801 The default for now is the strips mode.
1802 */
1803 if (bw < bh || bh < 2 * TILE_SIZE) {
1804 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1805 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1806 // Make sure that mid lies on a tile boundary
1807 mid = (mid / TILE_SIZE) * TILE_SIZE;
1809 lo.x1 = mid;
1810 hi.x0 = mid;
1812 if (setup->mouse_loc[NR::X] < mid) {
1813 // Always paint towards the mouse first
1814 return sp_canvas_paint_rect_internal(setup, lo)
1815 && sp_canvas_paint_rect_internal(setup, hi);
1816 } else {
1817 return sp_canvas_paint_rect_internal(setup, hi)
1818 && sp_canvas_paint_rect_internal(setup, lo);
1819 }
1820 } else {
1821 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1822 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1823 // Make sure that mid lies on a tile boundary
1824 mid = (mid / TILE_SIZE) * TILE_SIZE;
1826 lo.y1 = mid;
1827 hi.y0 = mid;
1829 if (setup->mouse_loc[NR::Y] < mid) {
1830 // Always paint towards the mouse first
1831 return sp_canvas_paint_rect_internal(setup, lo)
1832 && sp_canvas_paint_rect_internal(setup, hi);
1833 } else {
1834 return sp_canvas_paint_rect_internal(setup, hi)
1835 && sp_canvas_paint_rect_internal(setup, lo);
1836 }
1837 }
1838 }
1841 /**
1842 * Helper that draws a specific rectangular part of the canvas.
1843 *
1844 * @return true if the rectangle painting succeeds.
1845 */
1846 static bool
1847 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1848 {
1849 g_return_val_if_fail (!canvas->need_update, false);
1851 NRRectL rect;
1852 rect.x0 = xx0;
1853 rect.x1 = xx1;
1854 rect.y0 = yy0;
1855 rect.y1 = yy1;
1857 // Clip rect-to-draw by the current visible area
1858 rect.x0 = MAX (rect.x0, canvas->x0);
1859 rect.y0 = MAX (rect.y0, canvas->y0);
1860 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1861 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1863 #ifdef DEBUG_REDRAW
1864 // paint the area to redraw yellow
1865 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1866 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1867 canvas->pixmap_gc,
1868 TRUE,
1869 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1870 rect.x1 - rect.x0, rect.y1 - rect.y0);
1871 #endif
1873 PaintRectSetup setup;
1875 setup.canvas = canvas;
1876 setup.big_rect = rect;
1878 // Save the mouse location
1879 gint x, y;
1880 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1881 setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1883 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1884 // use 256K as a compromise to not slow down gradients
1885 // 256K is the cached buffer and we need 4 channels
1886 setup.max_pixels = 65536; // 256K/4
1887 } else {
1888 // paths only, so 1M works faster
1889 // 1M is the cached buffer and we need 4 channels
1890 setup.max_pixels = 262144;
1891 }
1893 // Start the clock
1894 g_get_current_time(&(setup.start_time));
1896 // Go
1897 return sp_canvas_paint_rect_internal(&setup, rect);
1898 }
1900 /**
1901 * Force a full redraw after a specified number of interrupted redraws
1902 */
1903 void
1904 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1905 g_return_if_fail(canvas != NULL);
1907 canvas->forced_redraw_limit = count;
1908 canvas->forced_redraw_count = 0;
1909 }
1911 /**
1912 * End forced full redraw requests
1913 */
1914 void
1915 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1916 g_return_if_fail(canvas != NULL);
1918 canvas->forced_redraw_limit = -1;
1919 }
1921 /**
1922 * The canvas widget's expose callback.
1923 */
1924 static gint
1925 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1926 {
1927 SPCanvas *canvas = SP_CANVAS (widget);
1929 if (!GTK_WIDGET_DRAWABLE (widget) ||
1930 (event->window != SP_CANVAS_WINDOW (canvas)))
1931 return FALSE;
1933 int n_rects;
1934 GdkRectangle *rects;
1935 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1937 for (int i = 0; i < n_rects; i++) {
1938 NRRectL rect;
1940 rect.x0 = rects[i].x + canvas->x0;
1941 rect.y0 = rects[i].y + canvas->y0;
1942 rect.x1 = rect.x0 + rects[i].width;
1943 rect.y1 = rect.y0 + rects[i].height;
1945 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1946 }
1948 if (n_rects > 0)
1949 g_free (rects);
1951 return FALSE;
1952 }
1954 /**
1955 * The canvas widget's keypress callback.
1956 */
1957 static gint
1958 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1959 {
1960 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1961 }
1963 /**
1964 * Crossing event handler for the canvas.
1965 */
1966 static gint
1967 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1968 {
1969 SPCanvas *canvas = SP_CANVAS (widget);
1971 if (event->window != SP_CANVAS_WINDOW (canvas))
1972 return FALSE;
1974 canvas->state = event->state;
1975 return pick_current_item (canvas, (GdkEvent *) event);
1976 }
1978 /**
1979 * Focus in handler for the canvas.
1980 */
1981 static gint
1982 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1983 {
1984 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1986 SPCanvas *canvas = SP_CANVAS (widget);
1988 if (canvas->focused_item) {
1989 return emit_event (canvas, (GdkEvent *) event);
1990 } else {
1991 return FALSE;
1992 }
1993 }
1995 /**
1996 * Focus out handler for the canvas.
1997 */
1998 static gint
1999 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2000 {
2001 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2003 SPCanvas *canvas = SP_CANVAS (widget);
2005 if (canvas->focused_item)
2006 return emit_event (canvas, (GdkEvent *) event);
2007 else
2008 return FALSE;
2009 }
2011 /**
2012 * Helper that repaints the areas in the canvas that need it.
2013 *
2014 * @return true if all the dirty parts have been redrawn
2015 */
2016 static int
2017 paint (SPCanvas *canvas)
2018 {
2019 if (canvas->need_update) {
2020 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2021 canvas->need_update = FALSE;
2022 }
2024 if (!canvas->need_redraw)
2025 return TRUE;
2027 Gdk::Region to_paint;
2029 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2030 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2031 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2033 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2034 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2035 TILE_SIZE, TILE_SIZE));
2036 }
2038 }
2039 }
2041 if (!to_paint.empty()) {
2042 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2043 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2044 for (Iter i=rect.begin(); i != rect.end(); ++i) {
2045 int x0 = (*i).get_x();
2046 int y0 = (*i).get_y();
2047 int x1 = x0 + (*i).get_width();
2048 int y1 = y0 + (*i).get_height();
2049 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2050 // Aborted
2051 return FALSE;
2052 };
2053 }
2054 }
2056 canvas->need_redraw = FALSE;
2058 // we've had a full unaborted redraw, reset the full redraw counter
2059 if (canvas->forced_redraw_limit != -1) {
2060 canvas->forced_redraw_count = 0;
2061 }
2063 return TRUE;
2064 }
2066 /**
2067 * Helper that invokes update, paint, and repick on canvas.
2068 */
2069 static int
2070 do_update (SPCanvas *canvas)
2071 {
2072 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2073 return TRUE;
2075 /* Cause the update if necessary */
2076 if (canvas->need_update) {
2077 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2078 canvas->need_update = FALSE;
2079 }
2081 /* Paint if able to */
2082 if (GTK_WIDGET_DRAWABLE (canvas)) {
2083 return paint (canvas);
2084 }
2086 /* Pick new current item */
2087 while (canvas->need_repick) {
2088 canvas->need_repick = FALSE;
2089 pick_current_item (canvas, &canvas->pick_event);
2090 }
2092 return TRUE;
2093 }
2095 /**
2096 * Idle handler for the canvas that deals with pending updates and redraws.
2097 */
2098 static gint
2099 idle_handler (gpointer data)
2100 {
2101 GDK_THREADS_ENTER ();
2103 SPCanvas *canvas = SP_CANVAS (data);
2105 int const ret = do_update (canvas);
2107 if (ret) {
2108 /* Reset idle id */
2109 canvas->idle_id = 0;
2110 }
2112 GDK_THREADS_LEAVE ();
2114 return !ret;
2115 }
2117 /**
2118 * Convenience function to add an idle handler to a canvas.
2119 */
2120 static void
2121 add_idle (SPCanvas *canvas)
2122 {
2123 if (canvas->idle_id != 0)
2124 return;
2126 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2127 }
2129 /**
2130 * Returns the root group of the specified canvas.
2131 */
2132 SPCanvasGroup *
2133 sp_canvas_root (SPCanvas *canvas)
2134 {
2135 g_return_val_if_fail (canvas != NULL, NULL);
2136 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2138 return SP_CANVAS_GROUP (canvas->root);
2139 }
2141 /**
2142 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2143 */
2144 void
2145 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2146 {
2147 g_return_if_fail (canvas != NULL);
2148 g_return_if_fail (SP_IS_CANVAS (canvas));
2150 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2151 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2152 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2153 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2155 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2156 canvas->dy0 = cy;
2157 canvas->x0 = ix;
2158 canvas->y0 = iy;
2160 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2162 if (!clear) {
2163 // scrolling without zoom; redraw only the newly exposed areas
2164 if ((dx != 0) || (dy != 0)) {
2165 canvas->is_scrolling = is_scrolling;
2166 if (GTK_WIDGET_REALIZED (canvas)) {
2167 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2168 }
2169 }
2170 } else {
2171 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2172 }
2173 }
2175 /**
2176 * Updates canvas if necessary.
2177 */
2178 void
2179 sp_canvas_update_now (SPCanvas *canvas)
2180 {
2181 g_return_if_fail (canvas != NULL);
2182 g_return_if_fail (SP_IS_CANVAS (canvas));
2184 if (!(canvas->need_update ||
2185 canvas->need_redraw))
2186 return;
2188 do_update (canvas);
2189 }
2191 /**
2192 * Update callback for canvas widget.
2193 */
2194 static void
2195 sp_canvas_request_update (SPCanvas *canvas)
2196 {
2197 canvas->need_update = TRUE;
2198 add_idle (canvas);
2199 }
2201 /**
2202 * Forces redraw of rectangular canvas area.
2203 */
2204 void
2205 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2206 {
2207 NRRectL bbox;
2208 NRRectL visible;
2209 NRRectL clip;
2211 g_return_if_fail (canvas != NULL);
2212 g_return_if_fail (SP_IS_CANVAS (canvas));
2214 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2215 if ((x0 >= x1) || (y0 >= y1)) return;
2217 bbox.x0 = x0;
2218 bbox.y0 = y0;
2219 bbox.x1 = x1;
2220 bbox.y1 = y1;
2222 visible.x0 = canvas->x0;
2223 visible.y0 = canvas->y0;
2224 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2225 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2227 nr_rect_l_intersect (&clip, &bbox, &visible);
2229 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2230 add_idle (canvas);
2231 }
2233 /**
2234 * Sets world coordinates from win and canvas.
2235 */
2236 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2237 {
2238 g_return_if_fail (canvas != NULL);
2239 g_return_if_fail (SP_IS_CANVAS (canvas));
2241 if (worldx) *worldx = canvas->x0 + winx;
2242 if (worldy) *worldy = canvas->y0 + winy;
2243 }
2245 /**
2246 * Sets win coordinates from world and canvas.
2247 */
2248 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2249 {
2250 g_return_if_fail (canvas != NULL);
2251 g_return_if_fail (SP_IS_CANVAS (canvas));
2253 if (winx) *winx = worldx - canvas->x0;
2254 if (winy) *winy = worldy - canvas->y0;
2255 }
2257 /**
2258 * Converts point from win to world coordinates.
2259 */
2260 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2261 {
2262 g_assert (canvas != NULL);
2263 g_assert (SP_IS_CANVAS (canvas));
2265 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2266 }
2268 /**
2269 * Converts point from world to win coordinates.
2270 */
2271 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2272 {
2273 g_assert (canvas != NULL);
2274 g_assert (SP_IS_CANVAS (canvas));
2276 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2277 }
2279 /**
2280 * Returns true if point given in world coordinates is inside window.
2281 */
2282 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2283 {
2284 g_assert( canvas != NULL );
2285 g_assert(SP_IS_CANVAS(canvas));
2287 using NR::X;
2288 using NR::Y;
2289 GtkWidget const &w = *GTK_WIDGET(canvas);
2290 return ( ( canvas->x0 <= world[X] ) &&
2291 ( canvas->y0 <= world[Y] ) &&
2292 ( world[X] < canvas->x0 + w.allocation.width ) &&
2293 ( world[Y] < canvas->y0 + w.allocation.height ) );
2294 }
2296 /**
2297 * Return canvas window coordinates as NR::Rect.
2298 */
2299 NR::Rect SPCanvas::getViewbox() const
2300 {
2301 GtkWidget const *w = GTK_WIDGET(this);
2302 return NR::Rect(NR::Point(dx0, dy0),
2303 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2304 }
2306 /**
2307 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2308 */
2309 NR::IRect SPCanvas::getViewboxIntegers() const
2310 {
2311 GtkWidget const *w = GTK_WIDGET(this);
2312 return NR::IRect(NR::IPoint(x0, y0),
2313 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2314 }
2316 inline int sp_canvas_tile_floor(int x)
2317 {
2318 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2319 }
2321 inline int sp_canvas_tile_ceil(int x)
2322 {
2323 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2324 }
2326 /**
2327 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2328 */
2329 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2330 {
2331 if ( nl >= nr || nt >= nb ) {
2332 if ( canvas->tiles ) g_free(canvas->tiles);
2333 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2334 canvas->tileH=canvas->tileV=0;
2335 canvas->tiles=NULL;
2336 return;
2337 }
2338 int tl=sp_canvas_tile_floor(nl);
2339 int tt=sp_canvas_tile_floor(nt);
2340 int tr=sp_canvas_tile_ceil(nr);
2341 int tb=sp_canvas_tile_ceil(nb);
2343 int nh = tr-tl, nv = tb-tt;
2344 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2345 for (int i=tl; i<tr; i++) {
2346 for (int j=tt; j<tb; j++) {
2347 int ind = (i-tl) + (j-tt)*nh;
2348 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2349 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2350 } else {
2351 ntiles[ind]=0; // newly exposed areas get 0
2352 }
2353 }
2354 }
2355 if ( canvas->tiles ) g_free(canvas->tiles);
2356 canvas->tiles=ntiles;
2357 canvas->tLeft=tl;
2358 canvas->tTop=tt;
2359 canvas->tRight=tr;
2360 canvas->tBottom=tb;
2361 canvas->tileH=nh;
2362 canvas->tileV=nv;
2363 }
2365 /*
2366 * Helper that queues a canvas rectangle for redraw
2367 */
2368 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2369 canvas->need_redraw = TRUE;
2371 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2372 }
2374 /**
2375 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2376 */
2377 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2378 {
2379 if ( nl >= nr || nt >= nb ) {
2380 return;
2381 }
2382 int tl=sp_canvas_tile_floor(nl);
2383 int tt=sp_canvas_tile_floor(nt);
2384 int tr=sp_canvas_tile_ceil(nr);
2385 int tb=sp_canvas_tile_ceil(nb);
2386 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2387 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2388 if ( tr > canvas->tRight ) tr=canvas->tRight;
2389 if ( tt < canvas->tTop ) tt=canvas->tTop;
2390 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2392 for (int i=tl; i<tr; i++) {
2393 for (int j=tt; j<tb; j++) {
2394 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2395 }
2396 }
2397 }
2400 /*
2401 Local Variables:
2402 mode:c++
2403 c-file-style:"stroustrup"
2404 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2405 indent-tabs-mode:nil
2406 fill-column:99
2407 End:
2408 */
2409 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :