Fixes for Deprecated Canvas Components

bob gibbs <[email protected]>
Newsgroups gmane.comp.gnome.components
Message-ID <1050457308.1568.10.camel@gibbsrc>
Hello,

	I know canvas component is deprecated, but it looked too good not to
try out. After many hours of debugging, I have it working perfectly, at
least with respect to its original design.

	I don't know if there are any plans to keep canvas components in
BonoboUI, but just in case, I thought I'd offer the changes to the 
group. I think using the canvas SVP as a separation layer from a canvas
application has lots of potential.

	At the moment, I don't have CVS commit access, but if you want me to
submit the changes I can get set up.

	I also have a very simple demo program that shows two-way events
between multiple canvas's and components at greater than 100hz.

Thanks
Bob Gibbs.
                
===========================================================================
                
ChangeLog:

bonobo-canvas-component.h

(bonobo_canvas_component_factory_new): new.  Convenience to replace old
embeddable way of creating the canvas component.

bonobo-canvas-component.c

(impl_Bonobo_Canvas_Component_update): Call rih group update here vice
inside
rih_update. Solves for case where a request_update is made while the
component
is waiting for a response.

(handle_event): new. 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.

(rih_update): Moved rih group update into
impl_Bonobo_Canvas_Component_update.
This solves for case where a request_update is made while the component
is
waiting for a response to its last
Bonobo_Canvas_ComponentProxy_requestUpdate.

(impl_Bonobo_Canvas_Component_event): Added mutex lock check to prevent
event
from being passed if any pseudo-canvas is in its idle loop. This is not
thread
related.  See code for further info.

(bonobo_canvas_new): Put into container to prevent gtk warning.

(bonobo_canvas_component_factory_new): new.  Convenience to replace old
embeddable way of creating the canvas component.

bonobo-canvas-item.c

(gbi_set_property): Moved proxy_size_allocate call from gbi_set_property
to
gbi_realize. Sometimes widget is not realized when user sets the
properties.

(gdk_event_to_bonobo_event): fixed x/y mixup for motion notify

(bonobo_canvas_item_class_init): Make sure set_property callback is
initialized
before calling install_property.

(impl_Bonobo_Canvas_ComponentProxy_requestUpdate): Added idle_handler
that
gets invoked whenever the canvas has already had one added.  This solves
for
case of NEED_UPDATE flags getting out of sync and canvas refusing to
update the
remote item forever thereafter. See code for further details.

(create_proxy): Fixed to allocate space for base epv before
initializing.


===========================================================================
--- bonobo-canvas-component.h.orig	Mon Mar 24 09:43:14 2003
+++ bonobo-canvas-component.h	Mon Mar 24 09:43:46 2003
@@ -69,6 +69,16 @@
 GnomeCanvas *bonobo_canvas_new (gboolean                     is_aa,
 				Bonobo_Canvas_ComponentProxy proxy);
 
+
+/* Sets up a callback to be invoked when the container activates the
object.
+ * Creating the component factory will do nothing until the container
connects.
+ */
+typedef BonoboCanvasComponent *(*GnomeItemCreator) 
+   (GnomeCanvas *canvas, void *user_data);
+
+BonoboObject *bonobo_canvas_component_factory_new(GnomeItemCreator
item_factory,
+      void *data);
+
 G_END_DECLS
 
 #endif /* BONOBO_UI_DISABLE_DEPRECATED */


===========================================================================
--- bonobo-canvas-component.c.orig	Mon Mar 24 09:42:46 2003
+++ bonobo-canvas-component.c	Mon Apr 14 21:42:11 2003
@@ -16,10 +16,13 @@
 #include <libgnomecanvas/gnome-canvas.h>
 #include <gdk/gdkx.h>
 #include <gdk/gdkprivate.h>
+#include <gtk/gtk.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,
@@ -171,9 +174,11 @@
 			    | 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,
@@ -194,6 +199,8 @@
 	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];
@@ -221,7 +228,7 @@
 			}
 		}
 	}
-	
+
 	invoke_update (item, (double *)aff, svp, flags);
 
 	if (svp){
@@ -247,6 +254,10 @@
 	/*
 	 * 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;
@@ -540,6 +551,82 @@
 		g_free (event->key.string);
 }
 
+/**
+ * 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 = 0;
+
+        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,
+                                        &ev->focus_change);
+                        else
+                                retval =
(klass->focus_out_event)(canvas,
+                                        &ev->focus_change);
+                        break;
+                default:
+                        g_warning("Canvas event not handled %d",
ev->type);
+                        break;
+
+        }
+        return retval;
+}
+
 /*
  * Receives events from the container end, decodes it into a synthetic
  * GdkEvent and forwards this to the CanvasItem
@@ -553,19 +640,56 @@
 	Gcc *gcc = GCC (bonobo_object_from_servant (servant));
 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (gcc->priv->item);
 	GdkEvent gdk_event;
-	int retval;
-
-	Bonobo_Gdk_Event_to_GdkEvent (gnome_event, &gdk_event);
+	int retval = 0;
 
 	restore_state (item, state);
+        
+        gdk_event.any.window = item->canvas->layout.bin_window;
 
-	g_signal_emit_by_name (gcc, "event", &gdk_event);
-
-	if (ICLASS (item)->event)
-		retval = ICLASS (item)->event (item, &gdk_event);
-	else
-		retval = FALSE;
+	Bonobo_Gdk_Event_to_GdkEvent (gnome_event, &gdk_event);
 
+        /*
+         * Problem:
+         * When dealing with multiple canvas component's within the
same process
+         * it is possible to get to this point when one of the
pseudo-canvas's
+         * is inside its idle loop.  This is normally not a problem
unless an
+         * event from one component can trigger a request for an update
on
+         * another component.
+         *
+         * In this way programs that use bonobo/orbit proxies behave
like
+         * multithreaded programs even if they are not multi-threaded
+         * programs.
+         *
+         * Solution:
+         * The only way I can think of at the moment to remedy this
situation is
+         * to not allow a item event to occur if any of the
pseudo-canvas's are
+         * inside their idle loop. i.e., Throw away the event. Because
this is a
+         * rare case, at least I think it is, I have added this mutex
check.
+         * For this to work, the main program must call g_thread_init()
and
+         * gdk_threads_init() whether it is using threads or not.
+         *
+         * Main programs that do not have this multiple component
effect
+         * described above do not need to initialize for threads and
everything
+         * will work fine.
+         *
+         * A better solution may be to implement a special mutex lock
inside the
+         * gnome canvas with a public interface specifically for
supporting
+         * Bonobo. A secondary problem resulting from this logic is
that it is
+         * possible for an even to get dropped.  This is no big deal
for motion
+         * events but could be for focus events.
+         */
+        if (gdk_threads_mutex) {
+               if (g_mutex_trylock(gdk_threads_mutex)) {
+
+                        retval = handle_event(GTK_WIDGET(item->canvas),
+                                        &gdk_event);
+                        g_mutex_unlock(gdk_threads_mutex);
+
+               }
+        }
+        else 
+                retval = handle_event(GTK_WIDGET(item->canvas),
&gdk_event);
+                
 	free_event (&gdk_event);
 
 	return retval;
@@ -809,23 +933,10 @@
 {
 	RootItemHack *rih = (RootItemHack *) item;
 	CORBA_Environment ev;
-	GnomeCanvasItemClass *gci_class = gtk_type_class (
-					gnome_canvas_item_get_type ());
 
 	CORBA_exception_init (&ev);
 	Bonobo_Canvas_ComponentProxy_requestUpdate (rih->proxy, &ev);
 	CORBA_exception_free (&ev);
-
-	/*
-	 * Mark our canvas and item as fully updated
-	 */
-
-	(* gci_class->update) (item, 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;
 }
 
 static void
@@ -898,8 +1009,12 @@
 
 	canvas->root = GNOME_CANVAS_ITEM (root_item_hack_new (canvas, proxy));
 
+        /* A hack to prevent gtkwidget from issuing a warning about
there not
+           being a parent class. */
+        gtk_container_add(GTK_CONTAINER
(gtk_window_new(GTK_WINDOW_TOPLEVEL)),
+                            GTK_WIDGET(canvas));
 	gtk_widget_realize (GTK_WIDGET (canvas));
-	
+
 	/* Gross */
 	GTK_WIDGET_SET_FLAGS (canvas, GTK_VISIBLE | GTK_MAPPED);
 
@@ -990,3 +1105,103 @@
 	return corba_uic;
 }
 
+/* BonoboCanvasComponentFactory is used to replace the old
BonoboEmbeddable
+ * object.  It sets up a canvas component factory to conform with the
current
+ * Bonobo IDL.
+ */
+
+#define BONOBO_CANVAS_COMPONENT_FACTORY_TYPE       \
+   (bonobo_canvas_component_factory_get_type())
+
+#define BONOBO_CANVAS_COMPONENT_FACTORY(o)    \
+   (G_TYPE_CHECK_INSTANCE_CAST ((o),\
+   BONOBO_CANVAS_COMPONENT_FACTORY_TYPE, BonoboCanvasComponentFactory))
+
+typedef struct _BonoboCanvasComponentFactoryPrivate \
+   BonoboCanvasComponentFactoryPrivate;
+
+typedef struct {
+        BonoboObject base;
+        BonoboCanvasComponentFactoryPrivate *priv;
+} BonoboCanvasComponentFactory;
+
+typedef struct {
+        BonoboObjectClass parent_class;
+
+        POA_Bonobo_CanvasComponentFactory__epv epv;
+} BonoboCanvasComponentFactoryClass;
+       
+GType bonobo_canvas_component_factory_get_type(void) G_GNUC_CONST;
+
+struct _BonoboCanvasComponentFactoryPrivate {
+   GnomeItemCreator item_creator;
+   void *item_creator_data;
+};
+
+static GObjectClass *gccf_parent_class;
+
+static Bonobo_Canvas_Component
+impl_Bonobo_canvas_component_factory_createCanvasItem (
+   PortableServer_Servant servant, CORBA_boolean aa,
+   const Bonobo_Canvas_ComponentProxy _item_proxy,
+   CORBA_Environment *ev)
+{
+        BonoboCanvasComponentFactory *factory =
BONOBO_CANVAS_COMPONENT_FACTORY(
+              bonobo_object_from_servant (servant));
+        Bonobo_Canvas_ComponentProxy item_proxy;
+        BonoboCanvasComponent *component;
+        GnomeCanvas *pseudo_canvas;
+
+        if (factory->priv->item_creator == NULL)
+                return CORBA_OBJECT_NIL;
+
+        item_proxy = CORBA_Object_duplicate (_item_proxy, ev);
+
+	pseudo_canvas = bonobo_canvas_new (aa, item_proxy);
+
+        component = (*factory->priv->item_creator)(
+                /*factory,*/ pseudo_canvas,
factory->priv->item_creator_data);
+
+        return bonobo_object_dup_ref (BONOBO_OBJREF (component), ev);
+}
+
+static void
+bonobo_canvas_component_factory_class_init (
+      BonoboCanvasComponentFactoryClass *klass)
+{
+        POA_Bonobo_CanvasComponentFactory__epv *epv = &klass->epv;
+
+        gccf_parent_class = g_type_class_peek_parent(klass);
+        epv->createCanvasComponent = 
+           impl_Bonobo_canvas_component_factory_createCanvasItem;
+}
+
+static void
+bonobo_canvas_component_factory_init (BonoboCanvasComponentFactory
*factory)
+{
+        factory->priv = g_new0 (BonoboCanvasComponentFactoryPrivate,
1);
+}
+
+BONOBO_TYPE_FUNC_FULL (BonoboCanvasComponentFactory,
+                       Bonobo_CanvasComponentFactory,
+                       BONOBO_TYPE_X_OBJECT,
+                       bonobo_canvas_component_factory);
+
+/**
+ * bonobo_canvas_component_factory_new:
+ * @item_factory: A callback invoke when the container activates the
object.
+ * @user_data: User data pointer.
+ *
+ * Returns: The object to be passed into bonobo_generic_factory_main.
+ */
+BonoboObject *bonobo_canvas_component_factory_new(
+      GnomeItemCreator item_factory, void *user_data)
+{
+        BonoboCanvasComponentFactory *factory;
+        factory = g_object_new (BONOBO_CANVAS_COMPONENT_FACTORY_TYPE,
NULL);
+
+        factory->priv->item_creator = item_factory;
+        factory->priv->item_creator_data = user_data;
+
+        return BONOBO_OBJECT(factory); 
+}


===========================================================================
--- bonobo-canvas-item.c.orig	Mon Mar 24 09:43:01 2003
+++ bonobo-canvas-item.c	Mon Apr 14 20:59:54 2003
@@ -192,6 +192,7 @@
 
 	CORBA_exception_init (&ev);
 	prepare_state (item, &state);
+
 	cuta = Bonobo_Canvas_Component_update (
 		gbi->priv->object,
 		&state, affine, clip_path, item_flags,
@@ -200,6 +201,7 @@
 
 	if (!BONOBO_EX (&ev)){
 		if (cuta->width > 0 && cuta->height > 0){
+
 			ArtUta *uta;
 
 			uta = uta_from_cuta (cuta);
@@ -250,6 +252,11 @@
 		gbi->priv->realize_pending = 1;
 		return;
 	}
+
+        proxy_size_allocate (
+                item->canvas,
+                &(GTK_WIDGET (item->canvas)->allocation),
+                BONOBO_CANVAS_ITEM(item));
 		
 	g_signal_connect (item->canvas, "size_allocate",
 			  G_CALLBACK (proxy_size_allocate), item);
@@ -447,7 +454,7 @@
 		e->_d = Bonobo_Gdk_MOTION;
 		e->_u.motion.time = event->motion.time;
 		e->_u.motion.x = event->motion.x;
-		e->_u.motion.y = event->motion.x;
+		e->_u.motion.y = event->motion.y;
 		e->_u.motion.x_root = event->motion.x_root;
 		e->_u.motion.y_root = event->motion.y_root;
 #ifdef FIXME
@@ -556,7 +563,11 @@
 
 		gbi->priv->object = bonobo_object_release_unref (gbi->priv->object,
&ev);
 
-		factory = bonobo_value_get_corba_object (value);
+                /* FIXME: I can't make it work as corba_object. 
+                   Using bonobo_unknown for now. */
+		/* factory = bonobo_value_get_corba_object (value); */
+               
+		factory = bonobo_value_get_unknown (value);
 
 		g_return_if_fail (factory != CORBA_OBJECT_NIL);
 
@@ -580,12 +591,6 @@
 			return;
 		}
 
-		/* Initial size notification */
-		proxy_size_allocate (
-			GNOME_CANVAS_ITEM (gbi)->canvas,
-			&(GTK_WIDGET (GNOME_CANVAS_ITEM (gbi)->canvas)->allocation),
-			gbi);
-	
 		if (gbi->priv->realize_pending){
 			gbi->priv->realize_pending = 0;
 			gbi_realize (GNOME_CANVAS_ITEM (gbi));
@@ -639,6 +644,8 @@
 	GnomeCanvasItemClass *item_class =
 		(GnomeCanvasItemClass *) object_class;
 
+	gobject_class->set_property = gbi_set_property;
+
 	g_object_class_install_property (
 		gobject_class,
 		PROP_CORBA_FACTORY,
@@ -660,7 +667,6 @@
 			G_PARAM_WRITABLE));
 	
 	gobject_class->finalize     = gbi_finalize;
-	gobject_class->set_property = gbi_set_property;
 
 	item_class->update     = gbi_update;
 	item_class->realize    = gbi_realize;
@@ -672,13 +678,42 @@
 	item_class->event      = gbi_event;
 }
 
+static gboolean
+gbi_idle_handler (GnomeCanvasItem *item)
+{
+	gnome_canvas_item_request_update (item);
+        return FALSE;
+}
+
 static void
 impl_Bonobo_Canvas_ComponentProxy_requestUpdate (PortableServer_Servant
servant,
 					         CORBA_Environment     *ev)
 {
 	ComponentProxyServant *item_proxy = (ComponentProxyServant *) servant;
 
-	gnome_canvas_item_request_update (item_proxy->item_bound);
+        if (item_proxy->item_bound->canvas->idle_id == 0)
+        {
+        	gnome_canvas_item_request_update (item_proxy->item_bound);
+        }
+        else
+        {
+                /* Problem: It is possible to get here at a time when
the canvas
+                 * is in the middle of a do_update due to the manner in
which
+                 * proxies are handled by Bonobo/Orbit. The do_update
can
+                 * request a redraw from the component.  When the
Component
+                 * replies a requestUpdate proxy may be included in the
queue.
+                 *
+                 * Solution:
+                 * The canvas is either in its do_update routine or it
is
+                 * waiting to do an update.  If it is waiting this
idle_handler
+                 * will get called just before the canvas's handler. 
If it is
+                 * in the middle of a do_update, then this will queue
for the
+                 * next one.
+                 */
+
+                 g_idle_add_full(GDK_PRIORITY_REDRAW-4,
gbi_idle_handler, 
+                        item_proxy->item_bound, NULL);
+        }
 
 }
 					    
@@ -719,10 +754,12 @@
 	return bonobo_object_dup_ref (item_proxy->ui_container, NULL);
 }
 
+static PortableServer_ServantBase__epv item_proxy_base_epv;
+
 static POA_Bonobo_Canvas_ComponentProxy__epv item_proxy_epv;
 
 static POA_Bonobo_Canvas_ComponentProxy__vepv item_proxy_vepv = {
-	NULL,
+	&item_proxy_base_epv,
 	&item_proxy_epv
 };
 
@@ -736,6 +773,8 @@
 	ComponentProxyServant *item_proxy = g_new0 (ComponentProxyServant, 1);
 	CORBA_Environment ev;
 	
+        item_proxy->proxy_servant.vepv = &item_proxy_vepv;
+
 	CORBA_exception_init (&ev);
 	POA_Bonobo_Canvas_ComponentProxy__init ((PortableServer_Servant)
item_proxy, &ev);
 
@@ -744,7 +783,6 @@
 	item_proxy_epv.ungrabFocus    =
impl_Bonobo_Canvas_ComponentProxy_ungrabFocus;
 	item_proxy_epv.getUIContainer =
impl_Bonobo_Canvas_ComponentProxy_getUIContainer;
 
-	item_proxy->proxy_servant.vepv = &item_proxy_vepv;
 	item_proxy->item_bound = item;
 
 	item_proxy->oid = PortableServer_POA_activate_object (
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.