/*
* bonobo-canvas-component.c: implements the CORBA interface for
* the Bonobo::Canvas:Item interface used in Bonobo::Views.
*
* Author:
* Miguel de Icaza (miguel@kernel.org)
*
* (C) 1999-2001 Ximian, Inc.
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <gtk/gtksignal.h>
#include <bonobo/Bonobo.h>
#include <libart_lgpl/art_affine.h>
#include <libgnomecanvas/gnome-canvas.h>
#include <gdkconfig.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#include <gdk/gdkprivate.h>
#include <gtk/gtk.h>
#include <bonobo/bonobo-control.h>
#include <bonobo/bonobo-exception.h>
#include <bonobo/bonobo-ui-component.h>
#include <bonobo/bonobo-canvas-component.h>
#include <bonobo/bonobo-ui-marshal.h>
#undef BONOBO_DISABLE_DEPRECATED
#include <bonobo/bonobo-xobject.h>
enum {
SET_BOUNDS,
EVENT,
LAST_SIGNAL
};
static gint gcc_signals [LAST_SIGNAL] = { 0, 0, };
typedef BonoboCanvasComponent Gcc;
#define GCC(x) BONOBO_CANVAS_COMPONENT(x)
struct _BonoboCanvasComponentPrivate {
GnomeCanvasItem *item;
};
#define PARENT_TYPE BONOBO_TYPE_OBJECT
/* Returns the GnomeCanvasItemClass of an object */
#define ICLASS(x) GNOME_CANVAS_ITEM_CLASS ((GTK_OBJECT_GET_CLASS (x)))
static GObjectClass *gcc_parent_class;
static gboolean do_update_flag = FALSE;
typedef struct
{
gpointer *arg1;
gpointer *arg2;
} EmitLater;
static gboolean
CORBA_SVP_Segment_to_SVPSeg (Bonobo_Canvas_SVPSegment *seg, ArtSVPSeg *art_seg)
{
int i;
art_seg->points = art_new (ArtPoint, seg->points._length);
if (!art_seg->points)
return FALSE;
art_seg->dir = seg->up ? 0 : 1;
art_seg->bbox.x0 = seg->bbox.x0;
art_seg->bbox.x1 = seg->bbox.x1;
art_seg->bbox.y0 = seg->bbox.y0;
art_seg->bbox.y1 = seg->bbox.y1;
art_seg->n_points = seg->points._length;
for (i = 0; i < art_seg->n_points; i++){
art_seg->points [i].x = seg->points._buffer [i].x;
art_seg->points [i].y = seg->points._buffer [i].y;
}
return TRUE;
}
static void
free_seg (ArtSVPSeg *seg)
{
g_assert (seg != NULL);
g_assert (seg->points != NULL);
art_free (seg->points);
}
/*
* Encodes an ArtUta
*/
static Bonobo_Canvas_ArtUTA *
CORBA_UTA (ArtUta *uta)
{
Bonobo_Canvas_ArtUTA *cuta;
cuta = Bonobo_Canvas_ArtUTA__alloc ();
if (!cuta)
return NULL;
if (!uta) {
cuta->width = 0;
cuta->height = 0;
cuta->utiles._length = 0;
cuta->utiles._maximum = 0;
return cuta;
}
cuta->utiles._buffer = CORBA_sequence_Bonobo_Canvas_int32_allocbuf (uta->width * uta->height);
cuta->utiles._length = uta->width * uta->height;
cuta->utiles._maximum = uta->width * uta->height;
if (!cuta->utiles._buffer) {
CORBA_free (cuta);
return NULL;
}
cuta->x0 = uta->x0;
cuta->y0 = uta->y0;
cuta->width = uta->width;
cuta->height = uta->height;
memcpy (cuta->utiles._buffer, uta->utiles, uta->width * uta->height * sizeof (ArtUtaBbox));
return cuta;
}
static void
restore_state (GnomeCanvasItem *item, const Bonobo_Canvas_State *state)
{
double affine [6];
int i;
for (i = 0; i < 6; i++)
affine [i] = state->item_aff [i];
gnome_canvas_item_affine_absolute (item->canvas->root, affine);
item->canvas->pixels_per_unit = state->pixels_per_unit;
item->canvas->scroll_x1 = state->canvas_scroll_x1;
item->canvas->scroll_y1 = state->canvas_scroll_y1;
item->canvas->zoom_xofs = state->zoom_xofs;
item->canvas->zoom_yofs = state->zoom_yofs;
}
/* This is copied from gnome-canvas.c since it is declared static */
static void
invoke_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
{
int child_flags;
double *child_affine;
double i2w[6], w2c[6], i2c[6];
child_flags = flags;
if (!(item->object.flags & GNOME_CANVAS_ITEM_VISIBLE))
child_flags &= ~GNOME_CANVAS_UPDATE_IS_VISIBLE;
/* Apply the child item's transform */
gnome_canvas_item_i2w_affine (item, i2w);
gnome_canvas_w2c_affine (item->canvas, w2c);
art_affine_multiply (i2c, i2w, w2c);
child_affine = i2c;
/* apply object flags to child flags */
child_flags &= ~GNOME_CANVAS_UPDATE_REQUESTED;
if (item->object.flags & GNOME_CANVAS_ITEM_NEED_UPDATE)
child_flags |= GNOME_CANVAS_UPDATE_REQUESTED;
if (item->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)
child_flags |= GNOME_CANVAS_UPDATE_AFFINE;
if (item->object.flags & GNOME_CANVAS_ITEM_NEED_CLIP)
child_flags |= GNOME_CANVAS_UPDATE_CLIP;
if (item->object.flags & GNOME_CANVAS_ITEM_NEED_VIS)
child_flags |= GNOME_CANVAS_UPDATE_VISIBILITY;
if ((child_flags & (GNOME_CANVAS_UPDATE_REQUESTED
| GNOME_CANVAS_UPDATE_AFFINE
| GNOME_CANVAS_UPDATE_CLIP
| GNOME_CANVAS_UPDATE_VISIBILITY))
&& GNOME_CANVAS_ITEM_GET_CLASS (item)->update)
(* GNOME_CANVAS_ITEM_GET_CLASS (item)->update) (
item, child_affine, clip_path, child_flags);
}
static Bonobo_Canvas_ArtUTA *
impl_Bonobo_Canvas_Component_update (PortableServer_Servant servant,
const Bonobo_Canvas_State *state,
const Bonobo_Canvas_affine aff,
const Bonobo_Canvas_SVP *clip_path,
CORBA_long flags,
CORBA_double *x1,
CORBA_double *y1,
CORBA_double *x2,
CORBA_double *y2,
CORBA_Environment *ev)
{
Gcc *gcc = GCC (bonobo_object_from_servant (servant));
GnomeCanvasItem *item = GNOME_CANVAS_ITEM (gcc->priv->item);
double affine [6];
int i;
ArtSVP *svp = NULL;
Bonobo_Canvas_ArtUTA *cuta;
GnomeCanvasItemClass *gci_class = gtk_type_class (
gnome_canvas_item_get_type ());
restore_state (item, state);
for (i = 0; i < 6; i++)
affine [i] = aff [i];
if (clip_path->_length > 0) {
svp = art_alloc (sizeof (ArtSVP) + (clip_path->_length * sizeof (ArtSVPSeg)));
if (svp == NULL)
goto fail;
svp->n_segs = clip_path->_length;
for (i = 0; svp->n_segs; i++) {
gboolean ok;
ok = CORBA_SVP_Segment_to_SVPSeg (&clip_path->_buffer [i], &svp->segs [i]);
if (!ok) {
int j;
for (j = 0; j < i; j++) {
free_seg (&svp->segs [j]);
art_free (svp);
goto fail;
}
}
}
}
invoke_update (item, (double *)aff, svp, flags);
if (svp){
for (i = 0; i < svp->n_segs; i++)
free_seg (&svp->segs [i]);
art_free (svp);
}
fail:
if (getenv ("CC_DEBUG"))
printf ("%g %g %g %g\n", item->x1, item->x2, item->y1, item->y2);
*x1 = item->x1;
*x2 = item->x2;
*y1 = item->y1;
*y2 = item->y2;
cuta = CORBA_UTA (item->canvas->redraw_area);
if (cuta == NULL) {
CORBA_exception_set_system (ev, ex_CORBA_NO_MEMORY, CORBA_COMPLETED_NO);
return NULL;
}
/*
* Now, mark our canvas as fully up to date
*/
/* Clears flags for root item. */
(* gci_class->update) (item->canvas->root, affine, svp, flags);
if (item->canvas->redraw_area) {
art_uta_free (item->canvas->redraw_area);
item->canvas->redraw_area = NULL;
}
item->canvas->need_redraw = FALSE;
return cuta;
}
static void
impl_Bonobo_Canvas_Component_realize (PortableServer_Servant servant,
const CORBA_char *window,
CORBA_Environment *ev)
{
Gcc *gcc = GCC (bonobo_object_from_servant (servant));
GnomeCanvasItem *item = GNOME_CANVAS_ITEM (gcc->priv->item);
GdkWindow *gdk_window = gdk_window_foreign_new_for_display
(gtk_widget_get_display (GTK_WIDGET (item->canvas)),
bonobo_control_x11_from_window_id (window));
if (gdk_window == NULL) {
g_warning ("Invalid window id passed='%s'", window);
return;
}
item->canvas->layout.bin_window = gdk_window;
ICLASS (item)->realize (item);
}
static void
impl_Bonobo_Canvas_Component_unrealize (PortableServer_Servant servant,
CORBA_Environment *ev)
{
Gcc *gcc = GCC (bonobo_object_from_servant (servant));
GnomeCanvasItem *item = GNOME_CANVAS_ITEM (gcc->priv->item);
ICLASS (item)->unrealize (item);
if (item->canvas->layout.bin_window) {
g_object_unref (item->canvas->layout.bin_window);
item->canvas->layout.bin_window = NULL;
}
}
static void
impl_Bonobo_Canvas_Component_map (PortableServer_Servant servant,
CORBA_Environment *ev)
{
Gcc *gcc = GCC (bonobo_object_from_servant (servant));
GnomeCanvasItem *item = GNOME_CANVAS_ITEM (gcc->priv->item);
ICLASS (item)->map (item);
}
static void
impl_Bonobo_Canvas_Component_unmap (PortableServer_Servant servant,
CORBA_Environment *ev)
{
Gcc *gcc = GCC (bonobo_object_from_servant (servant));
GnomeCanvasItem *item = GNOME_CANVAS_ITEM (gcc->priv->item);
ICLASS (item)->unmap (item);
}
static void
my_gdk_pixmap_foreign_release (GdkPixmap *pixmap)
{
GdkWindowObject *priv = (GdkWindowObject *) pixmap;
if (G_OBJECT (priv)->ref_count != 1){
g_warning ("This item is keeping a refcount to a foreign pixmap");
return;
}
#ifdef FIXME
gdk_xid_table_remove (priv->xwindow);
g_dataset_destroy (priv);
g_free (priv);
#endif
}
static void
impl_Bonobo_Canvas_Component_draw (PortableServer_Servant servant,
const Bonobo_Canvas_State *state,
const CORBA_char *drawable_id,
CORBA_short x,
CORBA_short y,
CORBA_short width,
CORBA_short height,
CORBA_Environment *ev)
{
Gcc *gcc = GCC (bonobo_object_from_servant (servant));
GnomeCanvasItem *item = GNOME_CANVAS_ITEM (gcc->priv->item);
GdkPixmap *pix;
gdk_flush ();
pix = gdk_pixmap_foreign_new_for_display
(gtk_widget_get_display (GTK_WIDGET (item->canvas)),
bonobo_control_x11_from_window_id (drawable_id));
if (pix == NULL){
g_warning ("Invalid window id passed='%s'", drawable_id);
return;
}
restore_state (item, state);
ICLASS (item)->draw (item, pix, x, y, width, height);
my_gdk_pixmap_foreign_release (pix);
gdk_flush ();
}
static void
impl_Bonobo_Canvas_Component_render (PortableServer_Servant servant,
Bonobo_Canvas_Buf *buf,
CORBA_Environment *ev)
{
Gcc *gcc = GCC (bonobo_object_from_servant (servant));
GnomeCanvasItem *item = GNOME_CANVAS_ITEM (gcc->priv->item);
GnomeCanvasBuf canvas_buf;
if (!(buf->flags & Bonobo_Canvas_IS_BUF)) {
buf->rgb_buf._length = buf->row_stride * (buf->rect.y1 - buf->rect.y0);
buf->rgb_buf._maximum = buf->rgb_buf._length;
buf->rgb_buf._buffer = CORBA_sequence_CORBA_octet_allocbuf (
buf->rgb_buf._length);
CORBA_sequence_set_release (&buf->rgb_buf, TRUE);
if (buf->rgb_buf._buffer == NULL) {
CORBA_exception_set_system (
ev, ex_CORBA_NO_MEMORY, CORBA_COMPLETED_NO);
return;
}
}
canvas_buf.buf = buf->rgb_buf._buffer;
canvas_buf.buf_rowstride = buf->row_stride;
canvas_buf.rect.x0 = buf->rect.x0;
canvas_buf.rect.x1 = buf->rect.x1;
canvas_buf.rect.y0 = buf->rect.y0;
canvas_buf.rect.y1 = buf->rect.y1;
canvas_buf.bg_color = buf->bg_color;
if (buf->flags & Bonobo_Canvas_IS_BG)
canvas_buf.is_bg = 1;
else
canvas_buf.is_bg = 0;
if (buf->flags & Bonobo_Canvas_IS_BUF)
canvas_buf.is_buf = 1;
else
canvas_buf.is_buf = 0;
ICLASS (item)->render (item, &canvas_buf);
/* return */
buf->flags =
(canvas_buf.is_bg ? Bonobo_Canvas_IS_BG : 0) |
(canvas_buf.is_buf ? Bonobo_Canvas_IS_BUF : 0);
}
static CORBA_boolean
impl_Bonobo_Canvas_Component_contains (PortableServer_Servant servant,
CORBA_double x,
CORBA_double y,
CORBA_Environment *ev)
{
Gcc *gcc = GCC (bonobo_object_from_servant (servant));
GnomeCanvasItem *item = GNOME_CANVAS_ITEM (gcc->priv->item);
GnomeCanvasItem *new_item;
int cx, cy;
CORBA_boolean ret;
gnome_canvas_w2c (item->canvas, x, y, &cx, &cy);
if (getenv ("CC_DEBUG"))
printf ("Point %g %g: ", x, y);
ret = ICLASS (item)->point (item, x, y, cx, cy, &new_item) == 0.0 &&
new_item != NULL;
if (getenv ("CC_DEBUG"))
printf ("=> %s\n", ret ? "yes" : "no");
return ret;
}
static void
impl_Bonobo_Canvas_Component_bounds (PortableServer_Servant servant,
const Bonobo_Canvas_State *state,
CORBA_double *x1,
CORBA_double *x2,
CORBA_double *y1,
CORBA_double *y2,
CORBA_Environment *ev)
{
Gcc *gcc = GCC (bonobo_object_from_servant (servant));
GnomeCanvasItem *item = GNOME_CANVAS_ITEM (gcc->priv->item);
restore_state (item, state);
ICLASS (item)->bounds (item, x1, y1, x2, y2);
}
/*
* Converts the event marshalled from the container into a GdkEvent
*/
static void
Bonobo_Gdk_Event_to_GdkEvent (const Bonobo_Gdk_Event *gnome_event, GdkEvent *gdk_event)
{
switch (gnome_event->_d){
case Bonobo_Gdk_FOCUS:
gdk_event->type = GDK_FOCUS_CHANGE;
gdk_event->focus_change.in = gnome_event->_u.focus.inside;
return;
case Bonobo_Gdk_KEY:
if (gnome_event->_u.key.type == Bonobo_Gdk_KEY_PRESS)
gdk_event->type = GDK_KEY_PRESS;
else
gdk_event->type = GDK_KEY_RELEASE;
gdk_event->key.time = gnome_event->_u.key.time;
gdk_event->key.state = gnome_event->_u.key.state;
gdk_event->key.keyval = gnome_event->_u.key.keyval;
gdk_event->key.length = gnome_event->_u.key.length;
gdk_event->key.string = g_strdup (gnome_event->_u.key.str);
return;
case Bonobo_Gdk_MOTION:
gdk_event->type = GDK_MOTION_NOTIFY;
gdk_event->motion.time = gnome_event->_u.motion.time;
gdk_event->motion.x = gnome_event->_u.motion.x;
gdk_event->motion.y = gnome_event->_u.motion.y;
gdk_event->motion.x_root = gnome_event->_u.motion.x_root;
gdk_event->motion.y_root = gnome_event->_u.motion.y_root;
#ifdef FIXME
gdk_event->motion.xtilt = gnome_event->_u.motion.xtilt;
gdk_event->motion.ytilt = gnome_event->_u.motion.ytilt;
#endif
gdk_event->motion.state = gnome_event->_u.motion.state;
gdk_event->motion.is_hint = gnome_event->_u.motion.is_hint;
return;
case Bonobo_Gdk_BUTTON:
switch (gnome_event->_u.button.type){
case Bonobo_Gdk_BUTTON_PRESS:
gdk_event->type = GDK_BUTTON_PRESS;
break;
case Bonobo_Gdk_BUTTON_RELEASE:
gdk_event->type = GDK_BUTTON_RELEASE;
break;
case Bonobo_Gdk_BUTTON_2_PRESS:
gdk_event->type = GDK_2BUTTON_PRESS;
break;
case Bonobo_Gdk_BUTTON_3_PRESS:
gdk_event->type = GDK_3BUTTON_PRESS;
break;
}
gdk_event->button.time = gnome_event->_u.button.time;
gdk_event->button.x = gnome_event->_u.button.x;
gdk_event->button.y = gnome_event->_u.button.y;
gdk_event->button.x_root = gnome_event->_u.button.x_root;
gdk_event->button.y_root = gnome_event->_u.button.y_root;
gdk_event->button.button = gnome_event->_u.button.button;
return;
case Bonobo_Gdk_CROSSING:
if (gnome_event->_u.crossing.type == Bonobo_Gdk_ENTER)
gdk_event->type = GDK_ENTER_NOTIFY;
else
gdk_event->type = GDK_LEAVE_NOTIFY;
gdk_event->crossing.time = gnome_event->_u.crossing.time;
gdk_event->crossing.x = gnome_event->_u.crossing.x;
gdk_event->crossing.y = gnome_event->_u.crossing.y;
gdk_event->crossing.x_root = gnome_event->_u.crossing.x_root;
gdk_event->crossing.y_root = gnome_event->_u.crossing.y_root;
gdk_event->crossing.state = gnome_event->_u.crossing.state;
switch (gnome_event->_u.crossing.mode){
case Bonobo_Gdk_NORMAL:
gdk_event->crossing.mode = GDK_CROSSING_NORMAL;
break;
case Bonobo_Gdk_GRAB:
gdk_event->crossing.mode = GDK_CROSSING_GRAB;
break;
case Bonobo_Gdk_UNGRAB:
gdk_event->crossing.mode = GDK_CROSSING_UNGRAB;
break;
}
return;
}
g_assert_not_reached ();
}
/**
* handle_event:
* @canvas: the pseudo-canvas that this component is part of.
* @ev: The GdkEvent event as set up for the component.
*
* Returns: True if a canvas item handles the event and returns true.
*
* Passes the remote item's event back into the local pseudo-canvas so that
* canvas items can see events the normal way as if they were not using bonobo.
*/
static gboolean
handle_event(GtkWidget *canvas, GdkEvent *ev)
{
GtkWidgetClass *klass = GTK_WIDGET_GET_CLASS(canvas);
gboolean retval = FALSE;
switch (ev->type) {
case GDK_ENTER_NOTIFY:
gnome_canvas_world_to_window(GNOME_CANVAS(canvas),
ev->crossing.x, ev->crossing.y,
&ev->crossing.x, &ev->crossing.y);
retval = (klass->enter_notify_event)(canvas,
&ev->crossing);
break;
case GDK_LEAVE_NOTIFY:
gnome_canvas_world_to_window(GNOME_CANVAS(canvas),
ev->crossing.x, ev->crossing.y,
&ev->crossing.x, &ev->crossing.y);
retval = (klass->leave_notify_event)(canvas,
&ev->crossing);
break;
case GDK_MOTION_NOTIFY:
gnome_canvas_world_to_window(GNOME_CANVAS(canvas),
ev->motion.x, ev->motion.y,
&ev->motion.x, &ev->motion.y);
retval = (klass->motion_notify_event)(canvas,
&ev->motion);
break;
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
gnome_canvas_world_to_window(GNOME_CANVAS(canvas),
ev->button.x, ev->button.y,
&ev->button.x, &ev->button.y);
retval = (klass->button_press_event)(canvas,
&ev->button);
break;
case GDK_BUTTON_RELEASE:
gnome_canvas_world_to_window(GNOME_CANVAS(canvas),
ev->button.x, ev->button.y,
&ev->button.x, &ev->button.y);
retval = (klass->button_release_event)(canvas,
&ev->button);
break;
case GDK_KEY_PRESS:
retval = (klass->key_press_event)(canvas, &ev->key);
break;
case GDK_KEY_RELEASE:
retval = (klass->key_release_event)(canvas, &ev->key);
break;
case GDK_FOCUS_CHANGE:
if (&ev->focus_change.in)
retval = (klass->focus_in_event)(canvas,