1 #define __SP_CANVAS_C__
3 /** \file
4 * Port of GnomeCanvas for Inkscape needs
5 *
6 * Authors:
7 * Federico Mena <federico@nuclecu.unam.mx>
8 * Raph Levien <raph@gimp.org>
9 * Lauris Kaplinski <lauris@kaplinski.com>
10 * fred
11 * bbyak
12 *
13 * Copyright (C) 1998 The Free Software Foundation
14 * Copyright (C) 2002-2006 authors
15 *
16 * Released under GNU GPL, read the file 'COPYING' for more information
17 */
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <libnr/nr-pixblock.h>
25 #include <gtk/gtkmain.h>
26 #include <gtk/gtksignal.h>
28 #include <gtkmm.h>
30 #include <helper/sp-marshal.h>
31 #include <display/sp-canvas.h>
32 #include "display-forward.h"
33 #include <libnr/nr-matrix-fns.h>
34 #include <libnr/nr-matrix-ops.h>
35 #include <libnr/nr-convex-hull.h>
36 #include "prefs-utils.h"
37 #include "box3d-context.h"
38 #include "inkscape.h"
39 #include "sodipodi-ctrlrect.h"
40 #if ENABLE_LCMS
41 #include "color-profile-fns.h"
42 #endif // ENABLE_LCMS
44 // Define this to visualize the regions to be redrawn
45 //#define DEBUG_REDRAW 1;
47 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
48 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
49 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
50 #define TILE_SIZE 16
52 enum {
53 RENDERMODE_NORMAL,
54 RENDERMODE_NOAA,
55 RENDERMODE_OUTLINE
56 };
58 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
60 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
62 enum {
63 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
64 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
65 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
66 };
68 /**
69 * A group of Items.
70 */
71 struct SPCanvasGroup {
72 SPCanvasItem item;
74 GList *items, *last;
75 };
77 /**
78 * The SPCanvasGroup vtable.
79 */
80 struct SPCanvasGroupClass {
81 SPCanvasItemClass parent_class;
82 };
84 /**
85 * The SPCanvas vtable.
86 */
87 struct SPCanvasClass {
88 GtkWidgetClass parent_class;
89 };
91 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
92 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
94 /* SPCanvasItem */
96 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
99 static void sp_canvas_request_update (SPCanvas *canvas);
101 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
102 static void sp_canvas_item_init (SPCanvasItem *item);
103 static void sp_canvas_item_dispose (GObject *object);
104 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
106 static int emit_event (SPCanvas *canvas, GdkEvent *event);
108 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
110 static GtkObjectClass *item_parent_class;
112 /**
113 * Registers the SPCanvasItem class with Glib and returns its type number.
114 */
115 GType
116 sp_canvas_item_get_type (void)
117 {
118 static GType type = 0;
119 if (!type) {
120 static GTypeInfo const info = {
121 sizeof (SPCanvasItemClass),
122 NULL, NULL,
123 (GClassInitFunc) sp_canvas_item_class_init,
124 NULL, NULL,
125 sizeof (SPCanvasItem),
126 0,
127 (GInstanceInitFunc) sp_canvas_item_init,
128 NULL
129 };
130 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
131 }
133 return type;
134 }
136 /**
137 * Initializes the SPCanvasItem vtable and the "event" signal.
138 */
139 static void
140 sp_canvas_item_class_init (SPCanvasItemClass *klass)
141 {
142 GObjectClass *object_class = (GObjectClass *) klass;
144 /* fixme: Derive from GObject */
145 item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
147 item_signals[ITEM_EVENT] = g_signal_new ("event",
148 G_TYPE_FROM_CLASS (klass),
149 G_SIGNAL_RUN_LAST,
150 G_STRUCT_OFFSET (SPCanvasItemClass, event),
151 NULL, NULL,
152 sp_marshal_BOOLEAN__POINTER,
153 G_TYPE_BOOLEAN, 1,
154 GDK_TYPE_EVENT);
156 object_class->dispose = sp_canvas_item_dispose;
157 }
159 /**
160 * Callback for initialization of SPCanvasItem.
161 */
162 static void
163 sp_canvas_item_init (SPCanvasItem *item)
164 {
165 item->flags |= SP_CANVAS_ITEM_VISIBLE;
166 item->xform = NR::Matrix(NR::identity());
167 }
169 /**
170 * Constructs new SPCanvasItem on SPCanvasGroup.
171 */
172 SPCanvasItem *
173 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
174 {
175 va_list args;
177 g_return_val_if_fail (parent != NULL, NULL);
178 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
179 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
181 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
183 va_start (args, first_arg_name);
184 sp_canvas_item_construct (item, parent, first_arg_name, args);
185 va_end (args);
187 return item;
188 }
190 /**
191 * Sets up the newly created SPCanvasItem.
192 *
193 * We make it static for encapsulation reasons since it was nowhere used.
194 */
195 static void
196 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
197 {
198 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
199 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
201 item->parent = SP_CANVAS_ITEM (parent);
202 item->canvas = item->parent->canvas;
204 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
206 group_add (SP_CANVAS_GROUP (item->parent), item);
208 sp_canvas_item_request_update (item);
209 }
211 /**
212 * Helper function that requests redraw only if item's visible flag is set.
213 */
214 static void
215 redraw_if_visible (SPCanvasItem *item)
216 {
217 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
218 int x0 = (int)(item->x1);
219 int x1 = (int)(item->x2);
220 int y0 = (int)(item->y1);
221 int y1 = (int)(item->y2);
223 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
224 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
225 }
226 }
227 }
229 /**
230 * Callback that removes item from all referers and destroys it.
231 */
232 static void
233 sp_canvas_item_dispose (GObject *object)
234 {
235 SPCanvasItem *item = SP_CANVAS_ITEM (object);
237 // Hack: if this is a ctrlrect, move it to 0,0;
238 // this redraws only the stroke of the rect to be deleted,
239 // avoiding redraw of the entire area
240 if (SP_IS_CTRLRECT(item)) {
241 SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
242 SP_CTRLRECT(object)->update(item->xform, 0);
243 } else {
244 redraw_if_visible (item);
245 }
246 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
248 if (item == item->canvas->current_item) {
249 item->canvas->current_item = NULL;
250 item->canvas->need_repick = TRUE;
251 }
253 if (item == item->canvas->new_current_item) {
254 item->canvas->new_current_item = NULL;
255 item->canvas->need_repick = TRUE;
256 }
258 if (item == item->canvas->grabbed_item) {
259 item->canvas->grabbed_item = NULL;
260 gdk_pointer_ungrab (GDK_CURRENT_TIME);
261 }
263 if (item == item->canvas->focused_item)
264 item->canvas->focused_item = NULL;
266 if (item->parent) {
267 group_remove (SP_CANVAS_GROUP (item->parent), item);
268 }
270 G_OBJECT_CLASS (item_parent_class)->dispose (object);
271 }
273 /**
274 * Helper function to update item and its children.
275 *
276 * NB! affine is parent2canvas.
277 */
278 static void
279 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
280 {
281 /* Apply the child item's transform */
282 NR::Matrix child_affine = item->xform * affine;
284 /* apply object flags to child flags */
285 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
287 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
288 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
290 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
291 child_flags |= SP_CANVAS_UPDATE_AFFINE;
293 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
294 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
295 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
296 }
298 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
299 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
300 }
302 /**
303 * Helper function to invoke the point method of the item.
304 *
305 * The argument x, y should be in the parent's item-relative coordinate
306 * system. This routine applies the inverse of the item's transform,
307 * maintaining the affine invariant.
308 */
309 static double
310 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
311 {
312 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
313 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
315 return NR_HUGE;
316 }
318 /**
319 * Makes the item's affine transformation matrix be equal to the specified
320 * matrix.
321 *
322 * @item: A canvas item.
323 * @affine: An affine transformation matrix.
324 */
325 void
326 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine)
327 {
328 item->xform = affine;
330 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
331 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
332 if (item->parent != NULL) {
333 sp_canvas_item_request_update (item->parent);
334 } else {
335 sp_canvas_request_update (item->canvas);
336 }
337 }
339 item->canvas->need_repick = TRUE;
340 }
342 /**
343 * Convenience function to reorder items in a group's child list.
344 *
345 * This puts the specified link after the "before" link.
346 */
347 static void
348 put_item_after (GList *link, GList *before)
349 {
350 if (link == before)
351 return;
353 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
355 if (before == NULL) {
356 if (link == parent->items) return;
358 link->prev->next = link->next;
360 if (link->next) {
361 link->next->prev = link->prev;
362 } else {
363 parent->last = link->prev;
364 }
366 link->prev = before;
367 link->next = parent->items;
368 link->next->prev = link;
369 parent->items = link;
370 } else {
371 if ((link == parent->last) && (before == parent->last->prev))
372 return;
374 if (link->next)
375 link->next->prev = link->prev;
377 if (link->prev)
378 link->prev->next = link->next;
379 else {
380 parent->items = link->next;
381 parent->items->prev = NULL;
382 }
384 link->prev = before;
385 link->next = before->next;
387 link->prev->next = link;
389 if (link->next)
390 link->next->prev = link;
391 else
392 parent->last = link;
393 }
394 }
397 /**
398 * Raises the item in its parent's stack by the specified number of positions.
399 *
400 * \param item A canvas item.
401 * \param positions Number of steps to raise the item.
402 *
403 * If the number of positions is greater than the distance to the top of the
404 * stack, then the item is put at the top.
405 */
406 void
407 sp_canvas_item_raise (SPCanvasItem *item, int positions)
408 {
409 g_return_if_fail (item != NULL);
410 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
411 g_return_if_fail (positions >= 0);
413 if (!item->parent || positions == 0)
414 return;
416 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
417 GList *link = g_list_find (parent->items, item);
418 g_assert (link != NULL);
420 GList *before;
421 for (before = link; positions && before; positions--)
422 before = before->next;
424 if (!before)
425 before = parent->last;
427 put_item_after (link, before);
429 redraw_if_visible (item);
430 item->canvas->need_repick = TRUE;
431 }
434 /**
435 * Lowers the item in its parent's stack by the specified number of positions.
436 *
437 * \param item A canvas item.
438 * \param positions Number of steps to lower the item.
439 *
440 * If the number of positions is greater than the distance to the bottom of the
441 * stack, then the item is put at the bottom.
442 **/
443 void
444 sp_canvas_item_lower (SPCanvasItem *item, int positions)
445 {
446 g_return_if_fail (item != NULL);
447 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
448 g_return_if_fail (positions >= 1);
450 if (!item->parent || positions == 0)
451 return;
453 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
454 GList *link = g_list_find (parent->items, item);
455 g_assert (link != NULL);
457 GList *before;
458 if (link->prev)
459 for (before = link->prev; positions && before; positions--)
460 before = before->prev;
461 else
462 before = NULL;
464 put_item_after (link, before);
466 redraw_if_visible (item);
467 item->canvas->need_repick = TRUE;
468 }
470 /**
471 * Sets visible flag on item and requests a redraw.
472 */
473 void
474 sp_canvas_item_show (SPCanvasItem *item)
475 {
476 g_return_if_fail (item != NULL);
477 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
479 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
480 return;
482 item->flags |= SP_CANVAS_ITEM_VISIBLE;
484 int x0 = (int)(item->x1);
485 int x1 = (int)(item->x2);
486 int y0 = (int)(item->y1);
487 int y1 = (int)(item->y2);
489 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
490 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
491 item->canvas->need_repick = TRUE;
492 }
493 }
495 /**
496 * Clears visible flag on item and requests a redraw.
497 */
498 void
499 sp_canvas_item_hide (SPCanvasItem *item)
500 {
501 g_return_if_fail (item != NULL);
502 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
504 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
505 return;
507 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
509 int x0 = (int)(item->x1);
510 int x1 = (int)(item->x2);
511 int y0 = (int)(item->y1);
512 int y1 = (int)(item->y2);
514 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
515 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
516 item->canvas->need_repick = TRUE;
517 }
518 }
520 /**
521 * Grab item under cursor.
522 *
523 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
524 */
525 int
526 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
527 {
528 g_return_val_if_fail (item != NULL, -1);
529 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
530 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
532 if (item->canvas->grabbed_item)
533 return -1;
535 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
536 return -1;
538 /* fixme: Top hack (Lauris) */
539 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
540 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
541 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
542 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
543 NULL, cursor, etime);
545 item->canvas->grabbed_item = item;
546 item->canvas->grabbed_event_mask = event_mask;
547 item->canvas->current_item = item; /* So that events go to the grabbed item */
549 return 0;
550 }
552 /**
553 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
554 * mouse.
555 *
556 * \param item A canvas item that holds a grab.
557 * \param etime The timestamp for ungrabbing the mouse.
558 */
559 void
560 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
561 {
562 g_return_if_fail (item != NULL);
563 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
565 if (item->canvas->grabbed_item != item)
566 return;
568 item->canvas->grabbed_item = NULL;
570 gdk_pointer_ungrab (etime);
571 }
573 /**
574 * Returns the product of all transformation matrices from the root item down
575 * to the item.
576 */
577 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
578 {
579 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
581 NR::Matrix affine = NR::identity();
583 while (item) {
584 affine *= item->xform;
585 item = item->parent;
586 }
587 return affine;
588 }
590 /**
591 * Helper that returns true iff item is descendant of parent.
592 */
593 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
594 {
595 while (item) {
596 if (item == parent)
597 return true;
598 item = item->parent;
599 }
601 return false;
602 }
604 /**
605 * Focus canvas, and item under cursor if it is not already focussed.
606 */
607 void
608 sp_canvas_item_grab_focus (SPCanvasItem *item)
609 {
610 g_return_if_fail (item != NULL);
611 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
612 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
614 SPCanvasItem *focused_item = item->canvas->focused_item;
616 if (focused_item) {
617 GdkEvent ev;
618 ev.focus_change.type = GDK_FOCUS_CHANGE;
619 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
620 ev.focus_change.send_event = FALSE;
621 ev.focus_change.in = FALSE;
623 emit_event (item->canvas, &ev);
624 }
626 item->canvas->focused_item = item;
627 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
629 if (focused_item) {
630 GdkEvent ev;
631 ev.focus_change.type = GDK_FOCUS_CHANGE;
632 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
633 ev.focus_change.send_event = FALSE;
634 ev.focus_change.in = TRUE;
636 emit_event (item->canvas, &ev);
637 }
638 }
640 /**
641 * Requests that the canvas queue an update for the specified item.
642 *
643 * To be used only by item implementations.
644 */
645 void
646 sp_canvas_item_request_update (SPCanvasItem *item)
647 {
648 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
649 return;
651 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
653 if (item->parent != NULL) {
654 /* Recurse up the tree */
655 sp_canvas_item_request_update (item->parent);
656 } else {
657 /* Have reached the top of the tree, make sure the update call gets scheduled. */
658 sp_canvas_request_update (item->canvas);
659 }
660 }
662 /**
663 * Returns position of item in group.
664 */
665 gint sp_canvas_item_order (SPCanvasItem * item)
666 {
667 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
668 }
670 /* SPCanvasGroup */
672 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
673 static void sp_canvas_group_init (SPCanvasGroup *group);
674 static void sp_canvas_group_destroy (GtkObject *object);
676 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
677 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
678 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
680 static SPCanvasItemClass *group_parent_class;
682 /**
683 * Registers SPCanvasGroup class with Gtk and returns its type number.
684 */
685 GtkType
686 sp_canvas_group_get_type (void)
687 {
688 static GtkType group_type = 0;
690 if (!group_type) {
691 static GtkTypeInfo const group_info = {
692 "SPCanvasGroup",
693 sizeof (SPCanvasGroup),
694 sizeof (SPCanvasGroupClass),
695 (GtkClassInitFunc) sp_canvas_group_class_init,
696 (GtkObjectInitFunc) sp_canvas_group_init,
697 NULL, NULL, NULL
698 };
700 group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
701 }
703 return group_type;
704 }
706 /**
707 * Class initialization function for SPCanvasGroupClass
708 */
709 static void
710 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
711 {
712 GtkObjectClass *object_class = (GtkObjectClass *) klass;
713 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
715 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
717 object_class->destroy = sp_canvas_group_destroy;
719 item_class->update = sp_canvas_group_update;
720 item_class->render = sp_canvas_group_render;
721 item_class->point = sp_canvas_group_point;
722 }
724 /**
725 * Callback. Empty.
726 */
727 static void
728 sp_canvas_group_init (SPCanvasGroup */*group*/)
729 {
730 /* Nothing here */
731 }
733 /**
734 * Callback that destroys all items in group and calls group's virtual
735 * destroy() function.
736 */
737 static void
738 sp_canvas_group_destroy (GtkObject *object)
739 {
740 g_return_if_fail (object != NULL);
741 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
743 SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
745 GList *list = group->items;
746 while (list) {
747 SPCanvasItem *child = (SPCanvasItem *)list->data;
748 list = list->next;
750 gtk_object_destroy (GTK_OBJECT (child));
751 }
753 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
754 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
755 }
757 /**
758 * Update handler for canvas groups
759 */
760 static void
761 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
762 {
763 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
764 NR::ConvexHull corners(NR::Point(0, 0));
765 bool empty=true;
767 for (GList *list = group->items; list; list = list->next) {
768 SPCanvasItem *i = (SPCanvasItem *)list->data;
770 sp_canvas_item_invoke_update (i, affine, flags);
772 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
773 if (empty) {
774 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
775 empty = false;
776 } else {
777 corners.add(NR::Point(i->x1, i->y1));
778 }
779 corners.add(NR::Point(i->x2, i->y2));
780 }
781 }
783 NR::Maybe<NR::Rect> const bounds = corners.bounds();
784 if (bounds) {
785 item->x1 = bounds->min()[NR::X];
786 item->y1 = bounds->min()[NR::Y];
787 item->x2 = bounds->max()[NR::X];
788 item->y2 = bounds->max()[NR::Y];
789 } else {
790 // FIXME ?
791 item->x1 = item->x2 = item->y1 = item->y2 = 0;
792 }
793 }
795 /**
796 * Point handler for canvas groups.
797 */
798 static double
799 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
800 {
801 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
802 double const x = p[NR::X];
803 double const y = p[NR::Y];
804 int x1 = (int)(x - item->canvas->close_enough);
805 int y1 = (int)(y - item->canvas->close_enough);
806 int x2 = (int)(x + item->canvas->close_enough);
807 int y2 = (int)(y + item->canvas->close_enough);
809 double best = 0.0;
810 *actual_item = NULL;
812 double dist = 0.0;
814 for (GList *list = group->items; list; list = list->next) {
815 SPCanvasItem *child = (SPCanvasItem *)list->data;
817 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
818 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
820 int has_point;
821 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
822 dist = sp_canvas_item_invoke_point (child, p, &point_item);
823 has_point = TRUE;
824 } else
825 has_point = FALSE;
827 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
828 best = dist;
829 *actual_item = point_item;
830 }
831 }
832 }
834 return best;
835 }
837 /**
838 * Renders all visible canvas group items in buf rectangle.
839 */
840 static void
841 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
842 {
843 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
845 for (GList *list = group->items; list; list = list->next) {
846 SPCanvasItem *child = (SPCanvasItem *)list->data;
847 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
848 if ((child->x1 < buf->rect.x1) &&
849 (child->y1 < buf->rect.y1) &&
850 (child->x2 > buf->rect.x0) &&
851 (child->y2 > buf->rect.y0)) {
852 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
853 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
854 }
855 }
856 }
857 }
859 /**
860 * Adds an item to a canvas group.
861 */
862 static void
863 group_add (SPCanvasGroup *group, SPCanvasItem *item)
864 {
865 gtk_object_ref (GTK_OBJECT (item));
866 gtk_object_sink (GTK_OBJECT (item));
868 if (!group->items) {
869 group->items = g_list_append (group->items, item);
870 group->last = group->items;
871 } else {
872 group->last = g_list_append (group->last, item)->next;
873 }
875 sp_canvas_item_request_update (item);
876 }
878 /**
879 * Removes an item from a canvas group
880 */
881 static void
882 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
883 {
884 g_return_if_fail (group != NULL);
885 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
886 g_return_if_fail (item != NULL);
888 for (GList *children = group->items; children; children = children->next) {
889 if (children->data == item) {
891 /* Unparent the child */
893 item->parent = NULL;
894 gtk_object_unref (GTK_OBJECT (item));
896 /* Remove it from the list */
898 if (children == group->last) group->last = children->prev;
900 group->items = g_list_remove_link (group->items, children);
901 g_list_free (children);
902 break;
903 }
904 }
905 }
907 /* SPCanvas */
909 static void sp_canvas_class_init (SPCanvasClass *klass);
910 static void sp_canvas_init (SPCanvas *canvas);
911 static void sp_canvas_destroy (GtkObject *object);
913 static void sp_canvas_realize (GtkWidget *widget);
914 static void sp_canvas_unrealize (GtkWidget *widget);
916 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
917 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
919 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
920 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
921 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
922 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
923 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
924 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
925 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
926 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
928 static GtkWidgetClass *canvas_parent_class;
930 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
931 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
932 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
933 static int do_update (SPCanvas *canvas);
935 /**
936 * Registers the SPCanvas class if necessary, and returns the type ID
937 * associated to it.
938 *
939 * \return The type ID of the SPCanvas class.
940 **/
941 GtkType
942 sp_canvas_get_type (void)
943 {
944 static GtkType canvas_type = 0;
946 if (!canvas_type) {
947 static GtkTypeInfo const canvas_info = {
948 "SPCanvas",
949 sizeof (SPCanvas),
950 sizeof (SPCanvasClass),
951 (GtkClassInitFunc) sp_canvas_class_init,
952 (GtkObjectInitFunc) sp_canvas_init,
953 NULL, NULL, NULL
954 };
956 canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
957 }
959 return canvas_type;
960 }
962 /**
963 * Class initialization function for SPCanvasClass.
964 */
965 static void
966 sp_canvas_class_init (SPCanvasClass *klass)
967 {
968 GtkObjectClass *object_class = (GtkObjectClass *) klass;
969 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
971 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
973 object_class->destroy = sp_canvas_destroy;
975 widget_class->realize = sp_canvas_realize;
976 widget_class->unrealize = sp_canvas_unrealize;
977 widget_class->size_request = sp_canvas_size_request;
978 widget_class->size_allocate = sp_canvas_size_allocate;
979 widget_class->button_press_event = sp_canvas_button;
980 widget_class->button_release_event = sp_canvas_button;
981 widget_class->motion_notify_event = sp_canvas_motion;
982 widget_class->scroll_event = sp_canvas_scroll;
983 widget_class->expose_event = sp_canvas_expose;
984 widget_class->key_press_event = sp_canvas_key;
985 widget_class->key_release_event = sp_canvas_key;
986 widget_class->enter_notify_event = sp_canvas_crossing;
987 widget_class->leave_notify_event = sp_canvas_crossing;
988 widget_class->focus_in_event = sp_canvas_focus_in;
989 widget_class->focus_out_event = sp_canvas_focus_out;
990 }
992 /**
993 * Callback: object initialization for SPCanvas.
994 */
995 static void
996 sp_canvas_init (SPCanvas *canvas)
997 {
998 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
999 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1000 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1002 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1003 canvas->pick_event.crossing.x = 0;
1004 canvas->pick_event.crossing.y = 0;
1006 /* Create the root item as a special case */
1007 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1008 canvas->root->canvas = canvas;
1010 gtk_object_ref (GTK_OBJECT (canvas->root));
1011 gtk_object_sink (GTK_OBJECT (canvas->root));
1013 canvas->need_repick = TRUE;
1015 // See comment at in sp-canvas.h.
1016 canvas->gen_all_enter_events = false;
1018 canvas->tiles=NULL;
1019 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1020 canvas->tileH=canvas->tileV=0;
1022 canvas->forced_redraw_count = 0;
1023 canvas->forced_redraw_limit = -1;
1025 canvas->is_scrolling = false;
1027 }
1029 /**
1030 * Convenience function to remove the idle handler of a canvas.
1031 */
1032 static void
1033 remove_idle (SPCanvas *canvas)
1034 {
1035 if (canvas->idle_id) {
1036 gtk_idle_remove (canvas->idle_id);
1037 canvas->idle_id = 0;
1038 }
1039 }
1041 /*
1042 * Removes the transient state of the canvas (idle handler, grabs).
1043 */
1044 static void
1045 shutdown_transients (SPCanvas *canvas)
1046 {
1047 /* We turn off the need_redraw flag, since if the canvas is mapped again
1048 * it will request a redraw anyways. We do not turn off the need_update
1049 * flag, though, because updates are not queued when the canvas remaps
1050 * itself.
1051 */
1052 if (canvas->need_redraw) {
1053 canvas->need_redraw = FALSE;
1054 }
1055 if ( canvas->tiles ) g_free(canvas->tiles);
1056 canvas->tiles=NULL;
1057 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1058 canvas->tileH=canvas->tileV=0;
1060 if (canvas->grabbed_item) {
1061 canvas->grabbed_item = NULL;
1062 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1063 }
1065 remove_idle (canvas);
1066 }
1068 /**
1069 * Destroy handler for SPCanvas.
1070 */
1071 static void
1072 sp_canvas_destroy (GtkObject *object)
1073 {
1074 SPCanvas *canvas = SP_CANVAS (object);
1076 if (canvas->root) {
1077 gtk_object_unref (GTK_OBJECT (canvas->root));
1078 canvas->root = NULL;
1079 }
1081 shutdown_transients (canvas);
1083 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1084 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1085 }
1087 /**
1088 * Returns new canvas as widget.
1089 */
1090 GtkWidget *
1091 sp_canvas_new_aa (void)
1092 {
1093 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1095 return (GtkWidget *) canvas;
1096 }
1098 /**
1099 * The canvas widget's realize callback.
1100 */
1101 static void
1102 sp_canvas_realize (GtkWidget *widget)
1103 {
1104 SPCanvas *canvas = SP_CANVAS (widget);
1106 GdkWindowAttr attributes;
1107 attributes.window_type = GDK_WINDOW_CHILD;
1108 attributes.x = widget->allocation.x;
1109 attributes.y = widget->allocation.y;
1110 attributes.width = widget->allocation.width;
1111 attributes.height = widget->allocation.height;
1112 attributes.wclass = GDK_INPUT_OUTPUT;
1113 attributes.visual = gdk_rgb_get_visual ();
1114 attributes.colormap = gdk_rgb_get_cmap ();
1115 attributes.event_mask = (gtk_widget_get_events (widget) |
1116 GDK_EXPOSURE_MASK |
1117 GDK_BUTTON_PRESS_MASK |
1118 GDK_BUTTON_RELEASE_MASK |
1119 GDK_POINTER_MOTION_MASK |
1120 GDK_PROXIMITY_IN_MASK |
1121 GDK_PROXIMITY_OUT_MASK |
1122 GDK_KEY_PRESS_MASK |
1123 GDK_KEY_RELEASE_MASK |
1124 GDK_ENTER_NOTIFY_MASK |
1125 GDK_LEAVE_NOTIFY_MASK |
1126 GDK_FOCUS_CHANGE_MASK);
1127 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1129 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1130 gdk_window_set_user_data (widget->window, widget);
1132 if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1133 gtk_widget_set_events(widget, attributes.event_mask);
1135 widget->style = gtk_style_attach (widget->style, widget->window);
1137 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1139 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1140 }
1142 /**
1143 * The canvas widget's unrealize callback.
1144 */
1145 static void
1146 sp_canvas_unrealize (GtkWidget *widget)
1147 {
1148 SPCanvas *canvas = SP_CANVAS (widget);
1150 shutdown_transients (canvas);
1152 gdk_gc_destroy (canvas->pixmap_gc);
1153 canvas->pixmap_gc = NULL;
1155 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1156 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1157 }
1159 /**
1160 * The canvas widget's size_request callback.
1161 */
1162 static void
1163 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1164 {
1165 static_cast<void>(SP_CANVAS (widget));
1167 req->width = 256;
1168 req->height = 256;
1169 }
1171 /**
1172 * The canvas widget's size_allocate callback.
1173 */
1174 static void
1175 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1176 {
1177 SPCanvas *canvas = SP_CANVAS (widget);
1179 /* Schedule redraw of new region */
1180 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1181 if (allocation->width > widget->allocation.width) {
1182 sp_canvas_request_redraw (canvas,
1183 canvas->x0 + widget->allocation.width,
1184 0,
1185 canvas->x0 + allocation->width,
1186 canvas->y0 + allocation->height);
1187 }
1188 if (allocation->height > widget->allocation.height) {
1189 sp_canvas_request_redraw (canvas,
1190 0,
1191 canvas->y0 + widget->allocation.height,
1192 canvas->x0 + allocation->width,
1193 canvas->y0 + allocation->height);
1194 }
1196 widget->allocation = *allocation;
1198 if (GTK_WIDGET_REALIZED (widget)) {
1199 gdk_window_move_resize (widget->window,
1200 widget->allocation.x, widget->allocation.y,
1201 widget->allocation.width, widget->allocation.height);
1202 }
1203 }
1205 /**
1206 * Helper that emits an event for an item in the canvas, be it the current
1207 * item, grabbed item, or focused item, as appropriate.
1208 */
1209 static int
1210 emit_event (SPCanvas *canvas, GdkEvent *event)
1211 {
1212 guint mask;
1214 if (canvas->grabbed_item) {
1215 switch (event->type) {
1216 case GDK_ENTER_NOTIFY:
1217 mask = GDK_ENTER_NOTIFY_MASK;
1218 break;
1219 case GDK_LEAVE_NOTIFY:
1220 mask = GDK_LEAVE_NOTIFY_MASK;
1221 break;
1222 case GDK_MOTION_NOTIFY:
1223 mask = GDK_POINTER_MOTION_MASK;
1224 break;
1225 case GDK_BUTTON_PRESS:
1226 case GDK_2BUTTON_PRESS:
1227 case GDK_3BUTTON_PRESS:
1228 mask = GDK_BUTTON_PRESS_MASK;
1229 break;
1230 case GDK_BUTTON_RELEASE:
1231 mask = GDK_BUTTON_RELEASE_MASK;
1232 break;
1233 case GDK_KEY_PRESS:
1234 mask = GDK_KEY_PRESS_MASK;
1235 break;
1236 case GDK_KEY_RELEASE:
1237 mask = GDK_KEY_RELEASE_MASK;
1238 break;
1239 case GDK_SCROLL:
1240 mask = GDK_SCROLL;
1241 break;
1242 default:
1243 mask = 0;
1244 break;
1245 }
1247 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1248 }
1250 /* Convert to world coordinates -- we have two cases because of diferent
1251 * offsets of the fields in the event structures.
1252 */
1254 GdkEvent ev = *event;
1256 switch (ev.type) {
1257 case GDK_ENTER_NOTIFY:
1258 case GDK_LEAVE_NOTIFY:
1259 ev.crossing.x += canvas->x0;
1260 ev.crossing.y += canvas->y0;
1261 break;
1262 case GDK_MOTION_NOTIFY:
1263 case GDK_BUTTON_PRESS:
1264 case GDK_2BUTTON_PRESS:
1265 case GDK_3BUTTON_PRESS:
1266 case GDK_BUTTON_RELEASE:
1267 ev.motion.x += canvas->x0;
1268 ev.motion.y += canvas->y0;
1269 break;
1270 default:
1271 break;
1272 }
1274 /* Choose where we send the event */
1276 /* canvas->current_item becomes NULL in some cases under Win32
1277 ** (e.g. if the pointer leaves the window). So this is a hack that
1278 ** Lauris applied to SP to get around the problem.
1279 */
1280 SPCanvasItem* item = NULL;
1281 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1282 item = canvas->grabbed_item;
1283 } else {
1284 item = canvas->current_item;
1285 }
1287 if (canvas->focused_item &&
1288 ((event->type == GDK_KEY_PRESS) ||
1289 (event->type == GDK_KEY_RELEASE) ||
1290 (event->type == GDK_FOCUS_CHANGE))) {
1291 item = canvas->focused_item;
1292 }
1294 /* The event is propagated up the hierarchy (for if someone connected to
1295 * a group instead of a leaf event), and emission is stopped if a
1296 * handler returns TRUE, just like for GtkWidget events.
1297 */
1299 gint finished = FALSE;
1301 while (item && !finished) {
1302 gtk_object_ref (GTK_OBJECT (item));
1303 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1304 SPCanvasItem *parent = item->parent;
1305 gtk_object_unref (GTK_OBJECT (item));
1306 item = parent;
1307 }
1309 return finished;
1310 }
1312 /**
1313 * Helper that re-picks the current item in the canvas, based on the event's
1314 * coordinates and emits enter/leave events for items as appropriate.
1315 */
1316 static int
1317 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1318 {
1319 int button_down = 0;
1320 double x, y;
1322 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1323 return FALSE;
1325 int retval = FALSE;
1327 if (canvas->gen_all_enter_events == false) {
1328 // If a button is down, we'll perform enter and leave events on the
1329 // current item, but not enter on any other item. This is more or
1330 // less like X pointer grabbing for canvas items.
1331 //
1332 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1333 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1335 if (!button_down) canvas->left_grabbed_item = FALSE;
1336 }
1338 /* Save the event in the canvas. This is used to synthesize enter and
1339 * leave events in case the current item changes. It is also used to
1340 * re-pick the current item if the current one gets deleted. Also,
1341 * synthesize an enter event.
1342 */
1343 if (event != &canvas->pick_event) {
1344 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1345 /* these fields have the same offsets in both types of events */
1347 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1348 canvas->pick_event.crossing.window = event->motion.window;
1349 canvas->pick_event.crossing.send_event = event->motion.send_event;
1350 canvas->pick_event.crossing.subwindow = NULL;
1351 canvas->pick_event.crossing.x = event->motion.x;
1352 canvas->pick_event.crossing.y = event->motion.y;
1353 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1354 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1355 canvas->pick_event.crossing.focus = FALSE;
1356 canvas->pick_event.crossing.state = event->motion.state;
1358 /* these fields don't have the same offsets in both types of events */
1360 if (event->type == GDK_MOTION_NOTIFY) {
1361 canvas->pick_event.crossing.x_root = event->motion.x_root;
1362 canvas->pick_event.crossing.y_root = event->motion.y_root;
1363 } else {
1364 canvas->pick_event.crossing.x_root = event->button.x_root;
1365 canvas->pick_event.crossing.y_root = event->button.y_root;
1366 }
1367 } else {
1368 canvas->pick_event = *event;
1369 }
1370 }
1372 /* Don't do anything else if this is a recursive call */
1373 if (canvas->in_repick) return retval;
1375 /* LeaveNotify means that there is no current item, so we don't look for one */
1376 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1377 /* these fields don't have the same offsets in both types of events */
1379 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1380 x = canvas->pick_event.crossing.x;
1381 y = canvas->pick_event.crossing.y;
1382 } else {
1383 x = canvas->pick_event.motion.x;
1384 y = canvas->pick_event.motion.y;
1385 }
1387 /* world coords */
1388 x += canvas->x0;
1389 y += canvas->y0;
1391 /* find the closest item */
1392 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1393 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1394 } else {
1395 canvas->new_current_item = NULL;
1396 }
1397 } else {
1398 canvas->new_current_item = NULL;
1399 }
1401 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1402 return retval; /* current item did not change */
1403 }
1405 /* Synthesize events for old and new current items */
1407 if ((canvas->new_current_item != canvas->current_item)
1408 && (canvas->current_item != NULL)
1409 && !canvas->left_grabbed_item) {
1410 GdkEvent new_event;
1411 SPCanvasItem *item;
1413 item = canvas->current_item;
1415 new_event = canvas->pick_event;
1416 new_event.type = GDK_LEAVE_NOTIFY;
1418 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1419 new_event.crossing.subwindow = NULL;
1420 canvas->in_repick = TRUE;
1421 retval = emit_event (canvas, &new_event);
1422 canvas->in_repick = FALSE;
1423 }
1425 if (canvas->gen_all_enter_events == false) {
1426 // new_current_item may have been set to NULL during the call to
1427 // emit_event() above
1428 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1429 canvas->left_grabbed_item = TRUE;
1430 return retval;
1431 }
1432 }
1434 /* Handle the rest of cases */
1436 canvas->left_grabbed_item = FALSE;
1437 canvas->current_item = canvas->new_current_item;
1439 if (canvas->current_item != NULL) {
1440 GdkEvent new_event;
1442 new_event = canvas->pick_event;
1443 new_event.type = GDK_ENTER_NOTIFY;
1444 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1445 new_event.crossing.subwindow = NULL;
1446 retval = emit_event (canvas, &new_event);
1447 }
1449 return retval;
1450 }
1452 /**
1453 * Button event handler for the canvas.
1454 */
1455 static gint
1456 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1457 {
1458 SPCanvas *canvas = SP_CANVAS (widget);
1460 int retval = FALSE;
1462 /* dispatch normally regardless of the event's window if an item has
1463 has a pointer grab in effect */
1464 if (!canvas->grabbed_item &&
1465 event->window != SP_CANVAS_WINDOW (canvas))
1466 return retval;
1468 int mask;
1469 switch (event->button) {
1470 case 1:
1471 mask = GDK_BUTTON1_MASK;
1472 break;
1473 case 2:
1474 mask = GDK_BUTTON2_MASK;
1475 break;
1476 case 3:
1477 mask = GDK_BUTTON3_MASK;
1478 break;
1479 case 4:
1480 mask = GDK_BUTTON4_MASK;
1481 break;
1482 case 5:
1483 mask = GDK_BUTTON5_MASK;
1484 break;
1485 default:
1486 mask = 0;
1487 }
1489 switch (event->type) {
1490 case GDK_BUTTON_PRESS:
1491 case GDK_2BUTTON_PRESS:
1492 case GDK_3BUTTON_PRESS:
1493 /* Pick the current item as if the button were not pressed, and
1494 * then process the event.
1495 */
1496 canvas->state = event->state;
1497 pick_current_item (canvas, (GdkEvent *) event);
1498 canvas->state ^= mask;
1499 retval = emit_event (canvas, (GdkEvent *) event);
1500 break;
1502 case GDK_BUTTON_RELEASE:
1503 /* Process the event as if the button were pressed, then repick
1504 * after the button has been released
1505 */
1506 canvas->state = event->state;
1507 retval = emit_event (canvas, (GdkEvent *) event);
1508 event->state ^= mask;
1509 canvas->state = event->state;
1510 pick_current_item (canvas, (GdkEvent *) event);
1511 event->state ^= mask;
1512 break;
1514 default:
1515 g_assert_not_reached ();
1516 }
1518 return retval;
1519 }
1521 /**
1522 * Scroll event handler for the canvas.
1523 *
1524 * \todo FIXME: generate motion events to re-select items.
1525 */
1526 static gint
1527 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1528 {
1529 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1530 }
1532 /**
1533 * Motion event handler for the canvas.
1534 */
1535 static int
1536 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1537 {
1538 SPCanvas *canvas = SP_CANVAS (widget);
1540 if (event->window != SP_CANVAS_WINDOW (canvas))
1541 return FALSE;
1543 if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1544 gint x, y;
1545 gdk_window_get_pointer (widget->window, &x, &y, NULL);
1546 event->x = x;
1547 event->y = y;
1548 }
1550 canvas->state = event->state;
1551 pick_current_item (canvas, (GdkEvent *) event);
1553 return emit_event (canvas, (GdkEvent *) event);
1554 }
1556 static void
1557 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)
1558 {
1559 GtkWidget *widget = GTK_WIDGET (canvas);
1561 SPCanvasBuf buf;
1562 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1563 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1564 } else {
1565 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1566 }
1568 // Mark the region clean
1569 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1571 buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1572 buf.rect.x0 = x0;
1573 buf.rect.y0 = y0;
1574 buf.rect.x1 = x1;
1575 buf.rect.y1 = y1;
1576 buf.visible_rect.x0 = draw_x1;
1577 buf.visible_rect.y0 = draw_y1;
1578 buf.visible_rect.x1 = draw_x2;
1579 buf.visible_rect.y1 = draw_y2;
1580 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1581 buf.bg_color = (((color->red & 0xff00) << 8)
1582 | (color->green & 0xff00)
1583 | (color->blue >> 8));
1584 buf.is_empty = true;
1586 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1587 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1588 }
1590 #if ENABLE_LCMS
1591 cmsHTRANSFORM transf = Inkscape::colorprofile_get_display_transform();
1592 #endif // ENABLE_LCMS
1594 if (buf.is_empty) {
1595 #if ENABLE_LCMS
1596 if ( transf ) {
1597 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1598 }
1599 #endif // ENABLE_LCMS
1600 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1601 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1602 canvas->pixmap_gc,
1603 TRUE,
1604 x0 - canvas->x0, y0 - canvas->y0,
1605 x1 - x0, y1 - y0);
1606 } else {
1607 /*
1608 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1609 // Why this must not be done currently:
1610 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1611 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)
1612 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels,
1613 // we need more bufs to paint a given area and as a result it's even a bit slower
1615 cairo_surface_t* cst = cairo_image_surface_create_for_data (
1616 buf.buf,
1617 CAIRO_FORMAT_RGB24, // unpacked, i.e. 32 bits! one byte is unused
1618 x1 - x0, y1 - y0,
1619 buf.buf_rowstride
1620 );
1621 cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1622 cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1623 cairo_paint (ct);
1624 cairo_destroy (ct);
1625 cairo_surface_finish (cst);
1626 cairo_surface_destroy (cst);
1627 */
1629 #if ENABLE_LCMS
1630 if ( transf ) {
1631 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1632 guchar* p = buf.buf + (sw * 3) * yy;
1633 cmsDoTransform( transf, p, p, (x1 - x0) );
1634 }
1635 }
1636 #endif // ENABLE_LCMS
1638 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1639 canvas->pixmap_gc,
1640 x0 - canvas->x0, y0 - canvas->y0,
1641 x1 - x0, y1 - y0,
1642 GDK_RGB_DITHER_MAX,
1643 buf.buf,
1644 sw * 3,
1645 x0 - canvas->x0, y0 - canvas->y0);
1646 }
1648 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1649 nr_pixelstore_256K_free (buf.buf);
1650 } else {
1651 nr_pixelstore_1M_free (buf.buf);
1652 }
1653 }
1655 struct PaintRectSetup {
1656 SPCanvas* canvas;
1657 NRRectL big_rect;
1658 GTimeVal start_time;
1659 int max_pixels;
1660 NR::Point mouse_loc;
1661 };
1663 /**
1664 * Paint the given rect, recursively subdividing the region until it is the size of a single
1665 * buffer.
1666 *
1667 * @return true if the drawing completes
1668 */
1669 static int
1670 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1671 {
1672 GTimeVal now;
1673 g_get_current_time (&now);
1675 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1676 + (now.tv_usec - setup->start_time.tv_usec);
1678 // Allow only very fast buffers to be run together;
1679 // as soon as the total redraw time exceeds 1ms, cancel;
1680 // this returns control to the idle loop and allows Inkscape to process user input
1681 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1682 // it will get back and finish painting what remains to paint.
1683 if (elapsed > 1000) {
1685 // Interrupting redraw isn't always good.
1686 // For example, when you drag one node of a big path, only the buffer containing
1687 // the mouse cursor will be redrawn again and again, and the rest of the path
1688 // will remain stale because Inkscape never has enough idle time to redraw all
1689 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1690 // If this limit is set, and if we have aborted redraw more times than is allowed,
1691 // interrupting is blocked and we're forced to redraw full screen once
1692 // (after which we can again interrupt forced_redraw_limit times).
1693 if (setup->canvas->forced_redraw_limit < 0 ||
1694 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1696 if (setup->canvas->forced_redraw_limit != -1) {
1697 setup->canvas->forced_redraw_count++;
1698 }
1700 return false;
1701 }
1702 }
1704 // Find the optimal buffer dimensions
1705 int bw = this_rect.x1 - this_rect.x0;
1706 int bh = this_rect.y1 - this_rect.y0;
1707 if ((bw < 1) || (bh < 1))
1708 return 0;
1710 if (bw * bh < setup->max_pixels) {
1711 // We are small enough
1712 sp_canvas_paint_single_buffer (setup->canvas,
1713 this_rect.x0, this_rect.y0,
1714 this_rect.x1, this_rect.y1,
1715 setup->big_rect.x0, setup->big_rect.y0,
1716 setup->big_rect.x1, setup->big_rect.y1, bw);
1717 return 1;
1718 }
1720 NRRectL lo = this_rect;
1721 NRRectL hi = this_rect;
1723 /*
1724 This test determines the redraw strategy:
1726 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1727 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1728 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1729 and seems to be faster for drawings with many smaller objects at zoom-out.
1731 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1732 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1733 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1734 faster.
1736 The default for now is the strips mode.
1737 */
1738 if (bw < bh || bh < 2 * TILE_SIZE) {
1739 int mid = (this_rect.x0 + this_rect.x1) / 2;
1740 // Make sure that mid lies on a tile boundary
1741 mid = (mid / TILE_SIZE) * TILE_SIZE;
1743 lo.x1 = mid;
1744 hi.x0 = mid;
1746 if (setup->mouse_loc[NR::X] < mid) {
1747 // Always paint towards the mouse first
1748 return sp_canvas_paint_rect_internal(setup, lo)
1749 && sp_canvas_paint_rect_internal(setup, hi);
1750 } else {
1751 return sp_canvas_paint_rect_internal(setup, hi)
1752 && sp_canvas_paint_rect_internal(setup, lo);
1753 }
1754 } else {
1755 int mid = (this_rect.y0 + this_rect.y1) / 2;
1756 // Make sure that mid lies on a tile boundary
1757 mid = (mid / TILE_SIZE) * TILE_SIZE;
1759 lo.y1 = mid;
1760 hi.y0 = mid;
1762 if (setup->mouse_loc[NR::Y] < mid) {
1763 // Always paint towards the mouse first
1764 return sp_canvas_paint_rect_internal(setup, lo)
1765 && sp_canvas_paint_rect_internal(setup, hi);
1766 } else {
1767 return sp_canvas_paint_rect_internal(setup, hi)
1768 && sp_canvas_paint_rect_internal(setup, lo);
1769 }
1770 }
1771 }
1774 /**
1775 * Helper that draws a specific rectangular part of the canvas.
1776 *
1777 * @return true if the rectangle painting succeeds.
1778 */
1779 static bool
1780 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1781 {
1782 g_return_val_if_fail (!canvas->need_update, false);
1784 NRRectL rect;
1785 rect.x0 = xx0;
1786 rect.x1 = xx1;
1787 rect.y0 = yy0;
1788 rect.y1 = yy1;
1790 // Clip rect-to-draw by the current visible area
1791 rect.x0 = MAX (rect.x0, canvas->x0);
1792 rect.y0 = MAX (rect.y0, canvas->y0);
1793 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1794 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1796 #ifdef DEBUG_REDRAW
1797 // paint the area to redraw yellow
1798 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1799 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1800 canvas->pixmap_gc,
1801 TRUE,
1802 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1803 rect.x1 - rect.x0, rect.y1 - rect.y0);
1804 #endif
1806 PaintRectSetup setup;
1808 setup.canvas = canvas;
1809 setup.big_rect = rect;
1811 // Save the mouse location
1812 gint x, y;
1813 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1814 setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1816 // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1817 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1818 // use 256K as a compromise to not slow down gradients
1819 // 256K is the cached buffer and we need 3 channels
1820 setup.max_pixels = 87381; // 256K/3
1821 } else {
1822 // paths only, so 1M works faster
1823 // 1M is the cached buffer and we need 3 channels
1824 setup.max_pixels = 349525;
1825 }
1827 // Start the clock
1828 g_get_current_time(&(setup.start_time));
1830 // Go
1831 return sp_canvas_paint_rect_internal(&setup, rect);
1832 }
1834 /**
1835 * Force a full redraw after a specified number of interrupted redraws
1836 */
1837 void
1838 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1839 g_return_if_fail(canvas != NULL);
1841 canvas->forced_redraw_limit = count;
1842 canvas->forced_redraw_count = 0;
1843 }
1845 /**
1846 * End forced full redraw requests
1847 */
1848 void
1849 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1850 g_return_if_fail(canvas != NULL);
1852 canvas->forced_redraw_limit = -1;
1853 }
1855 /**
1856 * The canvas widget's expose callback.
1857 */
1858 static gint
1859 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1860 {
1861 SPCanvas *canvas = SP_CANVAS (widget);
1863 if (!GTK_WIDGET_DRAWABLE (widget) ||
1864 (event->window != SP_CANVAS_WINDOW (canvas)))
1865 return FALSE;
1867 int n_rects;
1868 GdkRectangle *rects;
1869 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1871 for (int i = 0; i < n_rects; i++) {
1872 NRRectL rect;
1874 rect.x0 = rects[i].x + canvas->x0;
1875 rect.y0 = rects[i].y + canvas->y0;
1876 rect.x1 = rect.x0 + rects[i].width;
1877 rect.y1 = rect.y0 + rects[i].height;
1879 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1880 }
1882 if (n_rects > 0)
1883 g_free (rects);
1885 return FALSE;
1886 }
1888 /**
1889 * The canvas widget's keypress callback.
1890 */
1891 static gint
1892 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1893 {
1894 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1895 }
1897 /**
1898 * Crossing event handler for the canvas.
1899 */
1900 static gint
1901 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1902 {
1903 SPCanvas *canvas = SP_CANVAS (widget);
1905 if (event->window != SP_CANVAS_WINDOW (canvas))
1906 return FALSE;
1908 canvas->state = event->state;
1909 return pick_current_item (canvas, (GdkEvent *) event);
1910 }
1912 /**
1913 * Focus in handler for the canvas.
1914 */
1915 static gint
1916 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1917 {
1918 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1920 SPCanvas *canvas = SP_CANVAS (widget);
1922 if (canvas->focused_item) {
1923 return emit_event (canvas, (GdkEvent *) event);
1924 } else {
1925 return FALSE;
1926 }
1927 }
1929 /**
1930 * Focus out handler for the canvas.
1931 */
1932 static gint
1933 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1934 {
1935 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1937 SPCanvas *canvas = SP_CANVAS (widget);
1939 if (canvas->focused_item)
1940 return emit_event (canvas, (GdkEvent *) event);
1941 else
1942 return FALSE;
1943 }
1945 /**
1946 * Helper that repaints the areas in the canvas that need it.
1947 *
1948 * @return true if all the dirty parts have been redrawn
1949 */
1950 static int
1951 paint (SPCanvas *canvas)
1952 {
1953 if (canvas->need_update) {
1954 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1955 canvas->need_update = FALSE;
1956 }
1958 if (!canvas->need_redraw)
1959 return TRUE;
1961 Gdk::Region to_paint;
1963 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
1964 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
1965 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
1967 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
1968 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
1969 TILE_SIZE, TILE_SIZE));
1970 }
1972 }
1973 }
1975 if (!to_paint.empty()) {
1976 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
1977 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
1978 for (Iter i=rect.begin(); i != rect.end(); ++i) {
1979 int x0 = (*i).get_x();
1980 int y0 = (*i).get_y();
1981 int x1 = x0 + (*i).get_width();
1982 int y1 = y0 + (*i).get_height();
1983 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
1984 // Aborted
1985 return FALSE;
1986 };
1987 }
1988 }
1990 canvas->need_redraw = FALSE;
1992 // we've had a full unaborted redraw, reset the full redraw counter
1993 if (canvas->forced_redraw_limit != -1) {
1994 canvas->forced_redraw_count = 0;
1995 }
1997 return TRUE;
1998 }
2000 /**
2001 * Helper that invokes update, paint, and repick on canvas.
2002 */
2003 static int
2004 do_update (SPCanvas *canvas)
2005 {
2006 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2007 return TRUE;
2009 /* Cause the update if necessary */
2010 if (canvas->need_update) {
2011 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2012 canvas->need_update = FALSE;
2013 }
2015 /* Paint if able to */
2016 if (GTK_WIDGET_DRAWABLE (canvas)) {
2017 return paint (canvas);
2018 }
2020 /* Pick new current item */
2021 while (canvas->need_repick) {
2022 canvas->need_repick = FALSE;
2023 pick_current_item (canvas, &canvas->pick_event);
2024 }
2026 return TRUE;
2027 }
2029 /**
2030 * Idle handler for the canvas that deals with pending updates and redraws.
2031 */
2032 static gint
2033 idle_handler (gpointer data)
2034 {
2035 GDK_THREADS_ENTER ();
2037 SPCanvas *canvas = SP_CANVAS (data);
2039 int const ret = do_update (canvas);
2041 if (ret) {
2042 /* Reset idle id */
2043 canvas->idle_id = 0;
2044 }
2046 GDK_THREADS_LEAVE ();
2048 return !ret;
2049 }
2051 /**
2052 * Convenience function to add an idle handler to a canvas.
2053 */
2054 static void
2055 add_idle (SPCanvas *canvas)
2056 {
2057 if (canvas->idle_id != 0)
2058 return;
2060 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2061 }
2063 /**
2064 * Returns the root group of the specified canvas.
2065 */
2066 SPCanvasGroup *
2067 sp_canvas_root (SPCanvas *canvas)
2068 {
2069 g_return_val_if_fail (canvas != NULL, NULL);
2070 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2072 return SP_CANVAS_GROUP (canvas->root);
2073 }
2075 /**
2076 * Scrolls canvas to specific position.
2077 */
2078 void
2079 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2080 {
2081 g_return_if_fail (canvas != NULL);
2082 g_return_if_fail (SP_IS_CANVAS (canvas));
2084 int ix = (int) (cx + 0.5);
2085 int iy = (int) (cy + 0.5);
2086 int dx = ix - canvas->x0;
2087 int dy = iy - canvas->y0;
2089 canvas->dx0 = cx;
2090 canvas->dy0 = cy;
2091 canvas->x0 = ix;
2092 canvas->y0 = iy;
2094 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2096 if (!clear) {
2097 // scrolling without zoom; redraw only the newly exposed areas
2098 if ((dx != 0) || (dy != 0)) {
2099 canvas->is_scrolling = is_scrolling;
2100 if (GTK_WIDGET_REALIZED (canvas)) {
2101 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2102 }
2103 }
2104 } else {
2105 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2106 }
2108 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
2109 SPEventContext *ec = inkscape_active_event_context();
2110 if (SP_IS_3DBOX_CONTEXT (ec)) {
2111 // We could avoid redraw during panning by checking the status of is_scrolling, but this is
2112 // neither faster nor does it get rid of artefacts, so we update PLs unconditionally
2113 SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec);
2114 bc->_vpdrag->updateLines();
2115 }
2116 }
2118 /**
2119 * Updates canvas if necessary.
2120 */
2121 void
2122 sp_canvas_update_now (SPCanvas *canvas)
2123 {
2124 g_return_if_fail (canvas != NULL);
2125 g_return_if_fail (SP_IS_CANVAS (canvas));
2127 if (!(canvas->need_update ||
2128 canvas->need_redraw))
2129 return;
2131 do_update (canvas);
2132 }
2134 /**
2135 * Update callback for canvas widget.
2136 */
2137 static void
2138 sp_canvas_request_update (SPCanvas *canvas)
2139 {
2140 canvas->need_update = TRUE;
2141 add_idle (canvas);
2142 }
2144 /**
2145 * Forces redraw of rectangular canvas area.
2146 */
2147 void
2148 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2149 {
2150 NRRectL bbox;
2151 NRRectL visible;
2152 NRRectL clip;
2154 g_return_if_fail (canvas != NULL);
2155 g_return_if_fail (SP_IS_CANVAS (canvas));
2157 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2158 if ((x0 >= x1) || (y0 >= y1)) return;
2160 bbox.x0 = x0;
2161 bbox.y0 = y0;
2162 bbox.x1 = x1;
2163 bbox.y1 = y1;
2165 visible.x0 = canvas->x0;
2166 visible.y0 = canvas->y0;
2167 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2168 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2170 nr_rect_l_intersect (&clip, &bbox, &visible);
2172 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2173 add_idle (canvas);
2174 }
2176 /**
2177 * Sets world coordinates from win and canvas.
2178 */
2179 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2180 {
2181 g_return_if_fail (canvas != NULL);
2182 g_return_if_fail (SP_IS_CANVAS (canvas));
2184 if (worldx) *worldx = canvas->x0 + winx;
2185 if (worldy) *worldy = canvas->y0 + winy;
2186 }
2188 /**
2189 * Sets win coordinates from world and canvas.
2190 */
2191 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2192 {
2193 g_return_if_fail (canvas != NULL);
2194 g_return_if_fail (SP_IS_CANVAS (canvas));
2196 if (winx) *winx = worldx - canvas->x0;
2197 if (winy) *winy = worldy - canvas->y0;
2198 }
2200 /**
2201 * Converts point from win to world coordinates.
2202 */
2203 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2204 {
2205 g_assert (canvas != NULL);
2206 g_assert (SP_IS_CANVAS (canvas));
2208 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2209 }
2211 /**
2212 * Converts point from world to win coordinates.
2213 */
2214 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2215 {
2216 g_assert (canvas != NULL);
2217 g_assert (SP_IS_CANVAS (canvas));
2219 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2220 }
2222 /**
2223 * Returns true if point given in world coordinates is inside window.
2224 */
2225 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2226 {
2227 g_assert( canvas != NULL );
2228 g_assert(SP_IS_CANVAS(canvas));
2230 using NR::X;
2231 using NR::Y;
2232 GtkWidget const &w = *GTK_WIDGET(canvas);
2233 return ( ( canvas->x0 <= world[X] ) &&
2234 ( canvas->y0 <= world[Y] ) &&
2235 ( world[X] < canvas->x0 + w.allocation.width ) &&
2236 ( world[Y] < canvas->y0 + w.allocation.height ) );
2237 }
2239 /**
2240 * Return canvas window coordinates as NR::Rect.
2241 */
2242 NR::Rect SPCanvas::getViewbox() const
2243 {
2244 GtkWidget const *w = GTK_WIDGET(this);
2246 return NR::Rect(NR::Point(dx0, dy0),
2247 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2248 }
2250 inline int sp_canvas_tile_floor(int x)
2251 {
2252 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2253 }
2255 inline int sp_canvas_tile_ceil(int x)
2256 {
2257 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2258 }
2260 /**
2261 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2262 */
2263 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2264 {
2265 if ( nl >= nr || nt >= nb ) {
2266 if ( canvas->tiles ) g_free(canvas->tiles);
2267 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2268 canvas->tileH=canvas->tileV=0;
2269 canvas->tiles=NULL;
2270 return;
2271 }
2272 int tl=sp_canvas_tile_floor(nl);
2273 int tt=sp_canvas_tile_floor(nt);
2274 int tr=sp_canvas_tile_ceil(nr);
2275 int tb=sp_canvas_tile_ceil(nb);
2277 int nh = tr-tl, nv = tb-tt;
2278 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2279 for (int i=tl; i<tr; i++) {
2280 for (int j=tt; j<tb; j++) {
2281 int ind = (i-tl) + (j-tt)*nh;
2282 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2283 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2284 } else {
2285 ntiles[ind]=0; // newly exposed areas get 0
2286 }
2287 }
2288 }
2289 if ( canvas->tiles ) g_free(canvas->tiles);
2290 canvas->tiles=ntiles;
2291 canvas->tLeft=tl;
2292 canvas->tTop=tt;
2293 canvas->tRight=tr;
2294 canvas->tBottom=tb;
2295 canvas->tileH=nh;
2296 canvas->tileV=nv;
2297 }
2299 /*
2300 * Helper that queues a canvas rectangle for redraw
2301 */
2302 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2303 canvas->need_redraw = TRUE;
2305 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2306 }
2308 /**
2309 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2310 */
2311 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2312 {
2313 if ( nl >= nr || nt >= nb ) {
2314 return;
2315 }
2316 int tl=sp_canvas_tile_floor(nl);
2317 int tt=sp_canvas_tile_floor(nt);
2318 int tr=sp_canvas_tile_ceil(nr);
2319 int tb=sp_canvas_tile_ceil(nb);
2320 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2321 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2322 if ( tr > canvas->tRight ) tr=canvas->tRight;
2323 if ( tt < canvas->tTop ) tt=canvas->tTop;
2324 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2326 for (int i=tl; i<tr; i++) {
2327 for (int j=tt; j<tb; j++) {
2328 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2329 }
2330 }
2331 }
2334 /*
2335 Local Variables:
2336 mode:c++
2337 c-file-style:"stroustrup"
2338 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2339 indent-tabs-mode:nil
2340 fill-column:99
2341 End:
2342 */
2343 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :