1714bd454e246abcff32c90af93be9f9536588a0
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 const gint 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, const gchar *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 const GTypeInfo 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, const gchar *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, const gchar *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 const GtkTypeInfo 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 const SPCanvasGroup *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 const SPCanvasGroup *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 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
802 const double x = p[NR::X];
803 const double 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 const SPCanvasGroup *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 const GtkTypeInfo 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 cmsHPROFILE hprof = Inkscape::colorprofile_get_system_profile_handle();
1592 cmsHPROFILE srcprof = hprof ? cmsCreate_sRGBProfile() : 0;
1593 cmsHTRANSFORM transf = hprof ? cmsCreateTransform( srcprof, TYPE_RGB_8, hprof, TYPE_RGB_8, INTENT_PERCEPTUAL, 0 ) : 0;
1594 #endif // ENABLE_LCMS
1596 if (buf.is_empty) {
1597 #if ENABLE_LCMS
1598 if ( transf ) {
1599 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1600 }
1601 #endif // ENABLE_LCMS
1602 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1603 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1604 canvas->pixmap_gc,
1605 TRUE,
1606 x0 - canvas->x0, y0 - canvas->y0,
1607 x1 - x0, y1 - y0);
1608 } else {
1609 /*
1610 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1611 // Why this must not be done currently:
1612 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1613 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)
1614 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels,
1615 // we need more bufs to paint a given area and as a result it's even a bit slower
1617 cairo_surface_t* cst = cairo_image_surface_create_for_data (
1618 buf.buf,
1619 CAIRO_FORMAT_RGB24, // unpacked, i.e. 32 bits! one byte is unused
1620 x1 - x0, y1 - y0,
1621 buf.buf_rowstride
1622 );
1623 cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1624 cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1625 cairo_paint (ct);
1626 cairo_destroy (ct);
1627 cairo_surface_finish (cst);
1628 cairo_surface_destroy (cst);
1629 */
1631 #if ENABLE_LCMS
1632 if ( transf ) {
1633 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1634 guchar* p = buf.buf + (sw * 3) * yy;
1635 cmsDoTransform( transf, p, p, (x1 - x0) );
1636 }
1637 }
1638 #endif // ENABLE_LCMS
1640 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1641 canvas->pixmap_gc,
1642 x0 - canvas->x0, y0 - canvas->y0,
1643 x1 - x0, y1 - y0,
1644 GDK_RGB_DITHER_MAX,
1645 buf.buf,
1646 sw * 3,
1647 x0 - canvas->x0, y0 - canvas->y0);
1648 }
1650 #if ENABLE_LCMS
1651 if ( transf ) {
1652 cmsDeleteTransform( transf );
1653 transf = 0;
1654 }
1655 #endif // ENABLE_LCMS
1658 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1659 nr_pixelstore_256K_free (buf.buf);
1660 } else {
1661 nr_pixelstore_1M_free (buf.buf);
1662 }
1663 }
1665 struct PaintRectSetup {
1666 SPCanvas* canvas;
1667 NRRectL big_rect;
1668 GTimeVal start_time;
1669 int max_pixels;
1670 NR::Point mouse_loc;
1671 };
1673 /**
1674 * Paint the given rect, recursively subdividing the region until it is the size of a single
1675 * buffer.
1676 *
1677 * @return true if the drawing completes
1678 */
1679 static int
1680 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1681 {
1682 GTimeVal now;
1683 g_get_current_time (&now);
1685 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1686 + (now.tv_usec - setup->start_time.tv_usec);
1688 // Allow only very fast buffers to be run together;
1689 // as soon as the total redraw time exceeds 1ms, cancel;
1690 // this returns control to the idle loop and allows Inkscape to process user input
1691 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1692 // it will get back and finish painting what remains to paint.
1693 if (elapsed > 1000) {
1695 // Interrupting redraw isn't always good.
1696 // For example, when you drag one node of a big path, only the buffer containing
1697 // the mouse cursor will be redrawn again and again, and the rest of the path
1698 // will remain stale because Inkscape never has enough idle time to redraw all
1699 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1700 // If this limit is set, and if we have aborted redraw more times than is allowed,
1701 // interrupting is blocked and we're forced to redraw full screen once
1702 // (after which we can again interrupt forced_redraw_limit times).
1703 if (setup->canvas->forced_redraw_limit < 0 ||
1704 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1706 if (setup->canvas->forced_redraw_limit != -1) {
1707 setup->canvas->forced_redraw_count++;
1708 }
1710 return false;
1711 }
1712 }
1714 // Find the optimal buffer dimensions
1715 int bw = this_rect.x1 - this_rect.x0;
1716 int bh = this_rect.y1 - this_rect.y0;
1717 if ((bw < 1) || (bh < 1))
1718 return 0;
1720 if (bw * bh < setup->max_pixels) {
1721 // We are small enough
1722 sp_canvas_paint_single_buffer (setup->canvas,
1723 this_rect.x0, this_rect.y0,
1724 this_rect.x1, this_rect.y1,
1725 setup->big_rect.x0, setup->big_rect.y0,
1726 setup->big_rect.x1, setup->big_rect.y1, bw);
1727 return 1;
1728 }
1730 NRRectL lo = this_rect;
1731 NRRectL hi = this_rect;
1733 /*
1734 This test determines the redraw strategy:
1736 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1737 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1738 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1739 and seems to be faster for drawings with many smaller objects at zoom-out.
1741 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1742 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1743 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1744 faster.
1746 The default for now is the strips mode.
1747 */
1748 if (bw < bh || bh < 2 * TILE_SIZE) {
1749 int mid = (this_rect.x0 + this_rect.x1) / 2;
1750 // Make sure that mid lies on a tile boundary
1751 mid = (mid / TILE_SIZE) * TILE_SIZE;
1753 lo.x1 = mid;
1754 hi.x0 = mid;
1756 if (setup->mouse_loc[NR::X] < mid) {
1757 // Always paint towards the mouse first
1758 return sp_canvas_paint_rect_internal(setup, lo)
1759 && sp_canvas_paint_rect_internal(setup, hi);
1760 } else {
1761 return sp_canvas_paint_rect_internal(setup, hi)
1762 && sp_canvas_paint_rect_internal(setup, lo);
1763 }
1764 } else {
1765 int mid = (this_rect.y0 + this_rect.y1) / 2;
1766 // Make sure that mid lies on a tile boundary
1767 mid = (mid / TILE_SIZE) * TILE_SIZE;
1769 lo.y1 = mid;
1770 hi.y0 = mid;
1772 if (setup->mouse_loc[NR::Y] < mid) {
1773 // Always paint towards the mouse first
1774 return sp_canvas_paint_rect_internal(setup, lo)
1775 && sp_canvas_paint_rect_internal(setup, hi);
1776 } else {
1777 return sp_canvas_paint_rect_internal(setup, hi)
1778 && sp_canvas_paint_rect_internal(setup, lo);
1779 }
1780 }
1781 }
1784 /**
1785 * Helper that draws a specific rectangular part of the canvas.
1786 *
1787 * @return true if the rectangle painting succeeds.
1788 */
1789 static bool
1790 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1791 {
1792 g_return_val_if_fail (!canvas->need_update, false);
1794 NRRectL rect;
1795 rect.x0 = xx0;
1796 rect.x1 = xx1;
1797 rect.y0 = yy0;
1798 rect.y1 = yy1;
1800 // Clip rect-to-draw by the current visible area
1801 rect.x0 = MAX (rect.x0, canvas->x0);
1802 rect.y0 = MAX (rect.y0, canvas->y0);
1803 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1804 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1806 #ifdef DEBUG_REDRAW
1807 // paint the area to redraw yellow
1808 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1809 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1810 canvas->pixmap_gc,
1811 TRUE,
1812 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1813 rect.x1 - rect.x0, rect.y1 - rect.y0);
1814 #endif
1816 PaintRectSetup setup;
1818 setup.canvas = canvas;
1819 setup.big_rect = rect;
1821 // Save the mouse location
1822 gint x, y;
1823 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1824 setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1826 // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1827 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1828 // use 256K as a compromise to not slow down gradients
1829 // 256K is the cached buffer and we need 3 channels
1830 setup.max_pixels = 87381; // 256K/3
1831 } else {
1832 // paths only, so 1M works faster
1833 // 1M is the cached buffer and we need 3 channels
1834 setup.max_pixels = 349525;
1835 }
1837 // Start the clock
1838 g_get_current_time(&(setup.start_time));
1840 // Go
1841 return sp_canvas_paint_rect_internal(&setup, rect);
1842 }
1844 /**
1845 * Force a full redraw after a specified number of interrupted redraws
1846 */
1847 void
1848 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1849 g_return_if_fail(canvas != NULL);
1851 canvas->forced_redraw_limit = count;
1852 canvas->forced_redraw_count = 0;
1853 }
1855 /**
1856 * End forced full redraw requests
1857 */
1858 void
1859 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1860 g_return_if_fail(canvas != NULL);
1862 canvas->forced_redraw_limit = -1;
1863 }
1865 /**
1866 * The canvas widget's expose callback.
1867 */
1868 static gint
1869 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1870 {
1871 SPCanvas *canvas = SP_CANVAS (widget);
1873 if (!GTK_WIDGET_DRAWABLE (widget) ||
1874 (event->window != SP_CANVAS_WINDOW (canvas)))
1875 return FALSE;
1877 int n_rects;
1878 GdkRectangle *rects;
1879 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1881 for (int i = 0; i < n_rects; i++) {
1882 NRRectL rect;
1884 rect.x0 = rects[i].x + canvas->x0;
1885 rect.y0 = rects[i].y + canvas->y0;
1886 rect.x1 = rect.x0 + rects[i].width;
1887 rect.y1 = rect.y0 + rects[i].height;
1889 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1890 }
1892 if (n_rects > 0)
1893 g_free (rects);
1895 return FALSE;
1896 }
1898 /**
1899 * The canvas widget's keypress callback.
1900 */
1901 static gint
1902 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1903 {
1904 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1905 }
1907 /**
1908 * Crossing event handler for the canvas.
1909 */
1910 static gint
1911 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1912 {
1913 SPCanvas *canvas = SP_CANVAS (widget);
1915 if (event->window != SP_CANVAS_WINDOW (canvas))
1916 return FALSE;
1918 canvas->state = event->state;
1919 return pick_current_item (canvas, (GdkEvent *) event);
1920 }
1922 /**
1923 * Focus in handler for the canvas.
1924 */
1925 static gint
1926 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1927 {
1928 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1930 SPCanvas *canvas = SP_CANVAS (widget);
1932 if (canvas->focused_item) {
1933 return emit_event (canvas, (GdkEvent *) event);
1934 } else {
1935 return FALSE;
1936 }
1937 }
1939 /**
1940 * Focus out handler for the canvas.
1941 */
1942 static gint
1943 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1944 {
1945 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1947 SPCanvas *canvas = SP_CANVAS (widget);
1949 if (canvas->focused_item)
1950 return emit_event (canvas, (GdkEvent *) event);
1951 else
1952 return FALSE;
1953 }
1955 /**
1956 * Helper that repaints the areas in the canvas that need it.
1957 *
1958 * @return true if all the dirty parts have been redrawn
1959 */
1960 static int
1961 paint (SPCanvas *canvas)
1962 {
1963 if (canvas->need_update) {
1964 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1965 canvas->need_update = FALSE;
1966 }
1968 if (!canvas->need_redraw)
1969 return TRUE;
1971 Gdk::Region to_paint;
1973 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
1974 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
1975 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
1977 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
1978 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
1979 TILE_SIZE, TILE_SIZE));
1980 }
1982 }
1983 }
1985 if (!to_paint.empty()) {
1986 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
1987 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
1988 for (Iter i=rect.begin(); i != rect.end(); ++i) {
1989 int x0 = (*i).get_x();
1990 int y0 = (*i).get_y();
1991 int x1 = x0 + (*i).get_width();
1992 int y1 = y0 + (*i).get_height();
1993 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
1994 // Aborted
1995 return FALSE;
1996 };
1997 }
1998 }
2000 canvas->need_redraw = FALSE;
2002 // we've had a full unaborted redraw, reset the full redraw counter
2003 if (canvas->forced_redraw_limit != -1) {
2004 canvas->forced_redraw_count = 0;
2005 }
2007 return TRUE;
2008 }
2010 /**
2011 * Helper that invokes update, paint, and repick on canvas.
2012 */
2013 static int
2014 do_update (SPCanvas *canvas)
2015 {
2016 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2017 return TRUE;
2019 /* Cause the update if necessary */
2020 if (canvas->need_update) {
2021 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2022 canvas->need_update = FALSE;
2023 }
2025 /* Paint if able to */
2026 if (GTK_WIDGET_DRAWABLE (canvas)) {
2027 return paint (canvas);
2028 }
2030 /* Pick new current item */
2031 while (canvas->need_repick) {
2032 canvas->need_repick = FALSE;
2033 pick_current_item (canvas, &canvas->pick_event);
2034 }
2036 return TRUE;
2037 }
2039 /**
2040 * Idle handler for the canvas that deals with pending updates and redraws.
2041 */
2042 static gint
2043 idle_handler (gpointer data)
2044 {
2045 GDK_THREADS_ENTER ();
2047 SPCanvas *canvas = SP_CANVAS (data);
2049 const int ret = do_update (canvas);
2051 if (ret) {
2052 /* Reset idle id */
2053 canvas->idle_id = 0;
2054 }
2056 GDK_THREADS_LEAVE ();
2058 return !ret;
2059 }
2061 /**
2062 * Convenience function to add an idle handler to a canvas.
2063 */
2064 static void
2065 add_idle (SPCanvas *canvas)
2066 {
2067 if (canvas->idle_id != 0)
2068 return;
2070 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2071 }
2073 /**
2074 * Returns the root group of the specified canvas.
2075 */
2076 SPCanvasGroup *
2077 sp_canvas_root (SPCanvas *canvas)
2078 {
2079 g_return_val_if_fail (canvas != NULL, NULL);
2080 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2082 return SP_CANVAS_GROUP (canvas->root);
2083 }
2085 /**
2086 * Scrolls canvas to specific position.
2087 */
2088 void
2089 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2090 {
2091 g_return_if_fail (canvas != NULL);
2092 g_return_if_fail (SP_IS_CANVAS (canvas));
2094 int ix = (int) (cx + 0.5);
2095 int iy = (int) (cy + 0.5);
2096 int dx = ix - canvas->x0;
2097 int dy = iy - canvas->y0;
2099 canvas->dx0 = cx;
2100 canvas->dy0 = cy;
2101 canvas->x0 = ix;
2102 canvas->y0 = iy;
2104 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2106 if (!clear) {
2107 // scrolling without zoom; redraw only the newly exposed areas
2108 if ((dx != 0) || (dy != 0)) {
2109 canvas->is_scrolling = is_scrolling;
2110 if (GTK_WIDGET_REALIZED (canvas)) {
2111 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2112 }
2113 }
2114 } else {
2115 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2116 }
2118 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
2119 SPEventContext *ec = inkscape_active_event_context();
2120 if (SP_IS_3DBOX_CONTEXT (ec)) {
2121 // We could avoid redraw during panning by checking the status of is_scrolling, but this is
2122 // neither faster nor does it get rid of artefacts, so we update PLs unconditionally
2123 SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec);
2124 bc->_vpdrag->updateLines();
2125 }
2126 }
2128 /**
2129 * Updates canvas if necessary.
2130 */
2131 void
2132 sp_canvas_update_now (SPCanvas *canvas)
2133 {
2134 g_return_if_fail (canvas != NULL);
2135 g_return_if_fail (SP_IS_CANVAS (canvas));
2137 if (!(canvas->need_update ||
2138 canvas->need_redraw))
2139 return;
2141 do_update (canvas);
2142 }
2144 /**
2145 * Update callback for canvas widget.
2146 */
2147 static void
2148 sp_canvas_request_update (SPCanvas *canvas)
2149 {
2150 canvas->need_update = TRUE;
2151 add_idle (canvas);
2152 }
2154 /**
2155 * Forces redraw of rectangular canvas area.
2156 */
2157 void
2158 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2159 {
2160 NRRectL bbox;
2161 NRRectL visible;
2162 NRRectL clip;
2164 g_return_if_fail (canvas != NULL);
2165 g_return_if_fail (SP_IS_CANVAS (canvas));
2167 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2168 if ((x0 >= x1) || (y0 >= y1)) return;
2170 bbox.x0 = x0;
2171 bbox.y0 = y0;
2172 bbox.x1 = x1;
2173 bbox.y1 = y1;
2175 visible.x0 = canvas->x0;
2176 visible.y0 = canvas->y0;
2177 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2178 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2180 nr_rect_l_intersect (&clip, &bbox, &visible);
2182 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2183 add_idle (canvas);
2184 }
2186 /**
2187 * Sets world coordinates from win and canvas.
2188 */
2189 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2190 {
2191 g_return_if_fail (canvas != NULL);
2192 g_return_if_fail (SP_IS_CANVAS (canvas));
2194 if (worldx) *worldx = canvas->x0 + winx;
2195 if (worldy) *worldy = canvas->y0 + winy;
2196 }
2198 /**
2199 * Sets win coordinates from world and canvas.
2200 */
2201 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2202 {
2203 g_return_if_fail (canvas != NULL);
2204 g_return_if_fail (SP_IS_CANVAS (canvas));
2206 if (winx) *winx = worldx - canvas->x0;
2207 if (winy) *winy = worldy - canvas->y0;
2208 }
2210 /**
2211 * Converts point from win to world coordinates.
2212 */
2213 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2214 {
2215 g_assert (canvas != NULL);
2216 g_assert (SP_IS_CANVAS (canvas));
2218 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2219 }
2221 /**
2222 * Converts point from world to win coordinates.
2223 */
2224 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2225 {
2226 g_assert (canvas != NULL);
2227 g_assert (SP_IS_CANVAS (canvas));
2229 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2230 }
2232 /**
2233 * Returns true if point given in world coordinates is inside window.
2234 */
2235 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2236 {
2237 g_assert( canvas != NULL );
2238 g_assert(SP_IS_CANVAS(canvas));
2240 using NR::X;
2241 using NR::Y;
2242 GtkWidget const &w = *GTK_WIDGET(canvas);
2243 return ( ( canvas->x0 <= world[X] ) &&
2244 ( canvas->y0 <= world[Y] ) &&
2245 ( world[X] < canvas->x0 + w.allocation.width ) &&
2246 ( world[Y] < canvas->y0 + w.allocation.height ) );
2247 }
2249 /**
2250 * Return canvas window coordinates as NR::Rect.
2251 */
2252 NR::Rect SPCanvas::getViewbox() const
2253 {
2254 GtkWidget const *w = GTK_WIDGET(this);
2256 return NR::Rect(NR::Point(dx0, dy0),
2257 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2258 }
2260 inline int sp_canvas_tile_floor(int x)
2261 {
2262 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2263 }
2265 inline int sp_canvas_tile_ceil(int x)
2266 {
2267 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2268 }
2270 /**
2271 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2272 */
2273 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2274 {
2275 if ( nl >= nr || nt >= nb ) {
2276 if ( canvas->tiles ) g_free(canvas->tiles);
2277 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2278 canvas->tileH=canvas->tileV=0;
2279 canvas->tiles=NULL;
2280 return;
2281 }
2282 int tl=sp_canvas_tile_floor(nl);
2283 int tt=sp_canvas_tile_floor(nt);
2284 int tr=sp_canvas_tile_ceil(nr);
2285 int tb=sp_canvas_tile_ceil(nb);
2287 int nh = tr-tl, nv = tb-tt;
2288 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2289 for (int i=tl; i<tr; i++) {
2290 for (int j=tt; j<tb; j++) {
2291 int ind = (i-tl) + (j-tt)*nh;
2292 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2293 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2294 } else {
2295 ntiles[ind]=0; // newly exposed areas get 0
2296 }
2297 }
2298 }
2299 if ( canvas->tiles ) g_free(canvas->tiles);
2300 canvas->tiles=ntiles;
2301 canvas->tLeft=tl;
2302 canvas->tTop=tt;
2303 canvas->tRight=tr;
2304 canvas->tBottom=tb;
2305 canvas->tileH=nh;
2306 canvas->tileV=nv;
2307 }
2309 /*
2310 * Helper that queues a canvas rectangle for redraw
2311 */
2312 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2313 canvas->need_redraw = TRUE;
2315 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2316 }
2318 /**
2319 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2320 */
2321 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2322 {
2323 if ( nl >= nr || nt >= nb ) {
2324 return;
2325 }
2326 int tl=sp_canvas_tile_floor(nl);
2327 int tt=sp_canvas_tile_floor(nt);
2328 int tr=sp_canvas_tile_ceil(nr);
2329 int tb=sp_canvas_tile_ceil(nb);
2330 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2331 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2332 if ( tr > canvas->tRight ) tr=canvas->tRight;
2333 if ( tt < canvas->tTop ) tt=canvas->tTop;
2334 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2336 for (int i=tl; i<tr; i++) {
2337 for (int j=tt; j<tb; j++) {
2338 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2339 }
2340 }
2341 }
2344 /*
2345 Local Variables:
2346 mode:c++
2347 c-file-style:"stroustrup"
2348 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2349 indent-tabs-mode:nil
2350 fill-column:99
2351 End:
2352 */
2353 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :