1 /*
2 * RGBA display list system for inkscape
3 *
4 * Author:
5 * Lauris Kaplinski <lauris@kaplinski.com>
6 *
7 * Copyright (C) 2001-2002 Lauris Kaplinski
8 * Copyright (C) 2001 Ximian, Inc.
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
13 #include <libnr/nr-blit.h>
14 #include <gtk/gtksignal.h>
16 #include <display/sp-canvas-util.h>
17 #include "helper/sp-marshal.h"
18 #include <display/nr-arena.h>
19 #include <display/nr-arena-group.h>
20 #include <display/canvas-arena.h>
21 #include <display/inkscape-cairo.h>
23 enum {
24 ARENA_EVENT,
25 LAST_SIGNAL
26 };
28 static void sp_canvas_arena_class_init(SPCanvasArenaClass *klass);
29 static void sp_canvas_arena_init(SPCanvasArena *group);
30 static void sp_canvas_arena_destroy(GtkObject *object);
32 static void sp_canvas_arena_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
33 static void sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf);
34 static double sp_canvas_arena_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
35 static gint sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event);
37 static gint sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event);
39 static void sp_canvas_arena_request_update (NRArena *arena, NRArenaItem *item, void *data);
40 static void sp_canvas_arena_request_render (NRArena *arena, NRRectL *area, void *data);
42 NRArenaEventVector carenaev = {
43 {NULL},
44 sp_canvas_arena_request_update,
45 sp_canvas_arena_request_render
46 };
48 static SPCanvasItemClass *parent_class;
49 static guint signals[LAST_SIGNAL] = {0};
51 GtkType
52 sp_canvas_arena_get_type (void)
53 {
54 static GtkType type = 0;
55 if (!type) {
56 GtkTypeInfo info = {
57 (gchar *)"SPCanvasArena",
58 sizeof (SPCanvasArena),
59 sizeof (SPCanvasArenaClass),
60 (GtkClassInitFunc) sp_canvas_arena_class_init,
61 (GtkObjectInitFunc) sp_canvas_arena_init,
62 NULL, NULL, NULL
63 };
64 type = gtk_type_unique (SP_TYPE_CANVAS_ITEM, &info);
65 }
66 return type;
67 }
69 static void
70 sp_canvas_arena_class_init (SPCanvasArenaClass *klass)
71 {
72 GtkObjectClass *object_class;
73 SPCanvasItemClass *item_class;
75 object_class = (GtkObjectClass *) klass;
76 item_class = (SPCanvasItemClass *) klass;
78 parent_class = (SPCanvasItemClass*)gtk_type_class (SP_TYPE_CANVAS_ITEM);
80 signals[ARENA_EVENT] = gtk_signal_new ("arena_event",
81 GTK_RUN_LAST,
82 GTK_CLASS_TYPE(object_class),
83 ((glong)((guint8*)&(klass->arena_event) - (guint8*)klass)),
84 sp_marshal_INT__POINTER_POINTER,
85 GTK_TYPE_INT, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER);
87 object_class->destroy = sp_canvas_arena_destroy;
89 item_class->update = sp_canvas_arena_update;
90 item_class->render = sp_canvas_arena_render;
91 item_class->point = sp_canvas_arena_point;
92 item_class->event = sp_canvas_arena_event;
93 }
95 static void
96 sp_canvas_arena_init (SPCanvasArena *arena)
97 {
98 arena->sticky = FALSE;
100 arena->arena = NRArena::create();
101 arena->arena->canvasarena = arena;
102 arena->root = NRArenaGroup::create(arena->arena);
103 nr_arena_group_set_transparent (NR_ARENA_GROUP (arena->root), TRUE);
105 arena->active = NULL;
107 nr_active_object_add_listener ((NRActiveObject *) arena->arena, (NRObjectEventVector *) &carenaev, sizeof (carenaev), arena);
108 }
110 static void
111 sp_canvas_arena_destroy (GtkObject *object)
112 {
113 SPCanvasArena *arena = SP_CANVAS_ARENA (object);
115 if (arena->active) {
116 nr_object_unref ((NRObject *) arena->active);
117 arena->active = NULL;
118 }
120 if (arena->root) {
121 nr_arena_item_unref (arena->root);
122 arena->root = NULL;
123 }
125 if (arena->arena) {
126 nr_active_object_remove_listener_by_data ((NRActiveObject *) arena->arena, arena);
128 nr_object_unref ((NRObject *) arena->arena);
129 arena->arena = NULL;
130 }
132 if (GTK_OBJECT_CLASS (parent_class)->destroy)
133 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
134 }
136 static void
137 sp_canvas_arena_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
138 {
139 SPCanvasArena *arena = SP_CANVAS_ARENA (item);
141 if (((SPCanvasItemClass *) parent_class)->update)
142 (* ((SPCanvasItemClass *) parent_class)->update) (item, affine, flags);
144 arena->gc.transform = affine;
146 guint reset;
147 reset = (flags & SP_CANVAS_UPDATE_AFFINE)? NR_ARENA_ITEM_STATE_ALL : NR_ARENA_ITEM_STATE_NONE;
149 nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_ALL, reset);
151 item->x1 = arena->root->bbox.x0 - 1;
152 item->y1 = arena->root->bbox.y0 - 1;
153 item->x2 = arena->root->bbox.x1 + 1;
154 item->y2 = arena->root->bbox.y1 + 1;
156 if (arena->cursor) {
157 /* Mess with enter/leave notifiers */
158 NRArenaItem *new_arena = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
159 if (new_arena != arena->active) {
160 GdkEventCrossing ec;
161 ec.window = GTK_WIDGET (item->canvas)->window;
162 ec.send_event = TRUE;
163 ec.subwindow = ec.window;
164 ec.time = GDK_CURRENT_TIME;
165 ec.x = arena->c[Geom::X];
166 ec.y = arena->c[Geom::Y];
167 /* fixme: */
168 if (arena->active) {
169 ec.type = GDK_LEAVE_NOTIFY;
170 sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
171 }
172 /* fixme: This is not optimal - better track ::destroy (Lauris) */
173 if (arena->active) nr_object_unref ((NRObject *) arena->active);
174 arena->active = new_arena;
175 if (arena->active) nr_object_ref ((NRObject *) arena->active);
176 if (arena->active) {
177 ec.type = GDK_ENTER_NOTIFY;
178 sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
179 }
180 }
181 }
182 }
184 static void
185 sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf)
186 {
187 gint bw, bh;
189 SPCanvasArena *arena = SP_CANVAS_ARENA (item);
191 nr_arena_item_invoke_update (arena->root, NULL, &arena->gc,
192 NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_RENDER,
193 NR_ARENA_ITEM_STATE_NONE);
195 sp_canvas_prepare_buffer(buf);
197 bw = buf->rect.x1 - buf->rect.x0;
198 bh = buf->rect.y1 - buf->rect.y0;
199 if ((bw < 1) || (bh < 1)) return;
201 NRRectL area;
202 NRPixBlock cb;
204 area.x0 = buf->rect.x0;
205 area.y0 = buf->rect.y0;
206 area.x1 = buf->rect.x1;
207 area.y1 = buf->rect.y1;
209 nr_pixblock_setup_extern (&cb, NR_PIXBLOCK_MODE_R8G8B8A8P, area.x0, area.y0, area.x1, area.y1,
210 buf->buf,
211 buf->buf_rowstride,
212 FALSE, FALSE);
214 cb.visible_area = buf->visible_rect;
215 cairo_t *ct = nr_create_cairo_context (&area, &cb);
216 nr_arena_item_invoke_render (ct, arena->root, &area, &cb, 0);
218 cairo_surface_t *cst = cairo_get_target(ct);
219 cairo_destroy (ct);
220 cairo_surface_finish (cst);
221 cairo_surface_destroy (cst);
223 nr_pixblock_release (&cb);
224 }
226 static double
227 sp_canvas_arena_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
228 {
229 SPCanvasArena *arena = SP_CANVAS_ARENA (item);
231 nr_arena_item_invoke_update (arena->root, NULL, &arena->gc,
232 NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_PICK,
233 NR_ARENA_ITEM_STATE_NONE);
235 NRArenaItem *picked = nr_arena_item_invoke_pick (arena->root, p, arena->arena->delta, arena->sticky);
237 arena->picked = picked;
239 if (picked) {
240 *actual_item = item;
241 return 0.0;
242 }
244 return 1e18;
245 }
247 static gint
248 sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event)
249 {
250 NRArenaItem *new_arena;
251 /* fixme: This sucks, we have to handle enter/leave notifiers */
253 SPCanvasArena *arena = SP_CANVAS_ARENA (item);
255 gint ret = FALSE;
257 switch (event->type) {
258 case GDK_ENTER_NOTIFY:
259 if (!arena->cursor) {
260 if (arena->active) {
261 //g_warning ("Cursor entered to arena with already active item");
262 nr_object_unref ((NRObject *) arena->active);
263 }
264 arena->cursor = TRUE;
266 /* TODO ... event -> arena transform? */
267 arena->c = Geom::Point(event->crossing.x, event->crossing.y);
269 /* fixme: Not sure abut this, but seems the right thing (Lauris) */
270 nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE);
271 arena->active = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
272 if (arena->active) nr_object_ref ((NRObject *) arena->active);
273 ret = sp_canvas_arena_send_event (arena, event);
274 }
275 break;
277 case GDK_LEAVE_NOTIFY:
278 if (arena->cursor) {
279 ret = sp_canvas_arena_send_event (arena, event);
280 if (arena->active) nr_object_unref ((NRObject *) arena->active);
281 arena->active = NULL;
282 arena->cursor = FALSE;
283 }
284 break;
286 case GDK_MOTION_NOTIFY:
287 /* TODO ... event -> arena transform? */
288 arena->c = Geom::Point(event->motion.x, event->motion.y);
290 /* fixme: Not sure abut this, but seems the right thing (Lauris) */
291 nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE);
292 new_arena = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
293 if (new_arena != arena->active) {
294 GdkEventCrossing ec;
295 ec.window = event->motion.window;
296 ec.send_event = event->motion.send_event;
297 ec.subwindow = event->motion.window;
298 ec.time = event->motion.time;
299 ec.x = event->motion.x;
300 ec.y = event->motion.y;
301 /* fixme: */
302 if (arena->active) {
303 ec.type = GDK_LEAVE_NOTIFY;
304 ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
305 }
306 if (arena->active) nr_object_unref ((NRObject *) arena->active);
307 arena->active = new_arena;
308 if (arena->active) nr_object_ref ((NRObject *) arena->active);
309 if (arena->active) {
310 ec.type = GDK_ENTER_NOTIFY;
311 ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
312 }
313 }
314 ret = sp_canvas_arena_send_event (arena, event);
315 break;
317 default:
318 /* Just send event */
319 ret = sp_canvas_arena_send_event (arena, event);
320 break;
321 }
323 return ret;
324 }
326 static gint
327 sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event)
328 {
329 gint ret = FALSE;
331 /* Send event to arena */
332 gtk_signal_emit (GTK_OBJECT (arena), signals[ARENA_EVENT], arena->active, event, &ret);
334 return ret;
335 }
337 static void
338 sp_canvas_arena_request_update (NRArena */*arena*/, NRArenaItem */*item*/, void *data)
339 {
340 sp_canvas_item_request_update (SP_CANVAS_ITEM (data));
341 }
343 static void
344 sp_canvas_arena_request_render (NRArena */*arena*/, NRRectL *area, void *data)
345 {
346 sp_canvas_request_redraw (SP_CANVAS_ITEM (data)->canvas, area->x0, area->y0, area->x1, area->y1);
347 }
349 void
350 sp_canvas_arena_set_pick_delta (SPCanvasArena *ca, gdouble delta)
351 {
352 g_return_if_fail (ca != NULL);
353 g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
355 /* fixme: repick? */
356 ca->delta = delta;
357 }
359 void
360 sp_canvas_arena_set_sticky (SPCanvasArena *ca, gboolean sticky)
361 {
362 g_return_if_fail (ca != NULL);
363 g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
365 /* fixme: repick? */
366 ca->sticky = sticky;
367 }
369 void
370 sp_canvas_arena_render_pixblock (SPCanvasArena *ca, NRPixBlock *pb)
371 {
372 NRRectL area;
374 g_return_if_fail (ca != NULL);
375 g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
377 /* fixme: */
378 pb->empty = FALSE;
380 area.x0 = pb->area.x0;
381 area.y0 = pb->area.y0;
382 area.x1 = pb->area.x1;
383 area.y1 = pb->area.y1;
385 nr_arena_item_invoke_render (NULL, ca->root, &area, pb, 0);
386 }
389 /*
390 Local Variables:
391 mode:c++
392 c-file-style:"stroustrup"
393 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
394 indent-tabs-mode:nil
395 fill-column:99
396 End:
397 */
398 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :