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"
40 // Define this to visualize the regions to be redrawn
41 //#define DEBUG_REDRAW 1;
43 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
44 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
45 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
46 #define TILE_SIZE 32
48 enum {
49 RENDERMODE_NORMAL,
50 RENDERMODE_NOAA,
51 RENDERMODE_OUTLINE
52 };
54 const gint sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
56 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
58 enum {
59 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
60 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
61 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
62 };
64 /**
65 * A group of Items.
66 */
67 struct SPCanvasGroup {
68 SPCanvasItem item;
70 GList *items, *last;
71 };
73 /**
74 * The SPCanvasGroup vtable.
75 */
76 struct SPCanvasGroupClass {
77 SPCanvasItemClass parent_class;
78 };
80 /**
81 * The SPCanvas vtable.
82 */
83 struct SPCanvasClass {
84 GtkWidgetClass parent_class;
85 };
87 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
88 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
90 /* SPCanvasItem */
92 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
95 static void sp_canvas_request_update (SPCanvas *canvas);
97 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
98 static void sp_canvas_item_init (SPCanvasItem *item);
99 static void sp_canvas_item_dispose (GObject *object);
100 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args);
102 static int emit_event (SPCanvas *canvas, GdkEvent *event);
104 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
106 static GtkObjectClass *item_parent_class;
108 /**
109 * Registers the SPCanvasItem class with Glib and returns its type number.
110 */
111 GType
112 sp_canvas_item_get_type (void)
113 {
114 static GType type = 0;
115 if (!type) {
116 static const GTypeInfo info = {
117 sizeof (SPCanvasItemClass),
118 NULL, NULL,
119 (GClassInitFunc) sp_canvas_item_class_init,
120 NULL, NULL,
121 sizeof (SPCanvasItem),
122 0,
123 (GInstanceInitFunc) sp_canvas_item_init,
124 NULL
125 };
126 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
127 }
129 return type;
130 }
132 /**
133 * Initializes the SPCanvasItem vtable and the "event" signal.
134 */
135 static void
136 sp_canvas_item_class_init (SPCanvasItemClass *klass)
137 {
138 GObjectClass *object_class = (GObjectClass *) klass;
140 /* fixme: Derive from GObject */
141 item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
143 item_signals[ITEM_EVENT] = g_signal_new ("event",
144 G_TYPE_FROM_CLASS (klass),
145 G_SIGNAL_RUN_LAST,
146 G_STRUCT_OFFSET (SPCanvasItemClass, event),
147 NULL, NULL,
148 sp_marshal_BOOLEAN__POINTER,
149 G_TYPE_BOOLEAN, 1,
150 GDK_TYPE_EVENT);
152 object_class->dispose = sp_canvas_item_dispose;
153 }
155 /**
156 * Callback for initialization of SPCanvasItem.
157 */
158 static void
159 sp_canvas_item_init (SPCanvasItem *item)
160 {
161 item->flags |= SP_CANVAS_ITEM_VISIBLE;
162 item->xform = NR::Matrix(NR::identity());
163 }
165 /**
166 * Constructs new SPCanvasItem on SPCanvasGroup.
167 */
168 SPCanvasItem *
169 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, const gchar *first_arg_name, ...)
170 {
171 va_list args;
173 g_return_val_if_fail (parent != NULL, NULL);
174 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
175 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
177 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
179 va_start (args, first_arg_name);
180 sp_canvas_item_construct (item, parent, first_arg_name, args);
181 va_end (args);
183 return item;
184 }
186 /**
187 * Sets up the newly created SPCanvasItem.
188 *
189 * We make it static for encapsulation reasons since it was nowhere used.
190 */
191 static void
192 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args)
193 {
194 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
195 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
197 item->parent = SP_CANVAS_ITEM (parent);
198 item->canvas = item->parent->canvas;
200 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
202 group_add (SP_CANVAS_GROUP (item->parent), item);
204 sp_canvas_item_request_update (item);
205 }
207 /**
208 * Helper function that requests redraw only if item's visible flag is set.
209 */
210 static void
211 redraw_if_visible (SPCanvasItem *item)
212 {
213 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
214 int x0 = (int)(item->x1);
215 int x1 = (int)(item->x2);
216 int y0 = (int)(item->y1);
217 int y1 = (int)(item->y2);
219 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
220 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
221 }
222 }
223 }
225 /**
226 * Callback that removes item from all referers and destroys it.
227 */
228 static void
229 sp_canvas_item_dispose (GObject *object)
230 {
231 SPCanvasItem *item = SP_CANVAS_ITEM (object);
233 redraw_if_visible (item);
234 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
236 if (item == item->canvas->current_item) {
237 item->canvas->current_item = NULL;
238 item->canvas->need_repick = TRUE;
239 }
241 if (item == item->canvas->new_current_item) {
242 item->canvas->new_current_item = NULL;
243 item->canvas->need_repick = TRUE;
244 }
246 if (item == item->canvas->grabbed_item) {
247 item->canvas->grabbed_item = NULL;
248 gdk_pointer_ungrab (GDK_CURRENT_TIME);
249 }
251 if (item == item->canvas->focused_item)
252 item->canvas->focused_item = NULL;
254 if (item->parent) {
255 group_remove (SP_CANVAS_GROUP (item->parent), item);
256 }
258 G_OBJECT_CLASS (item_parent_class)->dispose (object);
259 }
261 /**
262 * Helper function to update item and its children.
263 *
264 * NB! affine is parent2canvas.
265 */
266 static void
267 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
268 {
269 /* Apply the child item's transform */
270 NR::Matrix child_affine = item->xform * affine;
272 /* apply object flags to child flags */
273 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
275 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
276 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
278 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
279 child_flags |= SP_CANVAS_UPDATE_AFFINE;
281 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
282 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
283 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
284 }
286 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
287 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
288 }
290 /**
291 * Helper function to invoke the point method of the item.
292 *
293 * The argument x, y should be in the parent's item-relative coordinate
294 * system. This routine applies the inverse of the item's transform,
295 * maintaining the affine invariant.
296 */
297 static double
298 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
299 {
300 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
301 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
303 return NR_HUGE;
304 }
306 /**
307 * Makes the item's affine transformation matrix be equal to the specified
308 * matrix.
309 *
310 * @item: A canvas item.
311 * @affine: An affine transformation matrix.
312 */
313 void
314 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const& affine)
315 {
316 item->xform = affine;
318 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
319 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
320 if (item->parent != NULL) {
321 sp_canvas_item_request_update (item->parent);
322 } else {
323 sp_canvas_request_update (item->canvas);
324 }
325 }
327 item->canvas->need_repick = TRUE;
328 }
330 /**
331 * Convenience function to reorder items in a group's child list.
332 *
333 * This puts the specified link after the "before" link.
334 */
335 static void
336 put_item_after (GList *link, GList *before)
337 {
338 if (link == before)
339 return;
341 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
343 if (before == NULL) {
344 if (link == parent->items) return;
346 link->prev->next = link->next;
348 if (link->next) {
349 link->next->prev = link->prev;
350 } else {
351 parent->last = link->prev;
352 }
354 link->prev = before;
355 link->next = parent->items;
356 link->next->prev = link;
357 parent->items = link;
358 } else {
359 if ((link == parent->last) && (before == parent->last->prev))
360 return;
362 if (link->next)
363 link->next->prev = link->prev;
365 if (link->prev)
366 link->prev->next = link->next;
367 else {
368 parent->items = link->next;
369 parent->items->prev = NULL;
370 }
372 link->prev = before;
373 link->next = before->next;
375 link->prev->next = link;
377 if (link->next)
378 link->next->prev = link;
379 else
380 parent->last = link;
381 }
382 }
385 /**
386 * Raises the item in its parent's stack by the specified number of positions.
387 *
388 * \param item A canvas item.
389 * \param positions Number of steps to raise the item.
390 *
391 * If the number of positions is greater than the distance to the top of the
392 * stack, then the item is put at the top.
393 */
394 void
395 sp_canvas_item_raise (SPCanvasItem *item, int positions)
396 {
397 g_return_if_fail (item != NULL);
398 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
399 g_return_if_fail (positions >= 0);
401 if (!item->parent || positions == 0)
402 return;
404 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
405 GList *link = g_list_find (parent->items, item);
406 g_assert (link != NULL);
408 GList *before;
409 for (before = link; positions && before; positions--)
410 before = before->next;
412 if (!before)
413 before = parent->last;
415 put_item_after (link, before);
417 redraw_if_visible (item);
418 item->canvas->need_repick = TRUE;
419 }
422 /**
423 * Lowers the item in its parent's stack by the specified number of positions.
424 *
425 * \param item A canvas item.
426 * \param positions Number of steps to lower the item.
427 *
428 * If the number of positions is greater than the distance to the bottom of the
429 * stack, then the item is put at the bottom.
430 **/
431 void
432 sp_canvas_item_lower (SPCanvasItem *item, int positions)
433 {
434 g_return_if_fail (item != NULL);
435 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
436 g_return_if_fail (positions >= 1);
438 if (!item->parent || positions == 0)
439 return;
441 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
442 GList *link = g_list_find (parent->items, item);
443 g_assert (link != NULL);
445 GList *before;
446 if (link->prev)
447 for (before = link->prev; positions && before; positions--)
448 before = before->prev;
449 else
450 before = NULL;
452 put_item_after (link, before);
454 redraw_if_visible (item);
455 item->canvas->need_repick = TRUE;
456 }
458 /**
459 * Sets visible flag on item and requests a redraw.
460 */
461 void
462 sp_canvas_item_show (SPCanvasItem *item)
463 {
464 g_return_if_fail (item != NULL);
465 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
467 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
468 return;
470 item->flags |= SP_CANVAS_ITEM_VISIBLE;
472 int x0 = (int)(item->x1);
473 int x1 = (int)(item->x2);
474 int y0 = (int)(item->y1);
475 int y1 = (int)(item->y2);
477 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
478 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
479 item->canvas->need_repick = TRUE;
480 }
481 }
483 /**
484 * Clears visible flag on item and requests a redraw.
485 */
486 void
487 sp_canvas_item_hide (SPCanvasItem *item)
488 {
489 g_return_if_fail (item != NULL);
490 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
492 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
493 return;
495 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
497 int x0 = (int)(item->x1);
498 int x1 = (int)(item->x2);
499 int y0 = (int)(item->y1);
500 int y1 = (int)(item->y2);
502 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
503 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
504 item->canvas->need_repick = TRUE;
505 }
506 }
508 /**
509 * Grab item under cursor.
510 *
511 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
512 */
513 int
514 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
515 {
516 g_return_val_if_fail (item != NULL, -1);
517 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
518 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
520 if (item->canvas->grabbed_item)
521 return -1;
523 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
524 return -1;
526 /* fixme: Top hack (Lauris) */
527 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
528 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
529 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
530 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
531 NULL, cursor, etime);
533 item->canvas->grabbed_item = item;
534 item->canvas->grabbed_event_mask = event_mask;
535 item->canvas->current_item = item; /* So that events go to the grabbed item */
537 return 0;
538 }
540 /**
541 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
542 * mouse.
543 *
544 * \param item A canvas item that holds a grab.
545 * \param etime The timestamp for ungrabbing the mouse.
546 */
547 void
548 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
549 {
550 g_return_if_fail (item != NULL);
551 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
553 if (item->canvas->grabbed_item != item)
554 return;
556 item->canvas->grabbed_item = NULL;
558 gdk_pointer_ungrab (etime);
559 }
561 /**
562 * Returns the product of all transformation matrices from the root item down
563 * to the item.
564 */
565 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
566 {
567 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
569 NR::Matrix affine = NR::identity();
571 while (item) {
572 affine *= item->xform;
573 item = item->parent;
574 }
575 return affine;
576 }
578 /**
579 * Helper that returns true iff item is descendant of parent.
580 */
581 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
582 {
583 while (item) {
584 if (item == parent)
585 return true;
586 item = item->parent;
587 }
589 return false;
590 }
592 /**
593 * Focus canvas, and item under cursor if it is not already focussed.
594 */
595 void
596 sp_canvas_item_grab_focus (SPCanvasItem *item)
597 {
598 g_return_if_fail (item != NULL);
599 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
600 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
602 SPCanvasItem *focused_item = item->canvas->focused_item;
604 if (focused_item) {
605 GdkEvent ev;
606 ev.focus_change.type = GDK_FOCUS_CHANGE;
607 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
608 ev.focus_change.send_event = FALSE;
609 ev.focus_change.in = FALSE;
611 emit_event (item->canvas, &ev);
612 }
614 item->canvas->focused_item = item;
615 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
617 if (focused_item) {
618 GdkEvent ev;
619 ev.focus_change.type = GDK_FOCUS_CHANGE;
620 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
621 ev.focus_change.send_event = FALSE;
622 ev.focus_change.in = TRUE;
624 emit_event (item->canvas, &ev);
625 }
626 }
628 /**
629 * Requests that the canvas queue an update for the specified item.
630 *
631 * To be used only by item implementations.
632 */
633 void
634 sp_canvas_item_request_update (SPCanvasItem *item)
635 {
636 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
637 return;
639 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
641 if (item->parent != NULL) {
642 /* Recurse up the tree */
643 sp_canvas_item_request_update (item->parent);
644 } else {
645 /* Have reached the top of the tree, make sure the update call gets scheduled. */
646 sp_canvas_request_update (item->canvas);
647 }
648 }
650 /**
651 * Returns position of item in group.
652 */
653 gint sp_canvas_item_order (SPCanvasItem * item)
654 {
655 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
656 }
658 /* SPCanvasGroup */
660 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
661 static void sp_canvas_group_init (SPCanvasGroup *group);
662 static void sp_canvas_group_destroy (GtkObject *object);
664 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
665 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
666 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
668 static SPCanvasItemClass *group_parent_class;
670 /**
671 * Registers SPCanvasGroup class with Gtk and returns its type number.
672 */
673 GtkType
674 sp_canvas_group_get_type (void)
675 {
676 static GtkType group_type = 0;
678 if (!group_type) {
679 static const GtkTypeInfo group_info = {
680 "SPCanvasGroup",
681 sizeof (SPCanvasGroup),
682 sizeof (SPCanvasGroupClass),
683 (GtkClassInitFunc) sp_canvas_group_class_init,
684 (GtkObjectInitFunc) sp_canvas_group_init,
685 NULL, NULL, NULL
686 };
688 group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
689 }
691 return group_type;
692 }
694 /**
695 * Class initialization function for SPCanvasGroupClass
696 */
697 static void
698 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
699 {
700 GtkObjectClass *object_class = (GtkObjectClass *) klass;
701 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
703 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
705 object_class->destroy = sp_canvas_group_destroy;
707 item_class->update = sp_canvas_group_update;
708 item_class->render = sp_canvas_group_render;
709 item_class->point = sp_canvas_group_point;
710 }
712 /**
713 * Callback. Empty.
714 */
715 static void
716 sp_canvas_group_init (SPCanvasGroup */*group*/)
717 {
718 /* Nothing here */
719 }
721 /**
722 * Callback that destroys all items in group and calls group's virtual
723 * destroy() function.
724 */
725 static void
726 sp_canvas_group_destroy (GtkObject *object)
727 {
728 g_return_if_fail (object != NULL);
729 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
731 const SPCanvasGroup *group = SP_CANVAS_GROUP (object);
733 GList *list = group->items;
734 while (list) {
735 SPCanvasItem *child = (SPCanvasItem *)list->data;
736 list = list->next;
738 gtk_object_destroy (GTK_OBJECT (child));
739 }
741 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
742 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
743 }
745 /**
746 * Update handler for canvas groups
747 */
748 static void
749 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
750 {
751 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
752 NR::ConvexHull corners(NR::Point(0, 0));
753 bool empty=true;
755 for (GList *list = group->items; list; list = list->next) {
756 SPCanvasItem *i = (SPCanvasItem *)list->data;
758 sp_canvas_item_invoke_update (i, affine, flags);
760 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
761 if (empty) {
762 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
763 empty = false;
764 } else {
765 corners.add(NR::Point(i->x1, i->y1));
766 }
767 corners.add(NR::Point(i->x2, i->y2));
768 }
769 }
771 NR::Maybe<NR::Rect> const bounds = corners.bounds();
772 if (bounds) {
773 item->x1 = bounds->min()[NR::X];
774 item->y1 = bounds->min()[NR::Y];
775 item->x2 = bounds->max()[NR::X];
776 item->y2 = bounds->max()[NR::Y];
777 } else {
778 // FIXME ?
779 item->x1 = item->x2 = item->y1 = item->y2 = 0;
780 }
781 }
783 /**
784 * Point handler for canvas groups.
785 */
786 static double
787 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
788 {
789 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
790 const double x = p[NR::X];
791 const double y = p[NR::Y];
792 int x1 = (int)(x - item->canvas->close_enough);
793 int y1 = (int)(y - item->canvas->close_enough);
794 int x2 = (int)(x + item->canvas->close_enough);
795 int y2 = (int)(y + item->canvas->close_enough);
797 double best = 0.0;
798 *actual_item = NULL;
800 double dist = 0.0;
802 for (GList *list = group->items; list; list = list->next) {
803 SPCanvasItem *child = (SPCanvasItem *)list->data;
805 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
806 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
808 int has_point;
809 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
810 dist = sp_canvas_item_invoke_point (child, p, &point_item);
811 has_point = TRUE;
812 } else
813 has_point = FALSE;
815 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
816 best = dist;
817 *actual_item = point_item;
818 }
819 }
820 }
822 return best;
823 }
825 /**
826 * Renders all visible canvas group items in buf rectangle.
827 */
828 static void
829 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
830 {
831 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
833 for (GList *list = group->items; list; list = list->next) {
834 SPCanvasItem *child = (SPCanvasItem *)list->data;
835 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
836 if ((child->x1 < buf->rect.x1) &&
837 (child->y1 < buf->rect.y1) &&
838 (child->x2 > buf->rect.x0) &&
839 (child->y2 > buf->rect.y0)) {
840 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
841 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
842 }
843 }
844 }
845 }
847 /**
848 * Adds an item to a canvas group.
849 */
850 static void
851 group_add (SPCanvasGroup *group, SPCanvasItem *item)
852 {
853 gtk_object_ref (GTK_OBJECT (item));
854 gtk_object_sink (GTK_OBJECT (item));
856 if (!group->items) {
857 group->items = g_list_append (group->items, item);
858 group->last = group->items;
859 } else {
860 group->last = g_list_append (group->last, item)->next;
861 }
863 sp_canvas_item_request_update (item);
864 }
866 /**
867 * Removes an item from a canvas group
868 */
869 static void
870 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
871 {
872 g_return_if_fail (group != NULL);
873 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
874 g_return_if_fail (item != NULL);
876 for (GList *children = group->items; children; children = children->next) {
877 if (children->data == item) {
879 /* Unparent the child */
881 item->parent = NULL;
882 gtk_object_unref (GTK_OBJECT (item));
884 /* Remove it from the list */
886 if (children == group->last) group->last = children->prev;
888 group->items = g_list_remove_link (group->items, children);
889 g_list_free (children);
890 break;
891 }
892 }
893 }
895 /* SPCanvas */
897 static void sp_canvas_class_init (SPCanvasClass *klass);
898 static void sp_canvas_init (SPCanvas *canvas);
899 static void sp_canvas_destroy (GtkObject *object);
901 static void sp_canvas_realize (GtkWidget *widget);
902 static void sp_canvas_unrealize (GtkWidget *widget);
904 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
905 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
907 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
908 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
909 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
910 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
911 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
912 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
913 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
914 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
916 static GtkWidgetClass *canvas_parent_class;
918 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
919 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
920 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
921 static int do_update (SPCanvas *canvas);
923 /**
924 * Registers the SPCanvas class if necessary, and returns the type ID
925 * associated to it.
926 *
927 * \return The type ID of the SPCanvas class.
928 **/
929 GtkType
930 sp_canvas_get_type (void)
931 {
932 static GtkType canvas_type = 0;
934 if (!canvas_type) {
935 static const GtkTypeInfo canvas_info = {
936 "SPCanvas",
937 sizeof (SPCanvas),
938 sizeof (SPCanvasClass),
939 (GtkClassInitFunc) sp_canvas_class_init,
940 (GtkObjectInitFunc) sp_canvas_init,
941 NULL, NULL, NULL
942 };
944 canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
945 }
947 return canvas_type;
948 }
950 /**
951 * Class initialization function for SPCanvasClass.
952 */
953 static void
954 sp_canvas_class_init (SPCanvasClass *klass)
955 {
956 GtkObjectClass *object_class = (GtkObjectClass *) klass;
957 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
959 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
961 object_class->destroy = sp_canvas_destroy;
963 widget_class->realize = sp_canvas_realize;
964 widget_class->unrealize = sp_canvas_unrealize;
965 widget_class->size_request = sp_canvas_size_request;
966 widget_class->size_allocate = sp_canvas_size_allocate;
967 widget_class->button_press_event = sp_canvas_button;
968 widget_class->button_release_event = sp_canvas_button;
969 widget_class->motion_notify_event = sp_canvas_motion;
970 widget_class->scroll_event = sp_canvas_scroll;
971 widget_class->expose_event = sp_canvas_expose;
972 widget_class->key_press_event = sp_canvas_key;
973 widget_class->key_release_event = sp_canvas_key;
974 widget_class->enter_notify_event = sp_canvas_crossing;
975 widget_class->leave_notify_event = sp_canvas_crossing;
976 widget_class->focus_in_event = sp_canvas_focus_in;
977 widget_class->focus_out_event = sp_canvas_focus_out;
978 }
980 /**
981 * Callback: object initialization for SPCanvas.
982 */
983 static void
984 sp_canvas_init (SPCanvas *canvas)
985 {
986 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
987 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
988 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
990 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
991 canvas->pick_event.crossing.x = 0;
992 canvas->pick_event.crossing.y = 0;
994 /* Create the root item as a special case */
995 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
996 canvas->root->canvas = canvas;
998 gtk_object_ref (GTK_OBJECT (canvas->root));
999 gtk_object_sink (GTK_OBJECT (canvas->root));
1001 canvas->need_repick = TRUE;
1003 // See comment at in sp-canvas.h.
1004 canvas->gen_all_enter_events = false;
1006 canvas->tiles=NULL;
1007 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1008 canvas->tileH=canvas->tileV=0;
1010 canvas->forced_redraw_count = 0;
1011 canvas->forced_redraw_limit = -1;
1013 canvas->is_scrolling = false;
1015 }
1017 /**
1018 * Convenience function to remove the idle handler of a canvas.
1019 */
1020 static void
1021 remove_idle (SPCanvas *canvas)
1022 {
1023 if (canvas->idle_id) {
1024 gtk_idle_remove (canvas->idle_id);
1025 canvas->idle_id = 0;
1026 }
1027 }
1029 /*
1030 * Removes the transient state of the canvas (idle handler, grabs).
1031 */
1032 static void
1033 shutdown_transients (SPCanvas *canvas)
1034 {
1035 /* We turn off the need_redraw flag, since if the canvas is mapped again
1036 * it will request a redraw anyways. We do not turn off the need_update
1037 * flag, though, because updates are not queued when the canvas remaps
1038 * itself.
1039 */
1040 if (canvas->need_redraw) {
1041 canvas->need_redraw = FALSE;
1042 }
1043 if ( canvas->tiles ) g_free(canvas->tiles);
1044 canvas->tiles=NULL;
1045 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1046 canvas->tileH=canvas->tileV=0;
1048 if (canvas->grabbed_item) {
1049 canvas->grabbed_item = NULL;
1050 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1051 }
1053 remove_idle (canvas);
1054 }
1056 /**
1057 * Destroy handler for SPCanvas.
1058 */
1059 static void
1060 sp_canvas_destroy (GtkObject *object)
1061 {
1062 SPCanvas *canvas = SP_CANVAS (object);
1064 if (canvas->root) {
1065 gtk_object_unref (GTK_OBJECT (canvas->root));
1066 canvas->root = NULL;
1067 }
1069 shutdown_transients (canvas);
1071 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1072 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1073 }
1075 /**
1076 * Returns new canvas as widget.
1077 */
1078 GtkWidget *
1079 sp_canvas_new_aa (void)
1080 {
1081 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1083 return (GtkWidget *) canvas;
1084 }
1086 /**
1087 * The canvas widget's realize callback.
1088 */
1089 static void
1090 sp_canvas_realize (GtkWidget *widget)
1091 {
1092 SPCanvas *canvas = SP_CANVAS (widget);
1094 GdkWindowAttr attributes;
1095 attributes.window_type = GDK_WINDOW_CHILD;
1096 attributes.x = widget->allocation.x;
1097 attributes.y = widget->allocation.y;
1098 attributes.width = widget->allocation.width;
1099 attributes.height = widget->allocation.height;
1100 attributes.wclass = GDK_INPUT_OUTPUT;
1101 attributes.visual = gdk_rgb_get_visual ();
1102 attributes.colormap = gdk_rgb_get_cmap ();
1103 attributes.event_mask = (gtk_widget_get_events (widget) |
1104 GDK_EXPOSURE_MASK |
1105 GDK_BUTTON_PRESS_MASK |
1106 GDK_BUTTON_RELEASE_MASK |
1107 GDK_POINTER_MOTION_MASK |
1108 GDK_PROXIMITY_IN_MASK |
1109 GDK_PROXIMITY_OUT_MASK |
1110 GDK_KEY_PRESS_MASK |
1111 GDK_KEY_RELEASE_MASK |
1112 GDK_ENTER_NOTIFY_MASK |
1113 GDK_LEAVE_NOTIFY_MASK |
1114 GDK_FOCUS_CHANGE_MASK);
1115 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1117 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1118 gdk_window_set_user_data (widget->window, widget);
1120 if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1121 gtk_widget_set_events(widget, attributes.event_mask);
1123 widget->style = gtk_style_attach (widget->style, widget->window);
1125 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1127 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1128 }
1130 /**
1131 * The canvas widget's unrealize callback.
1132 */
1133 static void
1134 sp_canvas_unrealize (GtkWidget *widget)
1135 {
1136 SPCanvas *canvas = SP_CANVAS (widget);
1138 shutdown_transients (canvas);
1140 gdk_gc_destroy (canvas->pixmap_gc);
1141 canvas->pixmap_gc = NULL;
1143 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1144 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1145 }
1147 /**
1148 * The canvas widget's size_request callback.
1149 */
1150 static void
1151 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1152 {
1153 static_cast<void>(SP_CANVAS (widget));
1155 req->width = 256;
1156 req->height = 256;
1157 }
1159 /**
1160 * The canvas widget's size_allocate callback.
1161 */
1162 static void
1163 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1164 {
1165 SPCanvas *canvas = SP_CANVAS (widget);
1167 /* Schedule redraw of new region */
1168 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1169 if (allocation->width > widget->allocation.width) {
1170 sp_canvas_request_redraw (canvas,
1171 canvas->x0 + widget->allocation.width,
1172 0,
1173 canvas->x0 + allocation->width,
1174 canvas->y0 + allocation->height);
1175 }
1176 if (allocation->height > widget->allocation.height) {
1177 sp_canvas_request_redraw (canvas,
1178 0,
1179 canvas->y0 + widget->allocation.height,
1180 canvas->x0 + allocation->width,
1181 canvas->y0 + allocation->height);
1182 }
1184 widget->allocation = *allocation;
1186 if (GTK_WIDGET_REALIZED (widget)) {
1187 gdk_window_move_resize (widget->window,
1188 widget->allocation.x, widget->allocation.y,
1189 widget->allocation.width, widget->allocation.height);
1190 }
1191 }
1193 /**
1194 * Helper that emits an event for an item in the canvas, be it the current
1195 * item, grabbed item, or focused item, as appropriate.
1196 */
1197 static int
1198 emit_event (SPCanvas *canvas, GdkEvent *event)
1199 {
1200 guint mask;
1202 if (canvas->grabbed_item) {
1203 switch (event->type) {
1204 case GDK_ENTER_NOTIFY:
1205 mask = GDK_ENTER_NOTIFY_MASK;
1206 break;
1207 case GDK_LEAVE_NOTIFY:
1208 mask = GDK_LEAVE_NOTIFY_MASK;
1209 break;
1210 case GDK_MOTION_NOTIFY:
1211 mask = GDK_POINTER_MOTION_MASK;
1212 break;
1213 case GDK_BUTTON_PRESS:
1214 case GDK_2BUTTON_PRESS:
1215 case GDK_3BUTTON_PRESS:
1216 mask = GDK_BUTTON_PRESS_MASK;
1217 break;
1218 case GDK_BUTTON_RELEASE:
1219 mask = GDK_BUTTON_RELEASE_MASK;
1220 break;
1221 case GDK_KEY_PRESS:
1222 mask = GDK_KEY_PRESS_MASK;
1223 break;
1224 case GDK_KEY_RELEASE:
1225 mask = GDK_KEY_RELEASE_MASK;
1226 break;
1227 case GDK_SCROLL:
1228 mask = GDK_SCROLL;
1229 break;
1230 default:
1231 mask = 0;
1232 break;
1233 }
1235 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1236 }
1238 /* Convert to world coordinates -- we have two cases because of diferent
1239 * offsets of the fields in the event structures.
1240 */
1242 GdkEvent ev = *event;
1244 switch (ev.type) {
1245 case GDK_ENTER_NOTIFY:
1246 case GDK_LEAVE_NOTIFY:
1247 ev.crossing.x += canvas->x0;
1248 ev.crossing.y += canvas->y0;
1249 break;
1250 case GDK_MOTION_NOTIFY:
1251 case GDK_BUTTON_PRESS:
1252 case GDK_2BUTTON_PRESS:
1253 case GDK_3BUTTON_PRESS:
1254 case GDK_BUTTON_RELEASE:
1255 ev.motion.x += canvas->x0;
1256 ev.motion.y += canvas->y0;
1257 break;
1258 default:
1259 break;
1260 }
1262 /* Choose where we send the event */
1264 /* canvas->current_item becomes NULL in some cases under Win32
1265 ** (e.g. if the pointer leaves the window). So this is a hack that
1266 ** Lauris applied to SP to get around the problem.
1267 */
1268 SPCanvasItem* item = NULL;
1269 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1270 item = canvas->grabbed_item;
1271 } else {
1272 item = canvas->current_item;
1273 }
1275 if (canvas->focused_item &&
1276 ((event->type == GDK_KEY_PRESS) ||
1277 (event->type == GDK_KEY_RELEASE) ||
1278 (event->type == GDK_FOCUS_CHANGE))) {
1279 item = canvas->focused_item;
1280 }
1282 /* The event is propagated up the hierarchy (for if someone connected to
1283 * a group instead of a leaf event), and emission is stopped if a
1284 * handler returns TRUE, just like for GtkWidget events.
1285 */
1287 gint finished = FALSE;
1289 while (item && !finished) {
1290 gtk_object_ref (GTK_OBJECT (item));
1291 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1292 SPCanvasItem *parent = item->parent;
1293 gtk_object_unref (GTK_OBJECT (item));
1294 item = parent;
1295 }
1297 return finished;
1298 }
1300 /**
1301 * Helper that re-picks the current item in the canvas, based on the event's
1302 * coordinates and emits enter/leave events for items as appropriate.
1303 */
1304 static int
1305 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1306 {
1307 int button_down = 0;
1308 double x, y;
1310 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1311 return FALSE;
1313 int retval = FALSE;
1315 if (canvas->gen_all_enter_events == false) {
1316 // If a button is down, we'll perform enter and leave events on the
1317 // current item, but not enter on any other item. This is more or
1318 // less like X pointer grabbing for canvas items.
1319 //
1320 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1321 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1323 if (!button_down) canvas->left_grabbed_item = FALSE;
1324 }
1326 /* Save the event in the canvas. This is used to synthesize enter and
1327 * leave events in case the current item changes. It is also used to
1328 * re-pick the current item if the current one gets deleted. Also,
1329 * synthesize an enter event.
1330 */
1331 if (event != &canvas->pick_event) {
1332 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1333 /* these fields have the same offsets in both types of events */
1335 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1336 canvas->pick_event.crossing.window = event->motion.window;
1337 canvas->pick_event.crossing.send_event = event->motion.send_event;
1338 canvas->pick_event.crossing.subwindow = NULL;
1339 canvas->pick_event.crossing.x = event->motion.x;
1340 canvas->pick_event.crossing.y = event->motion.y;
1341 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1342 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1343 canvas->pick_event.crossing.focus = FALSE;
1344 canvas->pick_event.crossing.state = event->motion.state;
1346 /* these fields don't have the same offsets in both types of events */
1348 if (event->type == GDK_MOTION_NOTIFY) {
1349 canvas->pick_event.crossing.x_root = event->motion.x_root;
1350 canvas->pick_event.crossing.y_root = event->motion.y_root;
1351 } else {
1352 canvas->pick_event.crossing.x_root = event->button.x_root;
1353 canvas->pick_event.crossing.y_root = event->button.y_root;
1354 }
1355 } else {
1356 canvas->pick_event = *event;
1357 }
1358 }
1360 /* Don't do anything else if this is a recursive call */
1361 if (canvas->in_repick) return retval;
1363 /* LeaveNotify means that there is no current item, so we don't look for one */
1364 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1365 /* these fields don't have the same offsets in both types of events */
1367 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1368 x = canvas->pick_event.crossing.x;
1369 y = canvas->pick_event.crossing.y;
1370 } else {
1371 x = canvas->pick_event.motion.x;
1372 y = canvas->pick_event.motion.y;
1373 }
1375 /* world coords */
1376 x += canvas->x0;
1377 y += canvas->y0;
1379 /* find the closest item */
1380 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1381 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1382 } else {
1383 canvas->new_current_item = NULL;
1384 }
1385 } else {
1386 canvas->new_current_item = NULL;
1387 }
1389 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1390 return retval; /* current item did not change */
1391 }
1393 /* Synthesize events for old and new current items */
1395 if ((canvas->new_current_item != canvas->current_item)
1396 && (canvas->current_item != NULL)
1397 && !canvas->left_grabbed_item) {
1398 GdkEvent new_event;
1399 SPCanvasItem *item;
1401 item = canvas->current_item;
1403 new_event = canvas->pick_event;
1404 new_event.type = GDK_LEAVE_NOTIFY;
1406 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1407 new_event.crossing.subwindow = NULL;
1408 canvas->in_repick = TRUE;
1409 retval = emit_event (canvas, &new_event);
1410 canvas->in_repick = FALSE;
1411 }
1413 if (canvas->gen_all_enter_events == false) {
1414 // new_current_item may have been set to NULL during the call to
1415 // emit_event() above
1416 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1417 canvas->left_grabbed_item = TRUE;
1418 return retval;
1419 }
1420 }
1422 /* Handle the rest of cases */
1424 canvas->left_grabbed_item = FALSE;
1425 canvas->current_item = canvas->new_current_item;
1427 if (canvas->current_item != NULL) {
1428 GdkEvent new_event;
1430 new_event = canvas->pick_event;
1431 new_event.type = GDK_ENTER_NOTIFY;
1432 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1433 new_event.crossing.subwindow = NULL;
1434 retval = emit_event (canvas, &new_event);
1435 }
1437 return retval;
1438 }
1440 /**
1441 * Button event handler for the canvas.
1442 */
1443 static gint
1444 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1445 {
1446 SPCanvas *canvas = SP_CANVAS (widget);
1448 int retval = FALSE;
1450 /* dispatch normally regardless of the event's window if an item has
1451 has a pointer grab in effect */
1452 if (!canvas->grabbed_item &&
1453 event->window != SP_CANVAS_WINDOW (canvas))
1454 return retval;
1456 int mask;
1457 switch (event->button) {
1458 case 1:
1459 mask = GDK_BUTTON1_MASK;
1460 break;
1461 case 2:
1462 mask = GDK_BUTTON2_MASK;
1463 break;
1464 case 3:
1465 mask = GDK_BUTTON3_MASK;
1466 break;
1467 case 4:
1468 mask = GDK_BUTTON4_MASK;
1469 break;
1470 case 5:
1471 mask = GDK_BUTTON5_MASK;
1472 break;
1473 default:
1474 mask = 0;
1475 }
1477 switch (event->type) {
1478 case GDK_BUTTON_PRESS:
1479 case GDK_2BUTTON_PRESS:
1480 case GDK_3BUTTON_PRESS:
1481 /* Pick the current item as if the button were not pressed, and
1482 * then process the event.
1483 */
1484 canvas->state = event->state;
1485 pick_current_item (canvas, (GdkEvent *) event);
1486 canvas->state ^= mask;
1487 retval = emit_event (canvas, (GdkEvent *) event);
1488 break;
1490 case GDK_BUTTON_RELEASE:
1491 /* Process the event as if the button were pressed, then repick
1492 * after the button has been released
1493 */
1494 canvas->state = event->state;
1495 retval = emit_event (canvas, (GdkEvent *) event);
1496 event->state ^= mask;
1497 canvas->state = event->state;
1498 pick_current_item (canvas, (GdkEvent *) event);
1499 event->state ^= mask;
1500 break;
1502 default:
1503 g_assert_not_reached ();
1504 }
1506 return retval;
1507 }
1509 /**
1510 * Scroll event handler for the canvas.
1511 *
1512 * \todo FIXME: generate motion events to re-select items.
1513 */
1514 static gint
1515 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1516 {
1517 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1518 }
1520 /**
1521 * Motion event handler for the canvas.
1522 */
1523 static int
1524 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1525 {
1526 SPCanvas *canvas = SP_CANVAS (widget);
1528 if (event->window != SP_CANVAS_WINDOW (canvas))
1529 return FALSE;
1531 if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1532 gint x, y;
1533 gdk_window_get_pointer (widget->window, &x, &y, NULL);
1534 event->x = x;
1535 event->y = y;
1536 }
1538 canvas->state = event->state;
1539 pick_current_item (canvas, (GdkEvent *) event);
1541 return emit_event (canvas, (GdkEvent *) event);
1542 }
1544 static void
1545 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)
1546 {
1547 GtkWidget *widget = GTK_WIDGET (canvas);
1549 SPCanvasBuf buf;
1550 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1551 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1552 } else {
1553 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1554 }
1556 // Mark the region clean
1557 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1559 buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1560 buf.rect.x0 = x0;
1561 buf.rect.y0 = y0;
1562 buf.rect.x1 = x1;
1563 buf.rect.y1 = y1;
1564 buf.visible_rect.x0 = draw_x1;
1565 buf.visible_rect.y0 = draw_y1;
1566 buf.visible_rect.x1 = draw_x2;
1567 buf.visible_rect.y1 = draw_y2;
1568 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1569 buf.bg_color = (((color->red & 0xff00) << 8)
1570 | (color->green & 0xff00)
1571 | (color->blue >> 8));
1572 buf.is_empty = true;
1574 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1575 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1576 }
1578 if (buf.is_empty) {
1579 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1580 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1581 canvas->pixmap_gc,
1582 TRUE,
1583 x0 - canvas->x0, y0 - canvas->y0,
1584 x1 - x0, y1 - y0);
1585 } else {
1586 /*
1587 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1588 // Why this must not be done currently:
1589 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1590 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)
1591 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels,
1592 // we need more bufs to paint a given area and as a result it's even a bit slower
1594 cairo_surface_t* cst = cairo_image_surface_create_for_data (
1595 buf.buf,
1596 CAIRO_FORMAT_RGB24, // unpacked, i.e. 32 bits! one byte is unused
1597 x1 - x0, y1 - y0,
1598 buf.buf_rowstride
1599 );
1600 cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1601 cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1602 cairo_paint (ct);
1603 cairo_destroy (ct);
1604 cairo_surface_finish (cst);
1605 cairo_surface_destroy (cst);
1606 */
1608 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1609 canvas->pixmap_gc,
1610 x0 - canvas->x0, y0 - canvas->y0,
1611 x1 - x0, y1 - y0,
1612 GDK_RGB_DITHER_MAX,
1613 buf.buf,
1614 sw * 3,
1615 x0 - canvas->x0, y0 - canvas->y0);
1616 }
1618 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1619 nr_pixelstore_256K_free (buf.buf);
1620 } else {
1621 nr_pixelstore_1M_free (buf.buf);
1622 }
1623 }
1625 struct PaintRectSetup {
1626 SPCanvas* canvas;
1627 NRRectL big_rect;
1628 GTimeVal start_time;
1629 int max_pixels;
1630 NR::Point mouse_loc;
1631 };
1633 /**
1634 * Paint the given rect, recursively subdividing the region until it is the size of a single
1635 * buffer.
1636 *
1637 * @return true if the drawing completes
1638 */
1639 static int
1640 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1641 {
1642 GTimeVal now;
1643 g_get_current_time (&now);
1645 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1646 + (now.tv_usec - setup->start_time.tv_usec);
1648 // Allow only very fast buffers to be run together;
1649 // as soon as the total redraw time exceeds 1ms, cancel;
1650 // this returns control to the idle loop and allows Inkscape to process user input
1651 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1652 // it will get back and finish painting what remains to paint.
1653 if (elapsed > 1000) {
1655 // Interrupting redraw isn't always good.
1656 // For example, when you drag one node of a big path, only the buffer containing
1657 // the mouse cursor will be redrawn again and again, and the rest of the path
1658 // will remain stale because Inkscape never has enough idle time to redraw all
1659 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1660 // If this limit is set, and if we have aborted redraw more times than is allowed,
1661 // interrupting is blocked and we're forced to redraw full screen once
1662 // (after which we can again interrupt forced_redraw_limit times).
1663 if (setup->canvas->forced_redraw_limit < 0 ||
1664 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1666 if (setup->canvas->forced_redraw_limit != -1) {
1667 setup->canvas->forced_redraw_count++;
1668 }
1670 return false;
1671 }
1672 }
1674 // Find the optimal buffer dimensions
1675 int bw = this_rect.x1 - this_rect.x0;
1676 int bh = this_rect.y1 - this_rect.y0;
1677 if ((bw < 1) || (bh < 1))
1678 return 0;
1680 if (bw * bh < setup->max_pixels) {
1681 // We are small enough
1682 sp_canvas_paint_single_buffer (setup->canvas,
1683 this_rect.x0, this_rect.y0,
1684 this_rect.x1, this_rect.y1,
1685 setup->big_rect.x0, setup->big_rect.y0,
1686 setup->big_rect.x1, setup->big_rect.y1, bw);
1687 return 1;
1688 }
1690 NRRectL lo = this_rect;
1691 NRRectL hi = this_rect;
1693 /*
1694 This test determines the redraw strategy:
1696 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1697 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1698 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1699 and seems to be faster for drawings with many smaller objects at zoom-out.
1701 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1702 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1703 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1704 faster.
1706 The default for now is the strips mode.
1707 */
1708 if (bw < bh) {
1709 int mid = (this_rect.x0 + this_rect.x1) / 2;
1710 // Make sure that mid lies on a tile boundary
1711 mid = (mid / TILE_SIZE) * TILE_SIZE;
1713 lo.x1 = mid;
1714 hi.x0 = mid;
1716 if (setup->mouse_loc[NR::X] < mid) {
1717 // Always paint towards the mouse first
1718 return sp_canvas_paint_rect_internal(setup, lo)
1719 && sp_canvas_paint_rect_internal(setup, hi);
1720 } else {
1721 return sp_canvas_paint_rect_internal(setup, hi)
1722 && sp_canvas_paint_rect_internal(setup, lo);
1723 }
1724 } else {
1725 int mid = (this_rect.y0 + this_rect.y1) / 2;
1726 // Make sure that mid lies on a tile boundary
1727 mid = (mid / TILE_SIZE) * TILE_SIZE;
1729 lo.y1 = mid;
1730 hi.y0 = mid;
1732 if (setup->mouse_loc[NR::Y] < mid) {
1733 // Always paint towards the mouse first
1734 return sp_canvas_paint_rect_internal(setup, lo)
1735 && sp_canvas_paint_rect_internal(setup, hi);
1736 } else {
1737 return sp_canvas_paint_rect_internal(setup, hi)
1738 && sp_canvas_paint_rect_internal(setup, lo);
1739 }
1740 }
1741 }
1744 /**
1745 * Helper that draws a specific rectangular part of the canvas.
1746 *
1747 * @return true if the rectangle painting succeeds.
1748 */
1749 static bool
1750 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1751 {
1752 g_return_val_if_fail (!canvas->need_update, false);
1754 NRRectL rect;
1755 rect.x0 = xx0;
1756 rect.x1 = xx1;
1757 rect.y0 = yy0;
1758 rect.y1 = yy1;
1760 // Clip rect-to-draw by the current visible area
1761 rect.x0 = MAX (rect.x0, canvas->x0);
1762 rect.y0 = MAX (rect.y0, canvas->y0);
1763 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1764 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1766 #ifdef DEBUG_REDRAW
1767 // paint the area to redraw yellow
1768 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1769 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1770 canvas->pixmap_gc,
1771 TRUE,
1772 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1773 rect.x1 - rect.x0, rect.y1 - rect.y0);
1774 #endif
1776 PaintRectSetup setup;
1778 setup.canvas = canvas;
1779 setup.big_rect = rect;
1781 // Save the mouse location
1782 gint x, y;
1783 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1784 setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1786 // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1787 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1788 // use 256K as a compromise to not slow down gradients
1789 // 256K is the cached buffer and we need 3 channels
1790 setup.max_pixels = 87381; // 256K/3
1791 } else {
1792 // paths only, so 1M works faster
1793 // 1M is the cached buffer and we need 3 channels
1794 setup.max_pixels = 349525;
1795 }
1797 // Start the clock
1798 g_get_current_time(&(setup.start_time));
1800 // Go
1801 return sp_canvas_paint_rect_internal(&setup, rect);
1802 }
1804 /**
1805 * Force a full redraw after a specified number of interrupted redraws
1806 */
1807 void
1808 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1809 g_return_if_fail(canvas != NULL);
1811 canvas->forced_redraw_limit = count;
1812 canvas->forced_redraw_count = 0;
1813 }
1815 /**
1816 * End forced full redraw requests
1817 */
1818 void
1819 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1820 g_return_if_fail(canvas != NULL);
1822 canvas->forced_redraw_limit = -1;
1823 }
1825 /**
1826 * The canvas widget's expose callback.
1827 */
1828 static gint
1829 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1830 {
1831 SPCanvas *canvas = SP_CANVAS (widget);
1833 if (!GTK_WIDGET_DRAWABLE (widget) ||
1834 (event->window != SP_CANVAS_WINDOW (canvas)))
1835 return FALSE;
1837 int n_rects;
1838 GdkRectangle *rects;
1839 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1841 for (int i = 0; i < n_rects; i++) {
1842 NRRectL rect;
1844 rect.x0 = rects[i].x + canvas->x0;
1845 rect.y0 = rects[i].y + canvas->y0;
1846 rect.x1 = rect.x0 + rects[i].width;
1847 rect.y1 = rect.y0 + rects[i].height;
1849 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1850 }
1852 if (n_rects > 0)
1853 g_free (rects);
1855 return FALSE;
1856 }
1858 /**
1859 * The canvas widget's keypress callback.
1860 */
1861 static gint
1862 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1863 {
1864 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1865 }
1867 /**
1868 * Crossing event handler for the canvas.
1869 */
1870 static gint
1871 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1872 {
1873 SPCanvas *canvas = SP_CANVAS (widget);
1875 if (event->window != SP_CANVAS_WINDOW (canvas))
1876 return FALSE;
1878 canvas->state = event->state;
1879 return pick_current_item (canvas, (GdkEvent *) event);
1880 }
1882 /**
1883 * Focus in handler for the canvas.
1884 */
1885 static gint
1886 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1887 {
1888 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1890 SPCanvas *canvas = SP_CANVAS (widget);
1892 if (canvas->focused_item) {
1893 return emit_event (canvas, (GdkEvent *) event);
1894 } else {
1895 return FALSE;
1896 }
1897 }
1899 /**
1900 * Focus out handler for the canvas.
1901 */
1902 static gint
1903 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1904 {
1905 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1907 SPCanvas *canvas = SP_CANVAS (widget);
1909 if (canvas->focused_item)
1910 return emit_event (canvas, (GdkEvent *) event);
1911 else
1912 return FALSE;
1913 }
1915 /**
1916 * Helper that repaints the areas in the canvas that need it.
1917 *
1918 * @return true if all the dirty parts have been redrawn
1919 */
1920 static int
1921 paint (SPCanvas *canvas)
1922 {
1923 if (canvas->need_update) {
1924 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1925 canvas->need_update = FALSE;
1926 }
1928 if (!canvas->need_redraw)
1929 return TRUE;
1931 Gdk::Region to_paint;
1933 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
1934 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
1935 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
1937 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
1938 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
1939 TILE_SIZE, TILE_SIZE));
1940 }
1942 }
1943 }
1945 if (!to_paint.empty()) {
1946 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
1947 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
1948 for (Iter i=rect.begin(); i != rect.end(); ++i) {
1949 int x0 = (*i).get_x();
1950 int y0 = (*i).get_y();
1951 int x1 = x0 + (*i).get_width();
1952 int y1 = y0 + (*i).get_height();
1953 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
1954 // Aborted
1955 return FALSE;
1956 };
1957 }
1958 }
1960 canvas->need_redraw = FALSE;
1962 // we've had a full unaborted redraw, reset the full redraw counter
1963 if (canvas->forced_redraw_limit != -1) {
1964 canvas->forced_redraw_count = 0;
1965 }
1967 return TRUE;
1968 }
1970 /**
1971 * Helper that invokes update, paint, and repick on canvas.
1972 */
1973 static int
1974 do_update (SPCanvas *canvas)
1975 {
1976 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1977 return TRUE;
1979 /* Cause the update if necessary */
1980 if (canvas->need_update) {
1981 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1982 canvas->need_update = FALSE;
1983 }
1985 /* Paint if able to */
1986 if (GTK_WIDGET_DRAWABLE (canvas)) {
1987 return paint (canvas);
1988 }
1990 /* Pick new current item */
1991 while (canvas->need_repick) {
1992 canvas->need_repick = FALSE;
1993 pick_current_item (canvas, &canvas->pick_event);
1994 }
1996 return TRUE;
1997 }
1999 /**
2000 * Idle handler for the canvas that deals with pending updates and redraws.
2001 */
2002 static gint
2003 idle_handler (gpointer data)
2004 {
2005 GDK_THREADS_ENTER ();
2007 SPCanvas *canvas = SP_CANVAS (data);
2009 const int ret = do_update (canvas);
2011 if (ret) {
2012 /* Reset idle id */
2013 canvas->idle_id = 0;
2014 }
2016 GDK_THREADS_LEAVE ();
2018 return !ret;
2019 }
2021 /**
2022 * Convenience function to add an idle handler to a canvas.
2023 */
2024 static void
2025 add_idle (SPCanvas *canvas)
2026 {
2027 if (canvas->idle_id != 0)
2028 return;
2030 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2031 }
2033 /**
2034 * Returns the root group of the specified canvas.
2035 */
2036 SPCanvasGroup *
2037 sp_canvas_root (SPCanvas *canvas)
2038 {
2039 g_return_val_if_fail (canvas != NULL, NULL);
2040 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2042 return SP_CANVAS_GROUP (canvas->root);
2043 }
2045 /**
2046 * Scrolls canvas to specific position.
2047 */
2048 void
2049 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2050 {
2051 g_return_if_fail (canvas != NULL);
2052 g_return_if_fail (SP_IS_CANVAS (canvas));
2054 int ix = (int) (cx + 0.5);
2055 int iy = (int) (cy + 0.5);
2056 int dx = ix - canvas->x0;
2057 int dy = iy - canvas->y0;
2059 canvas->dx0 = cx;
2060 canvas->dy0 = cy;
2061 canvas->x0 = ix;
2062 canvas->y0 = iy;
2064 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2066 if (!clear) {
2067 // scrolling without zoom; redraw only the newly exposed areas
2068 if ((dx != 0) || (dy != 0)) {
2069 canvas->is_scrolling = is_scrolling;
2070 if (GTK_WIDGET_REALIZED (canvas)) {
2071 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2072 }
2073 }
2074 } else {
2075 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2076 }
2078 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
2079 SPEventContext *ec = inkscape_active_event_context();
2080 if (SP_IS_3DBOX_CONTEXT (ec)) {
2081 // We could avoid redraw during panning by checking the status of is_scrolling, but this is
2082 // neither faster nor does it get rid of artefacts, so we update PLs unconditionally
2083 SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec);
2084 bc->_vpdrag->updateLines();
2085 }
2086 }
2088 /**
2089 * Updates canvas if necessary.
2090 */
2091 void
2092 sp_canvas_update_now (SPCanvas *canvas)
2093 {
2094 g_return_if_fail (canvas != NULL);
2095 g_return_if_fail (SP_IS_CANVAS (canvas));
2097 if (!(canvas->need_update ||
2098 canvas->need_redraw))
2099 return;
2101 do_update (canvas);
2102 }
2104 /**
2105 * Update callback for canvas widget.
2106 */
2107 static void
2108 sp_canvas_request_update (SPCanvas *canvas)
2109 {
2110 canvas->need_update = TRUE;
2111 add_idle (canvas);
2112 }
2114 /**
2115 * Forces redraw of rectangular canvas area.
2116 */
2117 void
2118 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2119 {
2120 NRRectL bbox;
2121 NRRectL visible;
2122 NRRectL clip;
2124 g_return_if_fail (canvas != NULL);
2125 g_return_if_fail (SP_IS_CANVAS (canvas));
2127 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2128 if ((x0 >= x1) || (y0 >= y1)) return;
2130 bbox.x0 = x0;
2131 bbox.y0 = y0;
2132 bbox.x1 = x1;
2133 bbox.y1 = y1;
2135 visible.x0 = canvas->x0;
2136 visible.y0 = canvas->y0;
2137 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2138 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2140 nr_rect_l_intersect (&clip, &bbox, &visible);
2142 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2143 add_idle (canvas);
2144 }
2146 /**
2147 * Sets world coordinates from win and canvas.
2148 */
2149 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2150 {
2151 g_return_if_fail (canvas != NULL);
2152 g_return_if_fail (SP_IS_CANVAS (canvas));
2154 if (worldx) *worldx = canvas->x0 + winx;
2155 if (worldy) *worldy = canvas->y0 + winy;
2156 }
2158 /**
2159 * Sets win coordinates from world and canvas.
2160 */
2161 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2162 {
2163 g_return_if_fail (canvas != NULL);
2164 g_return_if_fail (SP_IS_CANVAS (canvas));
2166 if (winx) *winx = worldx - canvas->x0;
2167 if (winy) *winy = worldy - canvas->y0;
2168 }
2170 /**
2171 * Converts point from win to world coordinates.
2172 */
2173 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2174 {
2175 g_assert (canvas != NULL);
2176 g_assert (SP_IS_CANVAS (canvas));
2178 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2179 }
2181 /**
2182 * Converts point from world to win coordinates.
2183 */
2184 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2185 {
2186 g_assert (canvas != NULL);
2187 g_assert (SP_IS_CANVAS (canvas));
2189 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2190 }
2192 /**
2193 * Returns true if point given in world coordinates is inside window.
2194 */
2195 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2196 {
2197 g_assert( canvas != NULL );
2198 g_assert(SP_IS_CANVAS(canvas));
2200 using NR::X;
2201 using NR::Y;
2202 GtkWidget const &w = *GTK_WIDGET(canvas);
2203 return ( ( canvas->x0 <= world[X] ) &&
2204 ( canvas->y0 <= world[Y] ) &&
2205 ( world[X] < canvas->x0 + w.allocation.width ) &&
2206 ( world[Y] < canvas->y0 + w.allocation.height ) );
2207 }
2209 /**
2210 * Return canvas window coordinates as NR::Rect.
2211 */
2212 NR::Rect SPCanvas::getViewbox() const
2213 {
2214 GtkWidget const *w = GTK_WIDGET(this);
2216 return NR::Rect(NR::Point(dx0, dy0),
2217 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2218 }
2220 inline int sp_canvas_tile_floor(int x)
2221 {
2222 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2223 }
2225 inline int sp_canvas_tile_ceil(int x)
2226 {
2227 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2228 }
2230 /**
2231 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2232 */
2233 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2234 {
2235 if ( nl >= nr || nt >= nb ) {
2236 if ( canvas->tiles ) g_free(canvas->tiles);
2237 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2238 canvas->tileH=canvas->tileV=0;
2239 canvas->tiles=NULL;
2240 return;
2241 }
2242 int tl=sp_canvas_tile_floor(nl);
2243 int tt=sp_canvas_tile_floor(nt);
2244 int tr=sp_canvas_tile_ceil(nr);
2245 int tb=sp_canvas_tile_ceil(nb);
2247 int nh = tr-tl, nv = tb-tt;
2248 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2249 for (int i=tl; i<tr; i++) {
2250 for (int j=tt; j<tb; j++) {
2251 int ind = (i-tl) + (j-tt)*nh;
2252 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2253 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2254 } else {
2255 ntiles[ind]=0; // newly exposed areas get 0
2256 }
2257 }
2258 }
2259 if ( canvas->tiles ) g_free(canvas->tiles);
2260 canvas->tiles=ntiles;
2261 canvas->tLeft=tl;
2262 canvas->tTop=tt;
2263 canvas->tRight=tr;
2264 canvas->tBottom=tb;
2265 canvas->tileH=nh;
2266 canvas->tileV=nv;
2267 }
2269 /*
2270 * Helper that queues a canvas rectangle for redraw
2271 */
2272 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2273 canvas->need_redraw = TRUE;
2275 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2276 }
2278 /**
2279 * Helper that marks specific canvas rectangle for redraw
2280 */
2281 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2282 {
2283 if ( nl >= nr || nt >= nb ) {
2284 return;
2285 }
2286 int tl=sp_canvas_tile_floor(nl);
2287 int tt=sp_canvas_tile_floor(nt);
2288 int tr=sp_canvas_tile_ceil(nr);
2289 int tb=sp_canvas_tile_ceil(nb);
2290 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2291 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2292 if ( tr > canvas->tRight ) tr=canvas->tRight;
2293 if ( tt < canvas->tTop ) tt=canvas->tTop;
2294 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2296 for (int i=tl; i<tr; i++) {
2297 for (int j=tt; j<tb; j++) {
2298 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2299 }
2300 }
2301 }
2304 /*
2305 Local Variables:
2306 mode:c++
2307 c-file-style:"stroustrup"
2308 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2309 indent-tabs-mode:nil
2310 fill-column:99
2311 End:
2312 */
2313 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :