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 "inkscape.h"
38 #include "sodipodi-ctrlrect.h"
39 #if ENABLE_LCMS
40 #include "color-profile-fns.h"
41 #endif // ENABLE_LCMS
42 #include "display/rendermode.h"
43 #include "libnr/nr-blit.h"
44 #include "display/inkscape-cairo.h"
46 // Define this to visualize the regions to be redrawn
47 //#define DEBUG_REDRAW 1;
49 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
50 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
51 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
52 #define TILE_SIZE 16
54 static gint const 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, gchar const *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 GTypeInfo const 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 ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
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, gchar const *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, gchar const *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 // Hack: if this is a ctrlrect, move it to 0,0;
234 // this redraws only the stroke of the rect to be deleted,
235 // avoiding redraw of the entire area
236 if (SP_IS_CTRLRECT(item)) {
237 SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
238 SP_CTRLRECT(object)->update(item->xform, 0);
239 } else {
240 redraw_if_visible (item);
241 }
242 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
244 if (item == item->canvas->current_item) {
245 item->canvas->current_item = NULL;
246 item->canvas->need_repick = TRUE;
247 }
249 if (item == item->canvas->new_current_item) {
250 item->canvas->new_current_item = NULL;
251 item->canvas->need_repick = TRUE;
252 }
254 if (item == item->canvas->grabbed_item) {
255 item->canvas->grabbed_item = NULL;
256 gdk_pointer_ungrab (GDK_CURRENT_TIME);
257 }
259 if (item == item->canvas->focused_item)
260 item->canvas->focused_item = NULL;
262 if (item->parent) {
263 group_remove (SP_CANVAS_GROUP (item->parent), item);
264 }
266 G_OBJECT_CLASS (item_parent_class)->dispose (object);
267 }
269 /**
270 * Helper function to update item and its children.
271 *
272 * NB! affine is parent2canvas.
273 */
274 static void
275 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
276 {
277 /* Apply the child item's transform */
278 NR::Matrix child_affine = item->xform * affine;
280 /* apply object flags to child flags */
281 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
283 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
284 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
286 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
287 child_flags |= SP_CANVAS_UPDATE_AFFINE;
289 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
290 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
291 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
292 }
294 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
295 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
296 }
298 /**
299 * Helper function to invoke the point method of the item.
300 *
301 * The argument x, y should be in the parent's item-relative coordinate
302 * system. This routine applies the inverse of the item's transform,
303 * maintaining the affine invariant.
304 */
305 static double
306 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
307 {
308 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
309 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
311 return NR_HUGE;
312 }
314 /**
315 * Makes the item's affine transformation matrix be equal to the specified
316 * matrix.
317 *
318 * @item: A canvas item.
319 * @affine: An affine transformation matrix.
320 */
321 void
322 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine)
323 {
324 item->xform = affine;
326 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
327 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
328 if (item->parent != NULL) {
329 sp_canvas_item_request_update (item->parent);
330 } else {
331 sp_canvas_request_update (item->canvas);
332 }
333 }
335 item->canvas->need_repick = TRUE;
336 }
338 /**
339 * Convenience function to reorder items in a group's child list.
340 *
341 * This puts the specified link after the "before" link.
342 */
343 static void
344 put_item_after (GList *link, GList *before)
345 {
346 if (link == before)
347 return;
349 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
351 if (before == NULL) {
352 if (link == parent->items) return;
354 link->prev->next = link->next;
356 if (link->next) {
357 link->next->prev = link->prev;
358 } else {
359 parent->last = link->prev;
360 }
362 link->prev = before;
363 link->next = parent->items;
364 link->next->prev = link;
365 parent->items = link;
366 } else {
367 if ((link == parent->last) && (before == parent->last->prev))
368 return;
370 if (link->next)
371 link->next->prev = link->prev;
373 if (link->prev)
374 link->prev->next = link->next;
375 else {
376 parent->items = link->next;
377 parent->items->prev = NULL;
378 }
380 link->prev = before;
381 link->next = before->next;
383 link->prev->next = link;
385 if (link->next)
386 link->next->prev = link;
387 else
388 parent->last = link;
389 }
390 }
393 /**
394 * Raises the item in its parent's stack by the specified number of positions.
395 *
396 * \param item A canvas item.
397 * \param positions Number of steps to raise the item.
398 *
399 * If the number of positions is greater than the distance to the top of the
400 * stack, then the item is put at the top.
401 */
402 void
403 sp_canvas_item_raise (SPCanvasItem *item, int positions)
404 {
405 g_return_if_fail (item != NULL);
406 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
407 g_return_if_fail (positions >= 0);
409 if (!item->parent || positions == 0)
410 return;
412 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
413 GList *link = g_list_find (parent->items, item);
414 g_assert (link != NULL);
416 GList *before;
417 for (before = link; positions && before; positions--)
418 before = before->next;
420 if (!before)
421 before = parent->last;
423 put_item_after (link, before);
425 redraw_if_visible (item);
426 item->canvas->need_repick = TRUE;
427 }
430 /**
431 * Lowers the item in its parent's stack by the specified number of positions.
432 *
433 * \param item A canvas item.
434 * \param positions Number of steps to lower the item.
435 *
436 * If the number of positions is greater than the distance to the bottom of the
437 * stack, then the item is put at the bottom.
438 **/
439 void
440 sp_canvas_item_lower (SPCanvasItem *item, int positions)
441 {
442 g_return_if_fail (item != NULL);
443 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
444 g_return_if_fail (positions >= 1);
446 if (!item->parent || positions == 0)
447 return;
449 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
450 GList *link = g_list_find (parent->items, item);
451 g_assert (link != NULL);
453 GList *before;
454 if (link->prev)
455 for (before = link->prev; positions && before; positions--)
456 before = before->prev;
457 else
458 before = NULL;
460 put_item_after (link, before);
462 redraw_if_visible (item);
463 item->canvas->need_repick = TRUE;
464 }
466 /**
467 * Sets visible flag on item and requests a redraw.
468 */
469 void
470 sp_canvas_item_show (SPCanvasItem *item)
471 {
472 g_return_if_fail (item != NULL);
473 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
475 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
476 return;
478 item->flags |= SP_CANVAS_ITEM_VISIBLE;
480 int x0 = (int)(item->x1);
481 int x1 = (int)(item->x2);
482 int y0 = (int)(item->y1);
483 int y1 = (int)(item->y2);
485 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
486 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
487 item->canvas->need_repick = TRUE;
488 }
489 }
491 /**
492 * Clears visible flag on item and requests a redraw.
493 */
494 void
495 sp_canvas_item_hide (SPCanvasItem *item)
496 {
497 g_return_if_fail (item != NULL);
498 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
500 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
501 return;
503 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
505 int x0 = (int)(item->x1);
506 int x1 = (int)(item->x2);
507 int y0 = (int)(item->y1);
508 int y1 = (int)(item->y2);
510 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
511 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
512 item->canvas->need_repick = TRUE;
513 }
514 }
516 /**
517 * Grab item under cursor.
518 *
519 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
520 */
521 int
522 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
523 {
524 g_return_val_if_fail (item != NULL, -1);
525 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
526 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
528 if (item->canvas->grabbed_item)
529 return -1;
531 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
532 return -1;
534 /* fixme: Top hack (Lauris) */
535 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
536 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
537 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
538 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
539 NULL, cursor, etime);
541 item->canvas->grabbed_item = item;
542 item->canvas->grabbed_event_mask = event_mask;
543 item->canvas->current_item = item; /* So that events go to the grabbed item */
545 return 0;
546 }
548 /**
549 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
550 * mouse.
551 *
552 * \param item A canvas item that holds a grab.
553 * \param etime The timestamp for ungrabbing the mouse.
554 */
555 void
556 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
557 {
558 g_return_if_fail (item != NULL);
559 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
561 if (item->canvas->grabbed_item != item)
562 return;
564 item->canvas->grabbed_item = NULL;
566 gdk_pointer_ungrab (etime);
567 }
569 /**
570 * Returns the product of all transformation matrices from the root item down
571 * to the item.
572 */
573 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
574 {
575 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
577 NR::Matrix affine = NR::identity();
579 while (item) {
580 affine *= item->xform;
581 item = item->parent;
582 }
583 return affine;
584 }
586 /**
587 * Helper that returns true iff item is descendant of parent.
588 */
589 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
590 {
591 while (item) {
592 if (item == parent)
593 return true;
594 item = item->parent;
595 }
597 return false;
598 }
600 /**
601 * Focus canvas, and item under cursor if it is not already focussed.
602 */
603 void
604 sp_canvas_item_grab_focus (SPCanvasItem *item)
605 {
606 g_return_if_fail (item != NULL);
607 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
608 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
610 SPCanvasItem *focused_item = item->canvas->focused_item;
612 if (focused_item) {
613 GdkEvent ev;
614 ev.focus_change.type = GDK_FOCUS_CHANGE;
615 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
616 ev.focus_change.send_event = FALSE;
617 ev.focus_change.in = FALSE;
619 emit_event (item->canvas, &ev);
620 }
622 item->canvas->focused_item = item;
623 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
625 if (focused_item) {
626 GdkEvent ev;
627 ev.focus_change.type = GDK_FOCUS_CHANGE;
628 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
629 ev.focus_change.send_event = FALSE;
630 ev.focus_change.in = TRUE;
632 emit_event (item->canvas, &ev);
633 }
634 }
636 /**
637 * Requests that the canvas queue an update for the specified item.
638 *
639 * To be used only by item implementations.
640 */
641 void
642 sp_canvas_item_request_update (SPCanvasItem *item)
643 {
644 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
645 return;
647 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
649 if (item->parent != NULL) {
650 /* Recurse up the tree */
651 sp_canvas_item_request_update (item->parent);
652 } else {
653 /* Have reached the top of the tree, make sure the update call gets scheduled. */
654 sp_canvas_request_update (item->canvas);
655 }
656 }
658 /**
659 * Returns position of item in group.
660 */
661 gint sp_canvas_item_order (SPCanvasItem * item)
662 {
663 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
664 }
666 /* SPCanvasGroup */
668 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
669 static void sp_canvas_group_init (SPCanvasGroup *group);
670 static void sp_canvas_group_destroy (GtkObject *object);
672 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
673 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
674 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
676 static SPCanvasItemClass *group_parent_class;
678 /**
679 * Registers SPCanvasGroup class with Gtk and returns its type number.
680 */
681 GType sp_canvas_group_get_type(void)
682 {
683 static GType type = 0;
684 if (!type) {
685 GTypeInfo info = {
686 sizeof(SPCanvasGroupClass),
687 0, // base_init
688 0, // base_finalize
689 (GClassInitFunc)sp_canvas_group_class_init,
690 0, // class_finalize
691 0, // class_data
692 sizeof(SPCanvasGroup),
693 0, // n_preallocs
694 (GInstanceInitFunc)sp_canvas_group_init,
695 0 // value_table
696 };
697 type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
698 }
699 return type;
700 }
702 /**
703 * Class initialization function for SPCanvasGroupClass
704 */
705 static void
706 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
707 {
708 GtkObjectClass *object_class = (GtkObjectClass *) klass;
709 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
711 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
713 object_class->destroy = sp_canvas_group_destroy;
715 item_class->update = sp_canvas_group_update;
716 item_class->render = sp_canvas_group_render;
717 item_class->point = sp_canvas_group_point;
718 }
720 /**
721 * Callback. Empty.
722 */
723 static void
724 sp_canvas_group_init (SPCanvasGroup */*group*/)
725 {
726 /* Nothing here */
727 }
729 /**
730 * Callback that destroys all items in group and calls group's virtual
731 * destroy() function.
732 */
733 static void
734 sp_canvas_group_destroy (GtkObject *object)
735 {
736 g_return_if_fail (object != NULL);
737 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
739 SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
741 GList *list = group->items;
742 while (list) {
743 SPCanvasItem *child = (SPCanvasItem *)list->data;
744 list = list->next;
746 gtk_object_destroy (GTK_OBJECT (child));
747 }
749 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
750 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
751 }
753 /**
754 * Update handler for canvas groups
755 */
756 static void
757 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
758 {
759 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
760 NR::ConvexHull corners(NR::Point(0, 0));
761 bool empty=true;
763 for (GList *list = group->items; list; list = list->next) {
764 SPCanvasItem *i = (SPCanvasItem *)list->data;
766 sp_canvas_item_invoke_update (i, affine, flags);
768 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
769 if (empty) {
770 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
771 empty = false;
772 } else {
773 corners.add(NR::Point(i->x1, i->y1));
774 }
775 corners.add(NR::Point(i->x2, i->y2));
776 }
777 }
779 NR::Maybe<NR::Rect> const bounds = corners.bounds();
780 if (bounds) {
781 item->x1 = bounds->min()[NR::X];
782 item->y1 = bounds->min()[NR::Y];
783 item->x2 = bounds->max()[NR::X];
784 item->y2 = bounds->max()[NR::Y];
785 } else {
786 // FIXME ?
787 item->x1 = item->x2 = item->y1 = item->y2 = 0;
788 }
789 }
791 /**
792 * Point handler for canvas groups.
793 */
794 static double
795 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
796 {
797 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
798 double const x = p[NR::X];
799 double const y = p[NR::Y];
800 int x1 = (int)(x - item->canvas->close_enough);
801 int y1 = (int)(y - item->canvas->close_enough);
802 int x2 = (int)(x + item->canvas->close_enough);
803 int y2 = (int)(y + item->canvas->close_enough);
805 double best = 0.0;
806 *actual_item = NULL;
808 double dist = 0.0;
810 for (GList *list = group->items; list; list = list->next) {
811 SPCanvasItem *child = (SPCanvasItem *)list->data;
813 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
814 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
816 int has_point;
817 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
818 dist = sp_canvas_item_invoke_point (child, p, &point_item);
819 has_point = TRUE;
820 } else
821 has_point = FALSE;
823 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
824 best = dist;
825 *actual_item = point_item;
826 }
827 }
828 }
830 return best;
831 }
833 /**
834 * Renders all visible canvas group items in buf rectangle.
835 */
836 static void
837 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
838 {
839 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
841 for (GList *list = group->items; list; list = list->next) {
842 SPCanvasItem *child = (SPCanvasItem *)list->data;
843 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
844 if ((child->x1 < buf->rect.x1) &&
845 (child->y1 < buf->rect.y1) &&
846 (child->x2 > buf->rect.x0) &&
847 (child->y2 > buf->rect.y0)) {
848 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
849 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
850 }
851 }
852 }
853 }
855 /**
856 * Adds an item to a canvas group.
857 */
858 static void
859 group_add (SPCanvasGroup *group, SPCanvasItem *item)
860 {
861 gtk_object_ref (GTK_OBJECT (item));
862 gtk_object_sink (GTK_OBJECT (item));
864 if (!group->items) {
865 group->items = g_list_append (group->items, item);
866 group->last = group->items;
867 } else {
868 group->last = g_list_append (group->last, item)->next;
869 }
871 sp_canvas_item_request_update (item);
872 }
874 /**
875 * Removes an item from a canvas group
876 */
877 static void
878 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
879 {
880 g_return_if_fail (group != NULL);
881 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
882 g_return_if_fail (item != NULL);
884 for (GList *children = group->items; children; children = children->next) {
885 if (children->data == item) {
887 /* Unparent the child */
889 item->parent = NULL;
890 gtk_object_unref (GTK_OBJECT (item));
892 /* Remove it from the list */
894 if (children == group->last) group->last = children->prev;
896 group->items = g_list_remove_link (group->items, children);
897 g_list_free (children);
898 break;
899 }
900 }
901 }
903 /* SPCanvas */
905 static void sp_canvas_class_init (SPCanvasClass *klass);
906 static void sp_canvas_init (SPCanvas *canvas);
907 static void sp_canvas_destroy (GtkObject *object);
909 static void sp_canvas_realize (GtkWidget *widget);
910 static void sp_canvas_unrealize (GtkWidget *widget);
912 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
913 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
915 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
916 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
917 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
918 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
919 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
920 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
921 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
922 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
924 static GtkWidgetClass *canvas_parent_class;
926 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
927 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
928 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
929 static int do_update (SPCanvas *canvas);
931 /**
932 * Registers the SPCanvas class if necessary, and returns the type ID
933 * associated to it.
934 *
935 * \return The type ID of the SPCanvas class.
936 **/
937 GType sp_canvas_get_type(void)
938 {
939 static GType type = 0;
940 if (!type) {
941 GTypeInfo info = {
942 sizeof(SPCanvasClass),
943 0, // base_init
944 0, // base_finalize
945 (GClassInitFunc)sp_canvas_class_init,
946 0, // class_finalize
947 0, // class_data
948 sizeof(SPCanvas),
949 0, // n_preallocs
950 (GInstanceInitFunc)sp_canvas_init,
951 0 // value_table
952 };
953 type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
954 }
955 return type;
956 }
958 /**
959 * Class initialization function for SPCanvasClass.
960 */
961 static void
962 sp_canvas_class_init (SPCanvasClass *klass)
963 {
964 GtkObjectClass *object_class = (GtkObjectClass *) klass;
965 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
967 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
969 object_class->destroy = sp_canvas_destroy;
971 widget_class->realize = sp_canvas_realize;
972 widget_class->unrealize = sp_canvas_unrealize;
973 widget_class->size_request = sp_canvas_size_request;
974 widget_class->size_allocate = sp_canvas_size_allocate;
975 widget_class->button_press_event = sp_canvas_button;
976 widget_class->button_release_event = sp_canvas_button;
977 widget_class->motion_notify_event = sp_canvas_motion;
978 widget_class->scroll_event = sp_canvas_scroll;
979 widget_class->expose_event = sp_canvas_expose;
980 widget_class->key_press_event = sp_canvas_key;
981 widget_class->key_release_event = sp_canvas_key;
982 widget_class->enter_notify_event = sp_canvas_crossing;
983 widget_class->leave_notify_event = sp_canvas_crossing;
984 widget_class->focus_in_event = sp_canvas_focus_in;
985 widget_class->focus_out_event = sp_canvas_focus_out;
986 }
988 /**
989 * Callback: object initialization for SPCanvas.
990 */
991 static void
992 sp_canvas_init (SPCanvas *canvas)
993 {
994 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
995 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
996 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
998 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
999 canvas->pick_event.crossing.x = 0;
1000 canvas->pick_event.crossing.y = 0;
1002 /* Create the root item as a special case */
1003 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1004 canvas->root->canvas = canvas;
1006 gtk_object_ref (GTK_OBJECT (canvas->root));
1007 gtk_object_sink (GTK_OBJECT (canvas->root));
1009 canvas->need_repick = TRUE;
1011 // See comment at in sp-canvas.h.
1012 canvas->gen_all_enter_events = false;
1014 canvas->tiles=NULL;
1015 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1016 canvas->tileH=canvas->tileV=0;
1018 canvas->forced_redraw_count = 0;
1019 canvas->forced_redraw_limit = -1;
1021 #if ENABLE_LCMS
1022 canvas->enable_cms_display_adj = false;
1023 canvas->cms_key = new Glib::ustring("");
1024 #endif // ENABLE_LCMS
1026 canvas->is_scrolling = false;
1028 }
1030 /**
1031 * Convenience function to remove the idle handler of a canvas.
1032 */
1033 static void
1034 remove_idle (SPCanvas *canvas)
1035 {
1036 if (canvas->idle_id) {
1037 gtk_idle_remove (canvas->idle_id);
1038 canvas->idle_id = 0;
1039 }
1040 }
1042 /*
1043 * Removes the transient state of the canvas (idle handler, grabs).
1044 */
1045 static void
1046 shutdown_transients (SPCanvas *canvas)
1047 {
1048 /* We turn off the need_redraw flag, since if the canvas is mapped again
1049 * it will request a redraw anyways. We do not turn off the need_update
1050 * flag, though, because updates are not queued when the canvas remaps
1051 * itself.
1052 */
1053 if (canvas->need_redraw) {
1054 canvas->need_redraw = FALSE;
1055 }
1056 if ( canvas->tiles ) g_free(canvas->tiles);
1057 canvas->tiles=NULL;
1058 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1059 canvas->tileH=canvas->tileV=0;
1061 if (canvas->grabbed_item) {
1062 canvas->grabbed_item = NULL;
1063 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1064 }
1066 remove_idle (canvas);
1067 }
1069 /**
1070 * Destroy handler for SPCanvas.
1071 */
1072 static void
1073 sp_canvas_destroy (GtkObject *object)
1074 {
1075 SPCanvas *canvas = SP_CANVAS (object);
1077 if (canvas->root) {
1078 gtk_object_unref (GTK_OBJECT (canvas->root));
1079 canvas->root = NULL;
1080 }
1082 shutdown_transients (canvas);
1084 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1085 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1086 }
1088 /**
1089 * Returns new canvas as widget.
1090 */
1091 GtkWidget *
1092 sp_canvas_new_aa (void)
1093 {
1094 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1096 return (GtkWidget *) canvas;
1097 }
1099 /**
1100 * The canvas widget's realize callback.
1101 */
1102 static void
1103 sp_canvas_realize (GtkWidget *widget)
1104 {
1105 SPCanvas *canvas = SP_CANVAS (widget);
1107 GdkWindowAttr attributes;
1108 attributes.window_type = GDK_WINDOW_CHILD;
1109 attributes.x = widget->allocation.x;
1110 attributes.y = widget->allocation.y;
1111 attributes.width = widget->allocation.width;
1112 attributes.height = widget->allocation.height;
1113 attributes.wclass = GDK_INPUT_OUTPUT;
1114 attributes.visual = gdk_rgb_get_visual ();
1115 attributes.colormap = gdk_rgb_get_cmap ();
1116 attributes.event_mask = (gtk_widget_get_events (widget) |
1117 GDK_EXPOSURE_MASK |
1118 GDK_BUTTON_PRESS_MASK |
1119 GDK_BUTTON_RELEASE_MASK |
1120 GDK_POINTER_MOTION_MASK |
1121 GDK_PROXIMITY_IN_MASK |
1122 GDK_PROXIMITY_OUT_MASK |
1123 GDK_KEY_PRESS_MASK |
1124 GDK_KEY_RELEASE_MASK |
1125 GDK_ENTER_NOTIFY_MASK |
1126 GDK_LEAVE_NOTIFY_MASK |
1127 GDK_FOCUS_CHANGE_MASK);
1128 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1130 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1131 gdk_window_set_user_data (widget->window, widget);
1133 if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1134 gtk_widget_set_events(widget, attributes.event_mask);
1136 widget->style = gtk_style_attach (widget->style, widget->window);
1138 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1140 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1141 }
1143 /**
1144 * The canvas widget's unrealize callback.
1145 */
1146 static void
1147 sp_canvas_unrealize (GtkWidget *widget)
1148 {
1149 SPCanvas *canvas = SP_CANVAS (widget);
1151 canvas->current_item = NULL;
1152 canvas->grabbed_item = NULL;
1153 canvas->focused_item = NULL;
1155 shutdown_transients (canvas);
1157 gdk_gc_destroy (canvas->pixmap_gc);
1158 canvas->pixmap_gc = NULL;
1160 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1161 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1162 }
1164 /**
1165 * The canvas widget's size_request callback.
1166 */
1167 static void
1168 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1169 {
1170 static_cast<void>(SP_CANVAS (widget));
1172 req->width = 256;
1173 req->height = 256;
1174 }
1176 /**
1177 * The canvas widget's size_allocate callback.
1178 */
1179 static void
1180 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1181 {
1182 SPCanvas *canvas = SP_CANVAS (widget);
1184 /* Schedule redraw of new region */
1185 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1186 if (allocation->width > widget->allocation.width) {
1187 sp_canvas_request_redraw (canvas,
1188 canvas->x0 + widget->allocation.width,
1189 0,
1190 canvas->x0 + allocation->width,
1191 canvas->y0 + allocation->height);
1192 }
1193 if (allocation->height > widget->allocation.height) {
1194 sp_canvas_request_redraw (canvas,
1195 0,
1196 canvas->y0 + widget->allocation.height,
1197 canvas->x0 + allocation->width,
1198 canvas->y0 + allocation->height);
1199 }
1201 widget->allocation = *allocation;
1203 if (GTK_WIDGET_REALIZED (widget)) {
1204 gdk_window_move_resize (widget->window,
1205 widget->allocation.x, widget->allocation.y,
1206 widget->allocation.width, widget->allocation.height);
1207 }
1208 }
1210 /**
1211 * Helper that emits an event for an item in the canvas, be it the current
1212 * item, grabbed item, or focused item, as appropriate.
1213 */
1214 static int
1215 emit_event (SPCanvas *canvas, GdkEvent *event)
1216 {
1217 guint mask;
1219 if (canvas->grabbed_item) {
1220 switch (event->type) {
1221 case GDK_ENTER_NOTIFY:
1222 mask = GDK_ENTER_NOTIFY_MASK;
1223 break;
1224 case GDK_LEAVE_NOTIFY:
1225 mask = GDK_LEAVE_NOTIFY_MASK;
1226 break;
1227 case GDK_MOTION_NOTIFY:
1228 mask = GDK_POINTER_MOTION_MASK;
1229 break;
1230 case GDK_BUTTON_PRESS:
1231 case GDK_2BUTTON_PRESS:
1232 case GDK_3BUTTON_PRESS:
1233 mask = GDK_BUTTON_PRESS_MASK;
1234 break;
1235 case GDK_BUTTON_RELEASE:
1236 mask = GDK_BUTTON_RELEASE_MASK;
1237 break;
1238 case GDK_KEY_PRESS:
1239 mask = GDK_KEY_PRESS_MASK;
1240 break;
1241 case GDK_KEY_RELEASE:
1242 mask = GDK_KEY_RELEASE_MASK;
1243 break;
1244 case GDK_SCROLL:
1245 mask = GDK_SCROLL;
1246 break;
1247 default:
1248 mask = 0;
1249 break;
1250 }
1252 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1253 }
1255 /* Convert to world coordinates -- we have two cases because of diferent
1256 * offsets of the fields in the event structures.
1257 */
1259 GdkEvent ev = *event;
1261 switch (ev.type) {
1262 case GDK_ENTER_NOTIFY:
1263 case GDK_LEAVE_NOTIFY:
1264 ev.crossing.x += canvas->x0;
1265 ev.crossing.y += canvas->y0;
1266 break;
1267 case GDK_MOTION_NOTIFY:
1268 case GDK_BUTTON_PRESS:
1269 case GDK_2BUTTON_PRESS:
1270 case GDK_3BUTTON_PRESS:
1271 case GDK_BUTTON_RELEASE:
1272 ev.motion.x += canvas->x0;
1273 ev.motion.y += canvas->y0;
1274 break;
1275 default:
1276 break;
1277 }
1279 /* Choose where we send the event */
1281 /* canvas->current_item becomes NULL in some cases under Win32
1282 ** (e.g. if the pointer leaves the window). So this is a hack that
1283 ** Lauris applied to SP to get around the problem.
1284 */
1285 SPCanvasItem* item = NULL;
1286 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1287 item = canvas->grabbed_item;
1288 } else {
1289 item = canvas->current_item;
1290 }
1292 if (canvas->focused_item &&
1293 ((event->type == GDK_KEY_PRESS) ||
1294 (event->type == GDK_KEY_RELEASE) ||
1295 (event->type == GDK_FOCUS_CHANGE))) {
1296 item = canvas->focused_item;
1297 }
1299 /* The event is propagated up the hierarchy (for if someone connected to
1300 * a group instead of a leaf event), and emission is stopped if a
1301 * handler returns TRUE, just like for GtkWidget events.
1302 */
1304 gint finished = FALSE;
1306 while (item && !finished) {
1307 gtk_object_ref (GTK_OBJECT (item));
1308 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1309 SPCanvasItem *parent = item->parent;
1310 gtk_object_unref (GTK_OBJECT (item));
1311 item = parent;
1312 }
1314 return finished;
1315 }
1317 /**
1318 * Helper that re-picks the current item in the canvas, based on the event's
1319 * coordinates and emits enter/leave events for items as appropriate.
1320 */
1321 static int
1322 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1323 {
1324 int button_down = 0;
1325 double x, y;
1327 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1328 return FALSE;
1330 int retval = FALSE;
1332 if (canvas->gen_all_enter_events == false) {
1333 // If a button is down, we'll perform enter and leave events on the
1334 // current item, but not enter on any other item. This is more or
1335 // less like X pointer grabbing for canvas items.
1336 //
1337 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1338 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1340 if (!button_down) canvas->left_grabbed_item = FALSE;
1341 }
1343 /* Save the event in the canvas. This is used to synthesize enter and
1344 * leave events in case the current item changes. It is also used to
1345 * re-pick the current item if the current one gets deleted. Also,
1346 * synthesize an enter event.
1347 */
1348 if (event != &canvas->pick_event) {
1349 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1350 /* these fields have the same offsets in both types of events */
1352 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1353 canvas->pick_event.crossing.window = event->motion.window;
1354 canvas->pick_event.crossing.send_event = event->motion.send_event;
1355 canvas->pick_event.crossing.subwindow = NULL;
1356 canvas->pick_event.crossing.x = event->motion.x;
1357 canvas->pick_event.crossing.y = event->motion.y;
1358 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1359 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1360 canvas->pick_event.crossing.focus = FALSE;
1361 canvas->pick_event.crossing.state = event->motion.state;
1363 /* these fields don't have the same offsets in both types of events */
1365 if (event->type == GDK_MOTION_NOTIFY) {
1366 canvas->pick_event.crossing.x_root = event->motion.x_root;
1367 canvas->pick_event.crossing.y_root = event->motion.y_root;
1368 } else {
1369 canvas->pick_event.crossing.x_root = event->button.x_root;
1370 canvas->pick_event.crossing.y_root = event->button.y_root;
1371 }
1372 } else {
1373 canvas->pick_event = *event;
1374 }
1375 }
1377 /* Don't do anything else if this is a recursive call */
1378 if (canvas->in_repick) return retval;
1380 /* LeaveNotify means that there is no current item, so we don't look for one */
1381 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1382 /* these fields don't have the same offsets in both types of events */
1384 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1385 x = canvas->pick_event.crossing.x;
1386 y = canvas->pick_event.crossing.y;
1387 } else {
1388 x = canvas->pick_event.motion.x;
1389 y = canvas->pick_event.motion.y;
1390 }
1392 /* world coords */
1393 x += canvas->x0;
1394 y += canvas->y0;
1396 /* find the closest item */
1397 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1398 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1399 } else {
1400 canvas->new_current_item = NULL;
1401 }
1402 } else {
1403 canvas->new_current_item = NULL;
1404 }
1406 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1407 return retval; /* current item did not change */
1408 }
1410 /* Synthesize events for old and new current items */
1412 if ((canvas->new_current_item != canvas->current_item)
1413 && (canvas->current_item != NULL)
1414 && !canvas->left_grabbed_item) {
1415 GdkEvent new_event;
1416 SPCanvasItem *item;
1418 item = canvas->current_item;
1420 new_event = canvas->pick_event;
1421 new_event.type = GDK_LEAVE_NOTIFY;
1423 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1424 new_event.crossing.subwindow = NULL;
1425 canvas->in_repick = TRUE;
1426 retval = emit_event (canvas, &new_event);
1427 canvas->in_repick = FALSE;
1428 }
1430 if (canvas->gen_all_enter_events == false) {
1431 // new_current_item may have been set to NULL during the call to
1432 // emit_event() above
1433 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1434 canvas->left_grabbed_item = TRUE;
1435 return retval;
1436 }
1437 }
1439 /* Handle the rest of cases */
1441 canvas->left_grabbed_item = FALSE;
1442 canvas->current_item = canvas->new_current_item;
1444 if (canvas->current_item != NULL) {
1445 GdkEvent new_event;
1447 new_event = canvas->pick_event;
1448 new_event.type = GDK_ENTER_NOTIFY;
1449 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1450 new_event.crossing.subwindow = NULL;
1451 retval = emit_event (canvas, &new_event);
1452 }
1454 return retval;
1455 }
1457 /**
1458 * Button event handler for the canvas.
1459 */
1460 static gint
1461 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1462 {
1463 SPCanvas *canvas = SP_CANVAS (widget);
1465 int retval = FALSE;
1467 /* dispatch normally regardless of the event's window if an item has
1468 has a pointer grab in effect */
1469 if (!canvas->grabbed_item &&
1470 event->window != SP_CANVAS_WINDOW (canvas))
1471 return retval;
1473 int mask;
1474 switch (event->button) {
1475 case 1:
1476 mask = GDK_BUTTON1_MASK;
1477 break;
1478 case 2:
1479 mask = GDK_BUTTON2_MASK;
1480 break;
1481 case 3:
1482 mask = GDK_BUTTON3_MASK;
1483 break;
1484 case 4:
1485 mask = GDK_BUTTON4_MASK;
1486 break;
1487 case 5:
1488 mask = GDK_BUTTON5_MASK;
1489 break;
1490 default:
1491 mask = 0;
1492 }
1494 switch (event->type) {
1495 case GDK_BUTTON_PRESS:
1496 case GDK_2BUTTON_PRESS:
1497 case GDK_3BUTTON_PRESS:
1498 /* Pick the current item as if the button were not pressed, and
1499 * then process the event.
1500 */
1501 canvas->state = event->state;
1502 pick_current_item (canvas, (GdkEvent *) event);
1503 canvas->state ^= mask;
1504 retval = emit_event (canvas, (GdkEvent *) event);
1505 break;
1507 case GDK_BUTTON_RELEASE:
1508 /* Process the event as if the button were pressed, then repick
1509 * after the button has been released
1510 */
1511 canvas->state = event->state;
1512 retval = emit_event (canvas, (GdkEvent *) event);
1513 event->state ^= mask;
1514 canvas->state = event->state;
1515 pick_current_item (canvas, (GdkEvent *) event);
1516 event->state ^= mask;
1517 break;
1519 default:
1520 g_assert_not_reached ();
1521 }
1523 return retval;
1524 }
1526 /**
1527 * Scroll event handler for the canvas.
1528 *
1529 * \todo FIXME: generate motion events to re-select items.
1530 */
1531 static gint
1532 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1533 {
1534 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1535 }
1537 /**
1538 * Motion event handler for the canvas.
1539 */
1540 static int
1541 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1542 {
1543 SPCanvas *canvas = SP_CANVAS (widget);
1545 if (event->window != SP_CANVAS_WINDOW (canvas))
1546 return FALSE;
1548 if (canvas->pixmap_gc == NULL) // canvas being deleted
1549 return FALSE;
1551 if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1552 gint x, y;
1553 gdk_window_get_pointer (widget->window, &x, &y, NULL);
1554 event->x = x;
1555 event->y = y;
1556 }
1558 canvas->state = event->state;
1559 pick_current_item (canvas, (GdkEvent *) event);
1561 return emit_event (canvas, (GdkEvent *) event);
1562 }
1564 static void
1565 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)
1566 {
1567 GtkWidget *widget = GTK_WIDGET (canvas);
1569 SPCanvasBuf buf;
1570 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1571 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1572 } else {
1573 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1574 }
1576 // Mark the region clean
1577 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1579 buf.buf_rowstride = sw * 4;
1580 buf.rect.x0 = x0;
1581 buf.rect.y0 = y0;
1582 buf.rect.x1 = x1;
1583 buf.rect.y1 = y1;
1584 buf.visible_rect.x0 = draw_x1;
1585 buf.visible_rect.y0 = draw_y1;
1586 buf.visible_rect.x1 = draw_x2;
1587 buf.visible_rect.y1 = draw_y2;
1588 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1589 buf.bg_color = (((color->red & 0xff00) << 8)
1590 | (color->green & 0xff00)
1591 | (color->blue >> 8));
1592 buf.is_empty = true;
1594 buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1596 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1597 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1598 }
1600 #if ENABLE_LCMS
1601 cmsHTRANSFORM transf = 0;
1602 long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1603 if ( fromDisplay ) {
1604 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1605 } else {
1606 transf = Inkscape::colorprofile_get_display_transform();
1607 }
1608 #endif // ENABLE_LCMS
1610 if (buf.is_empty) {
1611 #if ENABLE_LCMS
1612 if ( transf && canvas->enable_cms_display_adj ) {
1613 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1614 }
1615 #endif // ENABLE_LCMS
1616 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1617 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1618 canvas->pixmap_gc,
1619 TRUE,
1620 x0 - canvas->x0, y0 - canvas->y0,
1621 x1 - x0, y1 - y0);
1622 } else {
1624 #if ENABLE_LCMS
1625 if ( transf && canvas->enable_cms_display_adj ) {
1626 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1627 guchar* p = buf.buf + (sw * 3) * yy;
1628 cmsDoTransform( transf, p, p, (x1 - x0) );
1629 }
1630 }
1631 #endif // ENABLE_LCMS
1633 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1634 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1635 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1636 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1637 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1638 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1640 ///#define CANVAS_OUTPUT_VIA_CAIRO
1642 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1644 buf.cst = cairo_image_surface_create_for_data (
1645 buf.buf,
1646 CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
1647 x1 - x0, y1 - y0,
1648 buf.buf_rowstride
1649 );
1650 cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1651 cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1652 cairo_paint (window_ct);
1653 cairo_destroy (window_ct);
1654 cairo_surface_finish (buf.cst);
1655 cairo_surface_destroy (buf.cst);
1657 #else
1659 NRPixBlock b3;
1660 nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1662 NRPixBlock b4;
1663 nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1664 buf.buf,
1665 buf.buf_rowstride,
1666 FALSE, FALSE);
1668 // this does the 32->24 squishing, using an assembler routine:
1669 nr_blit_pixblock_pixblock (&b3, &b4);
1671 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1672 canvas->pixmap_gc,
1673 x0 - canvas->x0, y0 - canvas->y0,
1674 x1 - x0, y1 - y0,
1675 GDK_RGB_DITHER_MAX,
1676 b3.data.px,
1677 sw * 3,
1678 x0 - canvas->x0, y0 - canvas->y0);
1680 nr_pixblock_release (&b3);
1681 nr_pixblock_release (&b4);
1682 #endif
1683 }
1685 cairo_surface_t *cst = cairo_get_target(buf.ct);
1686 cairo_destroy (buf.ct);
1687 cairo_surface_finish (cst);
1688 cairo_surface_destroy (cst);
1690 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1691 nr_pixelstore_256K_free (buf.buf);
1692 } else {
1693 nr_pixelstore_1M_free (buf.buf);
1694 }
1695 }
1697 struct PaintRectSetup {
1698 SPCanvas* canvas;
1699 NRRectL big_rect;
1700 GTimeVal start_time;
1701 int max_pixels;
1702 NR::Point mouse_loc;
1703 };
1705 /**
1706 * Paint the given rect, recursively subdividing the region until it is the size of a single
1707 * buffer.
1708 *
1709 * @return true if the drawing completes
1710 */
1711 static int
1712 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1713 {
1714 GTimeVal now;
1715 g_get_current_time (&now);
1717 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1718 + (now.tv_usec - setup->start_time.tv_usec);
1720 // Allow only very fast buffers to be run together;
1721 // as soon as the total redraw time exceeds 1ms, cancel;
1722 // this returns control to the idle loop and allows Inkscape to process user input
1723 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1724 // it will get back and finish painting what remains to paint.
1725 if (elapsed > 1000) {
1727 // Interrupting redraw isn't always good.
1728 // For example, when you drag one node of a big path, only the buffer containing
1729 // the mouse cursor will be redrawn again and again, and the rest of the path
1730 // will remain stale because Inkscape never has enough idle time to redraw all
1731 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1732 // If this limit is set, and if we have aborted redraw more times than is allowed,
1733 // interrupting is blocked and we're forced to redraw full screen once
1734 // (after which we can again interrupt forced_redraw_limit times).
1735 if (setup->canvas->forced_redraw_limit < 0 ||
1736 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1738 if (setup->canvas->forced_redraw_limit != -1) {
1739 setup->canvas->forced_redraw_count++;
1740 }
1742 return false;
1743 }
1744 }
1746 // Find the optimal buffer dimensions
1747 int bw = this_rect.x1 - this_rect.x0;
1748 int bh = this_rect.y1 - this_rect.y0;
1749 if ((bw < 1) || (bh < 1))
1750 return 0;
1752 if (bw * bh < setup->max_pixels) {
1753 // We are small enough
1754 sp_canvas_paint_single_buffer (setup->canvas,
1755 this_rect.x0, this_rect.y0,
1756 this_rect.x1, this_rect.y1,
1757 setup->big_rect.x0, setup->big_rect.y0,
1758 setup->big_rect.x1, setup->big_rect.y1, bw);
1759 return 1;
1760 }
1762 NRRectL lo = this_rect;
1763 NRRectL hi = this_rect;
1765 /*
1766 This test determines the redraw strategy:
1768 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1769 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1770 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1771 and seems to be faster for drawings with many smaller objects at zoom-out.
1773 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1774 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1775 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1776 faster.
1778 The default for now is the strips mode.
1779 */
1780 if (bw < bh || bh < 2 * TILE_SIZE) {
1781 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1782 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1783 // Make sure that mid lies on a tile boundary
1784 mid = (mid / TILE_SIZE) * TILE_SIZE;
1786 lo.x1 = mid;
1787 hi.x0 = mid;
1789 if (setup->mouse_loc[NR::X] < mid) {
1790 // Always paint towards the mouse first
1791 return sp_canvas_paint_rect_internal(setup, lo)
1792 && sp_canvas_paint_rect_internal(setup, hi);
1793 } else {
1794 return sp_canvas_paint_rect_internal(setup, hi)
1795 && sp_canvas_paint_rect_internal(setup, lo);
1796 }
1797 } else {
1798 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1799 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1800 // Make sure that mid lies on a tile boundary
1801 mid = (mid / TILE_SIZE) * TILE_SIZE;
1803 lo.y1 = mid;
1804 hi.y0 = mid;
1806 if (setup->mouse_loc[NR::Y] < mid) {
1807 // Always paint towards the mouse first
1808 return sp_canvas_paint_rect_internal(setup, lo)
1809 && sp_canvas_paint_rect_internal(setup, hi);
1810 } else {
1811 return sp_canvas_paint_rect_internal(setup, hi)
1812 && sp_canvas_paint_rect_internal(setup, lo);
1813 }
1814 }
1815 }
1818 /**
1819 * Helper that draws a specific rectangular part of the canvas.
1820 *
1821 * @return true if the rectangle painting succeeds.
1822 */
1823 static bool
1824 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1825 {
1826 g_return_val_if_fail (!canvas->need_update, false);
1828 NRRectL rect;
1829 rect.x0 = xx0;
1830 rect.x1 = xx1;
1831 rect.y0 = yy0;
1832 rect.y1 = yy1;
1834 // Clip rect-to-draw by the current visible area
1835 rect.x0 = MAX (rect.x0, canvas->x0);
1836 rect.y0 = MAX (rect.y0, canvas->y0);
1837 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1838 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1840 #ifdef DEBUG_REDRAW
1841 // paint the area to redraw yellow
1842 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1843 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1844 canvas->pixmap_gc,
1845 TRUE,
1846 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1847 rect.x1 - rect.x0, rect.y1 - rect.y0);
1848 #endif
1850 PaintRectSetup setup;
1852 setup.canvas = canvas;
1853 setup.big_rect = rect;
1855 // Save the mouse location
1856 gint x, y;
1857 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1858 setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1860 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1861 // use 256K as a compromise to not slow down gradients
1862 // 256K is the cached buffer and we need 4 channels
1863 setup.max_pixels = 65536; // 256K/4
1864 } else {
1865 // paths only, so 1M works faster
1866 // 1M is the cached buffer and we need 4 channels
1867 setup.max_pixels = 262144;
1868 }
1870 // Start the clock
1871 g_get_current_time(&(setup.start_time));
1873 // Go
1874 return sp_canvas_paint_rect_internal(&setup, rect);
1875 }
1877 /**
1878 * Force a full redraw after a specified number of interrupted redraws
1879 */
1880 void
1881 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1882 g_return_if_fail(canvas != NULL);
1884 canvas->forced_redraw_limit = count;
1885 canvas->forced_redraw_count = 0;
1886 }
1888 /**
1889 * End forced full redraw requests
1890 */
1891 void
1892 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1893 g_return_if_fail(canvas != NULL);
1895 canvas->forced_redraw_limit = -1;
1896 }
1898 /**
1899 * The canvas widget's expose callback.
1900 */
1901 static gint
1902 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1903 {
1904 SPCanvas *canvas = SP_CANVAS (widget);
1906 if (!GTK_WIDGET_DRAWABLE (widget) ||
1907 (event->window != SP_CANVAS_WINDOW (canvas)))
1908 return FALSE;
1910 int n_rects;
1911 GdkRectangle *rects;
1912 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1914 for (int i = 0; i < n_rects; i++) {
1915 NRRectL rect;
1917 rect.x0 = rects[i].x + canvas->x0;
1918 rect.y0 = rects[i].y + canvas->y0;
1919 rect.x1 = rect.x0 + rects[i].width;
1920 rect.y1 = rect.y0 + rects[i].height;
1922 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1923 }
1925 if (n_rects > 0)
1926 g_free (rects);
1928 return FALSE;
1929 }
1931 /**
1932 * The canvas widget's keypress callback.
1933 */
1934 static gint
1935 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1936 {
1937 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1938 }
1940 /**
1941 * Crossing event handler for the canvas.
1942 */
1943 static gint
1944 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1945 {
1946 SPCanvas *canvas = SP_CANVAS (widget);
1948 if (event->window != SP_CANVAS_WINDOW (canvas))
1949 return FALSE;
1951 canvas->state = event->state;
1952 return pick_current_item (canvas, (GdkEvent *) event);
1953 }
1955 /**
1956 * Focus in handler for the canvas.
1957 */
1958 static gint
1959 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1960 {
1961 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1963 SPCanvas *canvas = SP_CANVAS (widget);
1965 if (canvas->focused_item) {
1966 return emit_event (canvas, (GdkEvent *) event);
1967 } else {
1968 return FALSE;
1969 }
1970 }
1972 /**
1973 * Focus out handler for the canvas.
1974 */
1975 static gint
1976 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1977 {
1978 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1980 SPCanvas *canvas = SP_CANVAS (widget);
1982 if (canvas->focused_item)
1983 return emit_event (canvas, (GdkEvent *) event);
1984 else
1985 return FALSE;
1986 }
1988 /**
1989 * Helper that repaints the areas in the canvas that need it.
1990 *
1991 * @return true if all the dirty parts have been redrawn
1992 */
1993 static int
1994 paint (SPCanvas *canvas)
1995 {
1996 if (canvas->need_update) {
1997 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1998 canvas->need_update = FALSE;
1999 }
2001 if (!canvas->need_redraw)
2002 return TRUE;
2004 Gdk::Region to_paint;
2006 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2007 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2008 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2010 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2011 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2012 TILE_SIZE, TILE_SIZE));
2013 }
2015 }
2016 }
2018 if (!to_paint.empty()) {
2019 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2020 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2021 for (Iter i=rect.begin(); i != rect.end(); ++i) {
2022 int x0 = (*i).get_x();
2023 int y0 = (*i).get_y();
2024 int x1 = x0 + (*i).get_width();
2025 int y1 = y0 + (*i).get_height();
2026 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2027 // Aborted
2028 return FALSE;
2029 };
2030 }
2031 }
2033 canvas->need_redraw = FALSE;
2035 // we've had a full unaborted redraw, reset the full redraw counter
2036 if (canvas->forced_redraw_limit != -1) {
2037 canvas->forced_redraw_count = 0;
2038 }
2040 return TRUE;
2041 }
2043 /**
2044 * Helper that invokes update, paint, and repick on canvas.
2045 */
2046 static int
2047 do_update (SPCanvas *canvas)
2048 {
2049 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2050 return TRUE;
2052 /* Cause the update if necessary */
2053 if (canvas->need_update) {
2054 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2055 canvas->need_update = FALSE;
2056 }
2058 /* Paint if able to */
2059 if (GTK_WIDGET_DRAWABLE (canvas)) {
2060 return paint (canvas);
2061 }
2063 /* Pick new current item */
2064 while (canvas->need_repick) {
2065 canvas->need_repick = FALSE;
2066 pick_current_item (canvas, &canvas->pick_event);
2067 }
2069 return TRUE;
2070 }
2072 /**
2073 * Idle handler for the canvas that deals with pending updates and redraws.
2074 */
2075 static gint
2076 idle_handler (gpointer data)
2077 {
2078 GDK_THREADS_ENTER ();
2080 SPCanvas *canvas = SP_CANVAS (data);
2082 int const ret = do_update (canvas);
2084 if (ret) {
2085 /* Reset idle id */
2086 canvas->idle_id = 0;
2087 }
2089 GDK_THREADS_LEAVE ();
2091 return !ret;
2092 }
2094 /**
2095 * Convenience function to add an idle handler to a canvas.
2096 */
2097 static void
2098 add_idle (SPCanvas *canvas)
2099 {
2100 if (canvas->idle_id != 0)
2101 return;
2103 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2104 }
2106 /**
2107 * Returns the root group of the specified canvas.
2108 */
2109 SPCanvasGroup *
2110 sp_canvas_root (SPCanvas *canvas)
2111 {
2112 g_return_val_if_fail (canvas != NULL, NULL);
2113 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2115 return SP_CANVAS_GROUP (canvas->root);
2116 }
2118 /**
2119 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2120 */
2121 void
2122 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2123 {
2124 g_return_if_fail (canvas != NULL);
2125 g_return_if_fail (SP_IS_CANVAS (canvas));
2127 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2128 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2129 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2130 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2132 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2133 canvas->dy0 = cy;
2134 canvas->x0 = ix;
2135 canvas->y0 = iy;
2137 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2139 if (!clear) {
2140 // scrolling without zoom; redraw only the newly exposed areas
2141 if ((dx != 0) || (dy != 0)) {
2142 canvas->is_scrolling = is_scrolling;
2143 if (GTK_WIDGET_REALIZED (canvas)) {
2144 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2145 }
2146 }
2147 } else {
2148 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2149 }
2150 }
2152 /**
2153 * Updates canvas if necessary.
2154 */
2155 void
2156 sp_canvas_update_now (SPCanvas *canvas)
2157 {
2158 g_return_if_fail (canvas != NULL);
2159 g_return_if_fail (SP_IS_CANVAS (canvas));
2161 if (!(canvas->need_update ||
2162 canvas->need_redraw))
2163 return;
2165 do_update (canvas);
2166 }
2168 /**
2169 * Update callback for canvas widget.
2170 */
2171 static void
2172 sp_canvas_request_update (SPCanvas *canvas)
2173 {
2174 canvas->need_update = TRUE;
2175 add_idle (canvas);
2176 }
2178 /**
2179 * Forces redraw of rectangular canvas area.
2180 */
2181 void
2182 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2183 {
2184 NRRectL bbox;
2185 NRRectL visible;
2186 NRRectL clip;
2188 g_return_if_fail (canvas != NULL);
2189 g_return_if_fail (SP_IS_CANVAS (canvas));
2191 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2192 if ((x0 >= x1) || (y0 >= y1)) return;
2194 bbox.x0 = x0;
2195 bbox.y0 = y0;
2196 bbox.x1 = x1;
2197 bbox.y1 = y1;
2199 visible.x0 = canvas->x0;
2200 visible.y0 = canvas->y0;
2201 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2202 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2204 nr_rect_l_intersect (&clip, &bbox, &visible);
2206 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2207 add_idle (canvas);
2208 }
2210 /**
2211 * Sets world coordinates from win and canvas.
2212 */
2213 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2214 {
2215 g_return_if_fail (canvas != NULL);
2216 g_return_if_fail (SP_IS_CANVAS (canvas));
2218 if (worldx) *worldx = canvas->x0 + winx;
2219 if (worldy) *worldy = canvas->y0 + winy;
2220 }
2222 /**
2223 * Sets win coordinates from world and canvas.
2224 */
2225 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2226 {
2227 g_return_if_fail (canvas != NULL);
2228 g_return_if_fail (SP_IS_CANVAS (canvas));
2230 if (winx) *winx = worldx - canvas->x0;
2231 if (winy) *winy = worldy - canvas->y0;
2232 }
2234 /**
2235 * Converts point from win to world coordinates.
2236 */
2237 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2238 {
2239 g_assert (canvas != NULL);
2240 g_assert (SP_IS_CANVAS (canvas));
2242 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2243 }
2245 /**
2246 * Converts point from world to win coordinates.
2247 */
2248 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2249 {
2250 g_assert (canvas != NULL);
2251 g_assert (SP_IS_CANVAS (canvas));
2253 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2254 }
2256 /**
2257 * Returns true if point given in world coordinates is inside window.
2258 */
2259 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2260 {
2261 g_assert( canvas != NULL );
2262 g_assert(SP_IS_CANVAS(canvas));
2264 using NR::X;
2265 using NR::Y;
2266 GtkWidget const &w = *GTK_WIDGET(canvas);
2267 return ( ( canvas->x0 <= world[X] ) &&
2268 ( canvas->y0 <= world[Y] ) &&
2269 ( world[X] < canvas->x0 + w.allocation.width ) &&
2270 ( world[Y] < canvas->y0 + w.allocation.height ) );
2271 }
2273 /**
2274 * Return canvas window coordinates as NR::Rect.
2275 */
2276 NR::Rect SPCanvas::getViewbox() const
2277 {
2278 GtkWidget const *w = GTK_WIDGET(this);
2279 return NR::Rect(NR::Point(dx0, dy0),
2280 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2281 }
2283 /**
2284 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2285 */
2286 NR::IRect SPCanvas::getViewboxIntegers() const
2287 {
2288 GtkWidget const *w = GTK_WIDGET(this);
2289 return NR::IRect(NR::IPoint(x0, y0),
2290 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2291 }
2293 inline int sp_canvas_tile_floor(int x)
2294 {
2295 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2296 }
2298 inline int sp_canvas_tile_ceil(int x)
2299 {
2300 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2301 }
2303 /**
2304 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2305 */
2306 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2307 {
2308 if ( nl >= nr || nt >= nb ) {
2309 if ( canvas->tiles ) g_free(canvas->tiles);
2310 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2311 canvas->tileH=canvas->tileV=0;
2312 canvas->tiles=NULL;
2313 return;
2314 }
2315 int tl=sp_canvas_tile_floor(nl);
2316 int tt=sp_canvas_tile_floor(nt);
2317 int tr=sp_canvas_tile_ceil(nr);
2318 int tb=sp_canvas_tile_ceil(nb);
2320 int nh = tr-tl, nv = tb-tt;
2321 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2322 for (int i=tl; i<tr; i++) {
2323 for (int j=tt; j<tb; j++) {
2324 int ind = (i-tl) + (j-tt)*nh;
2325 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2326 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2327 } else {
2328 ntiles[ind]=0; // newly exposed areas get 0
2329 }
2330 }
2331 }
2332 if ( canvas->tiles ) g_free(canvas->tiles);
2333 canvas->tiles=ntiles;
2334 canvas->tLeft=tl;
2335 canvas->tTop=tt;
2336 canvas->tRight=tr;
2337 canvas->tBottom=tb;
2338 canvas->tileH=nh;
2339 canvas->tileV=nv;
2340 }
2342 /*
2343 * Helper that queues a canvas rectangle for redraw
2344 */
2345 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2346 canvas->need_redraw = TRUE;
2348 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2349 }
2351 /**
2352 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2353 */
2354 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2355 {
2356 if ( nl >= nr || nt >= nb ) {
2357 return;
2358 }
2359 int tl=sp_canvas_tile_floor(nl);
2360 int tt=sp_canvas_tile_floor(nt);
2361 int tr=sp_canvas_tile_ceil(nr);
2362 int tb=sp_canvas_tile_ceil(nb);
2363 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2364 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2365 if ( tr > canvas->tRight ) tr=canvas->tRight;
2366 if ( tt < canvas->tTop ) tt=canvas->tTop;
2367 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2369 for (int i=tl; i<tr; i++) {
2370 for (int j=tt; j<tb; j++) {
2371 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2372 }
2373 }
2374 }
2377 /*
2378 Local Variables:
2379 mode:c++
2380 c-file-style:"stroustrup"
2381 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2382 indent-tabs-mode:nil
2383 fill-column:99
2384 End:
2385 */
2386 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :