Code

Split SPCanvasItem and SPCanvasGroup to individual .h files. Removed forward header.
[inkscape.git] / src / display / canvas-arena.cpp
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);
110 static void
111 sp_canvas_arena_destroy (GtkObject *object)
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);
136 static void
137 sp_canvas_arena_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
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     }
184 static void
185 sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf)
187     gint bw, bh;
188  
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);
226 static double
227 sp_canvas_arena_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
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;
247 static gint
248 sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event)
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;
326 static gint
327 sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event)
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;
337 static void
338 sp_canvas_arena_request_update (NRArena */*arena*/, NRArenaItem */*item*/, void *data)
340     sp_canvas_item_request_update (SP_CANVAS_ITEM (data));
343 static void
344 sp_canvas_arena_request_render (NRArena */*arena*/, NRRectL *area, void *data)
346     sp_canvas_request_redraw (SP_CANVAS_ITEM (data)->canvas, area->x0, area->y0, area->x1, area->y1);
349 void
350 sp_canvas_arena_set_pick_delta (SPCanvasArena *ca, gdouble delta)
352     g_return_if_fail (ca != NULL);
353     g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
355     /* fixme: repick? */
356     ca->delta = delta;
359 void
360 sp_canvas_arena_set_sticky (SPCanvasArena *ca, gboolean sticky)
362     g_return_if_fail (ca != NULL);
363     g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
365     /* fixme: repick? */
366     ca->sticky = sticky;
369 void
370 sp_canvas_arena_render_pixblock (SPCanvasArena *ca, NRPixBlock *pb)
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);
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 :