Code

af9a38281916624b3a6f33d8720ed744af4bcae5
[inkscape.git] / src / display / canvas-arena.cpp
1 #define __SP_CANVAS_ARENA_C__
3 /*
4  * RGBA display list system for inkscape
5  *
6  * Author:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *
9  * Copyright (C) 2001-2002 Lauris Kaplinski
10  * Copyright (C) 2001 Ximian, Inc.
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
15 #include <libnr/nr-blit.h>
16 #include <gtk/gtksignal.h>
18 #include <display/display-forward.h>
19 #include <display/sp-canvas-util.h>
20 #include "helper/sp-marshal.h"
21 #include <display/nr-arena.h>
22 #include <display/nr-arena-group.h>
23 #include <display/canvas-arena.h>
24 #include <display/inkscape-cairo.h>
26 enum {
27     ARENA_EVENT,
28     LAST_SIGNAL
29 };
31 static void sp_canvas_arena_class_init(SPCanvasArenaClass *klass);
32 static void sp_canvas_arena_init(SPCanvasArena *group);
33 static void sp_canvas_arena_destroy(GtkObject *object);
35 static void sp_canvas_arena_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
36 static void sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf);
37 static double sp_canvas_arena_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
38 static gint sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event);
40 static gint sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event);
42 static void sp_canvas_arena_request_update (NRArena *arena, NRArenaItem *item, void *data);
43 static void sp_canvas_arena_request_render (NRArena *arena, NRRectL *area, void *data);
45 NRArenaEventVector carenaev = {
46     {NULL},
47     sp_canvas_arena_request_update,
48     sp_canvas_arena_request_render
49 };
51 static SPCanvasItemClass *parent_class;
52 static guint signals[LAST_SIGNAL] = {0};
54 GtkType
55 sp_canvas_arena_get_type (void)
56 {
57     static GtkType type = 0;
58     if (!type) {
59         GtkTypeInfo info = {
60             (gchar *)"SPCanvasArena",
61             sizeof (SPCanvasArena),
62             sizeof (SPCanvasArenaClass),
63             (GtkClassInitFunc) sp_canvas_arena_class_init,
64             (GtkObjectInitFunc) sp_canvas_arena_init,
65             NULL, NULL, NULL
66         };
67         type = gtk_type_unique (SP_TYPE_CANVAS_ITEM, &info);
68     }
69     return type;
70 }
72 static void
73 sp_canvas_arena_class_init (SPCanvasArenaClass *klass)
74 {
75     GtkObjectClass *object_class;
76     SPCanvasItemClass *item_class;
78     object_class = (GtkObjectClass *) klass;
79     item_class = (SPCanvasItemClass *) klass;
81     parent_class = (SPCanvasItemClass*)gtk_type_class (SP_TYPE_CANVAS_ITEM);
83     signals[ARENA_EVENT] = gtk_signal_new ("arena_event",
84                                            GTK_RUN_LAST,
85                                            GTK_CLASS_TYPE(object_class),
86                                            ((glong)((guint8*)&(klass->arena_event) - (guint8*)klass)),
87                                            sp_marshal_INT__POINTER_POINTER,
88                                            GTK_TYPE_INT, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER);
90     object_class->destroy = sp_canvas_arena_destroy;
92     item_class->update = sp_canvas_arena_update;
93     item_class->render = sp_canvas_arena_render;
94     item_class->point = sp_canvas_arena_point;
95     item_class->event = sp_canvas_arena_event;
96 }
98 static void
99 sp_canvas_arena_init (SPCanvasArena *arena)
101     arena->sticky = FALSE;
103     arena->arena = NRArena::create();
104     arena->arena->canvasarena = arena;
105     arena->root = NRArenaGroup::create(arena->arena);
106     nr_arena_group_set_transparent (NR_ARENA_GROUP (arena->root), TRUE);
108     arena->active = NULL;
110     nr_active_object_add_listener ((NRActiveObject *) arena->arena, (NRObjectEventVector *) &carenaev, sizeof (carenaev), arena);
113 static void
114 sp_canvas_arena_destroy (GtkObject *object)
116     SPCanvasArena *arena = SP_CANVAS_ARENA (object);
118     if (arena->active) {
119         nr_object_unref ((NRObject *) arena->active);
120         arena->active = NULL;
121     }
123     if (arena->root) {
124         nr_arena_item_unref (arena->root);
125         arena->root = NULL;
126     }
128     if (arena->arena) {
129         nr_active_object_remove_listener_by_data ((NRActiveObject *) arena->arena, arena);
131         nr_object_unref ((NRObject *) arena->arena);
132         arena->arena = NULL;
133     }
135     if (GTK_OBJECT_CLASS (parent_class)->destroy)
136         (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
139 static void
140 sp_canvas_arena_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
142     SPCanvasArena *arena = SP_CANVAS_ARENA (item);
144     if (((SPCanvasItemClass *) parent_class)->update)
145         (* ((SPCanvasItemClass *) parent_class)->update) (item, affine, flags);
147     arena->gc.transform = affine;
149     guint reset;
150     reset = (flags & SP_CANVAS_UPDATE_AFFINE)? NR_ARENA_ITEM_STATE_ALL : NR_ARENA_ITEM_STATE_NONE;
152     nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_ALL, reset);
154     item->x1 = arena->root->bbox.x0 - 1;
155     item->y1 = arena->root->bbox.y0 - 1;
156     item->x2 = arena->root->bbox.x1 + 1;
157     item->y2 = arena->root->bbox.y1 + 1;
159     if (arena->cursor) {
160         /* Mess with enter/leave notifiers */
161         NRArenaItem *new_arena = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
162         if (new_arena != arena->active) {
163             GdkEventCrossing ec;
164             ec.window = GTK_WIDGET (item->canvas)->window;
165             ec.send_event = TRUE;
166             ec.subwindow = ec.window;
167             ec.time = GDK_CURRENT_TIME;
168             ec.x = arena->c[Geom::X];
169             ec.y = arena->c[Geom::Y];
170             /* fixme: */
171             if (arena->active) {
172                 ec.type = GDK_LEAVE_NOTIFY;
173                 sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
174             }
175             /* fixme: This is not optimal - better track ::destroy (Lauris) */
176             if (arena->active) nr_object_unref ((NRObject *) arena->active);
177             arena->active = new_arena;
178             if (arena->active) nr_object_ref ((NRObject *) arena->active);
179             if (arena->active) {
180                 ec.type = GDK_ENTER_NOTIFY;
181                 sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
182             }
183         }
184     }
187 static void
188 sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf)
190     gint bw, bh;
191  
192     SPCanvasArena *arena = SP_CANVAS_ARENA (item);
194     nr_arena_item_invoke_update (arena->root, NULL, &arena->gc,
195                                  NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_RENDER,
196                                  NR_ARENA_ITEM_STATE_NONE);
198     sp_canvas_prepare_buffer(buf);
200     bw = buf->rect.x1 - buf->rect.x0;
201     bh = buf->rect.y1 - buf->rect.y0;
202     if ((bw < 1) || (bh < 1)) return;
204     NRRectL area;
205     NRPixBlock cb;
207     area.x0 = buf->rect.x0;
208     area.y0 = buf->rect.y0;
209     area.x1 = buf->rect.x1;
210     area.y1 = buf->rect.y1;
212     nr_pixblock_setup_extern (&cb, NR_PIXBLOCK_MODE_R8G8B8A8P, area.x0, area.y0, area.x1, area.y1,
213                               buf->buf,
214                               buf->buf_rowstride,
215                               FALSE, FALSE);
217     cb.visible_area = buf->visible_rect;
218     cairo_t *ct = nr_create_cairo_context (&area, &cb);
219     nr_arena_item_invoke_render (ct, arena->root, &area, &cb, 0);
221     cairo_surface_t *cst = cairo_get_target(ct);
222     cairo_destroy (ct);
223     cairo_surface_finish (cst);
224     cairo_surface_destroy (cst);
226     nr_pixblock_release (&cb);
229 static double
230 sp_canvas_arena_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
232     SPCanvasArena *arena = SP_CANVAS_ARENA (item);
234     nr_arena_item_invoke_update (arena->root, NULL, &arena->gc,
235                                  NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_PICK,
236                                  NR_ARENA_ITEM_STATE_NONE);
238     NRArenaItem *picked = nr_arena_item_invoke_pick (arena->root, p, arena->arena->delta, arena->sticky);
240     arena->picked = picked;
242     if (picked) {
243         *actual_item = item;
244         return 0.0;
245     }
247     return 1e18;
250 static gint
251 sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event)
253     NRArenaItem *new_arena;
254     /* fixme: This sucks, we have to handle enter/leave notifiers */
256     SPCanvasArena *arena = SP_CANVAS_ARENA (item);
258     gint ret = FALSE;
260     switch (event->type) {
261         case GDK_ENTER_NOTIFY:
262             if (!arena->cursor) {
263                 if (arena->active) {
264                     //g_warning ("Cursor entered to arena with already active item");
265                     nr_object_unref ((NRObject *) arena->active);
266                 }
267                 arena->cursor = TRUE;
269                 /* TODO ... event -> arena transform? */
270                 arena->c = Geom::Point(event->crossing.x, event->crossing.y);
272                 /* fixme: Not sure abut this, but seems the right thing (Lauris) */
273                 nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE);
274                 arena->active = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
275                 if (arena->active) nr_object_ref ((NRObject *) arena->active);
276                 ret = sp_canvas_arena_send_event (arena, event);
277             }
278             break;
280         case GDK_LEAVE_NOTIFY:
281             if (arena->cursor) {
282                 ret = sp_canvas_arena_send_event (arena, event);
283                 if (arena->active) nr_object_unref ((NRObject *) arena->active);
284                 arena->active = NULL;
285                 arena->cursor = FALSE;
286             }
287             break;
289         case GDK_MOTION_NOTIFY:
290             /* TODO ... event -> arena transform? */
291             arena->c = Geom::Point(event->motion.x, event->motion.y);
293             /* fixme: Not sure abut this, but seems the right thing (Lauris) */
294             nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE);
295             new_arena = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
296             if (new_arena != arena->active) {
297                 GdkEventCrossing ec;
298                 ec.window = event->motion.window;
299                 ec.send_event = event->motion.send_event;
300                 ec.subwindow = event->motion.window;
301                 ec.time = event->motion.time;
302                 ec.x = event->motion.x;
303                 ec.y = event->motion.y;
304                 /* fixme: */
305                 if (arena->active) {
306                     ec.type = GDK_LEAVE_NOTIFY;
307                     ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
308                 }
309                 if (arena->active) nr_object_unref ((NRObject *) arena->active);
310                 arena->active = new_arena;
311                 if (arena->active) nr_object_ref ((NRObject *) arena->active);
312                 if (arena->active) {
313                     ec.type = GDK_ENTER_NOTIFY;
314                     ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
315                 }
316             }
317             ret = sp_canvas_arena_send_event (arena, event);
318             break;
320         default:
321             /* Just send event */
322             ret = sp_canvas_arena_send_event (arena, event);
323             break;
324     }
326     return ret;
329 static gint
330 sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event)
332     gint ret = FALSE;
334     /* Send event to arena */
335     gtk_signal_emit (GTK_OBJECT (arena), signals[ARENA_EVENT], arena->active, event, &ret);
337     return ret;
340 static void
341 sp_canvas_arena_request_update (NRArena */*arena*/, NRArenaItem */*item*/, void *data)
343     sp_canvas_item_request_update (SP_CANVAS_ITEM (data));
346 static void
347 sp_canvas_arena_request_render (NRArena */*arena*/, NRRectL *area, void *data)
349     sp_canvas_request_redraw (SP_CANVAS_ITEM (data)->canvas, area->x0, area->y0, area->x1, area->y1);
352 void
353 sp_canvas_arena_set_pick_delta (SPCanvasArena *ca, gdouble delta)
355     g_return_if_fail (ca != NULL);
356     g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
358     /* fixme: repick? */
359     ca->delta = delta;
362 void
363 sp_canvas_arena_set_sticky (SPCanvasArena *ca, gboolean sticky)
365     g_return_if_fail (ca != NULL);
366     g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
368     /* fixme: repick? */
369     ca->sticky = sticky;
372 void
373 sp_canvas_arena_render_pixblock (SPCanvasArena *ca, NRPixBlock *pb)
375     NRRectL area;
377     g_return_if_fail (ca != NULL);
378     g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
380     /* fixme: */
381     pb->empty = FALSE;
383     area.x0 = pb->area.x0;
384     area.y0 = pb->area.y0;
385     area.x1 = pb->area.x1;
386     area.y1 = pb->area.y1;
388     nr_arena_item_invoke_render (NULL, ca->root, &area, pb, 0);
392 /*
393   Local Variables:
394   mode:c++
395   c-file-style:"stroustrup"
396   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
397   indent-tabs-mode:nil
398   fill-column:99
399   End:
400 */
401 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :