Skip to main content

Introduction to Bonobo: Basic Bonobo use

Getting started on the client side

Michael Meeks (michael@ximian.com), Component software engineer, Ximian Inc.
Michael is a Christian and enthusiastic believer in Free software. He very much enjoys working for Ximian Inc. where he gets paid to develop various free Gnome components, particularly ORBit2, Bonobo and Gnome 2 infrastructure. Prior to this he worked for Quantel gaining expertise in real time AV editing and playback achieved with high performance focused hardware/software solutions. You can reach him at michael@ximian.com.

Summary:  In the first article, we looked at an overview of Bonobo -- why it is necessary, and what it aims to achieve. In this article, we examine how you can set about using Bonobo components in your software.

Date:  01 Sep 2001
Level:  Intermediate
Activity:  582 views

In this article, the term "Bonobo" is used to specify both the Bonobo infrastructure, and -- often the underlying CORBA transport. This is to reduce confusion where making a distinction between the two is not helpful.

Similarly, I do not refer to the various language bindings for Bonobo (particularly good are Perl and Python -- see Resources), but rather stick with the native C bindings.

Aggregation explained

The first thing to understand about the way Bonobo interfaces behave is that it is not exactly like the way an Object Oriented (OO) language behaves with a class. Whilst an OO language's class type data, (by type data we mean the abstract information describing the class or interface) contains information about the class's methods. It also (typically) contains data and scope type information. What this means is that in an OO language there is often a protected-private-public distinction for methods. In addition, there is often data layout information -- that is, a "Point" object might have a data member of name "foo" of type "double." This implementation detail can thus be confused with the interface that the object exports.

In CORBA, the only things that are exposed to the remote world are methods (even attributes are implemented as get/set methods). Thus an interface is precisely that -- a pure method-based WYSIWYG interface. Hence whilst a Bonobo interface can inherit from another Bonobo interface, there is no reason the implementation needs to.

Secondly, Bonobo does not use multiple inheritance (MI). Instead, it uses single inheritance in combination with interface aggregation in the same way as COM, UNO and XPCOM.

Most interfaces inherit solely from the Bonobo::Unknown interface, which provides a nexus for retrieving (and querying) other interfaces. Remember that an interface contains no data: It simply provides a way to talk to the actual data on the object that lives behind it. So it should be reasonable that several different interfaces could be used to access that data. Thus a Control, for example, might have a property-access interface and a visual-embedding interface.

To get an interface from an object reference, one would use the queryInterface (QI) method in Interface Definition Language (IDL):


The queryInterface method


module Bonobo {
        interface Unknown {
                ...
                Unknown queryInterface (Unknown obj, in string repoid);
        };
};


Whilst this is an important concept, in many cases the bindings will return interfaces of the type you expect, thus it is frequently not neccessary to use QI.


Some sample client code

Bonobo splits into several parts. In the client section, the parts that we are interested in are the client sugarwrappers, and using the CORBA interfaces directly ourselves.

Using CORBA interfaces from C

The above IDL queryInterface method, when compiled to a C stub, would have a signature thus:


The queryInterface method compiled



Bonobo_Unknown
        Bonobo_Unknown_queryInterface (
               Bonobo_Unknown     obj,
               const CORBA_char  *repoid,
               CORBA_Environment *ev);


and be used thus:


The queryInterface method in use


        CORBA_Environment ev;
        CORBA_exception_init (&ev)
        control = Bonobo_Unknown_queryInterface (
                object, "IDL:Bonobo/Control:1.0", &ev);


This looks intuitive enough: We pass a C string, and we get an Unknown back. But there are several things worth noticing:

  • The namespace and interface name are mangled into the method name using underscores, ie. since the interface is in the Bonobo:: namespace it has a Bonobo_ prefix, and since the method is in the Unknown interface, there is then the Unknown. This distinction is an important visual indication as to whether a method is a CORBA method (it will have a mixed case signature like Bonobo_Control_activate) as opposed to a wrapper function ( which will be all lower case: bonobo_control_new).
  • Remote handles have an associated interface, for instance, Bonobo_Unknown denotes a CORBA pointer to a remote object implementing the Unknown interface.
  • Each time a CORBA method is invoked it is possible that an exception will be flagged in 'ev'. This can be easily acertained by using the if (BONOBO_EX (&ev)) idiom, the environment needs to be initialized before the CORBA call by the CORBA_exception_init.

Bonobo wrappers

To ease the hassles of using the CORBA methods in a fine-grained fashion and being concerned about exception environments, there are client wrappers that make use of Bonobo -- and its integration with existing Gtk+ programs -- far easier.

Bonobo provides a Control interface that allows the construction of rich embedded GUI widgets. It also provides a PropertyBag interface to allow any properties to be set on the widget, and often an EventSource interface to allow connection to any signals the control may emit. Thus Bonobo provides capabilities similar to those of an ActiveX control or a JavaBean.

Thus to create a sample calculator widget and insert it into (perhaps an educational application to teach math) you might do:


A calculator widget


        GtkWidget *control;
        control = bonobo_widget_new_control (
                "OAFIID:Bonobo_Sample_Calculator", NULL);
        gtk_widget_show (control);


This widget would then behave just as any other widget would, and would allow insertion into any Gtk container in the normal way.

To set the displayed value of the calculator widget, we might want to set the "value" property. There are several ways to do this, first the simple, but non-type-safe fashion:


Setting the "value" property


        CORBA_double i;
        bonobo_widget_get_property (control, "value", &i, NULL);
        i+= 0.37;
        bonobo_widget_set_property (control, "value", i, NULL);


In this example, we grab the value, add 0.37 to it and set it back again. The more type-safe -- but longer -- method is perhaps more instructive, after obtaining the BonoboControlFrame from the widget with bonobo_widget_get_control_frame we would do:


Setting the "value" property correctly


        Bonobo_PropertyBag pb;
        CORBA_Environment ev;

        /* allocate the exception environment */
        CORBA_exception_init (&ev);

        pb = bonobo_control_frame_get_control_property_bag (
                control_frame, &ev);

        /* check for an exception */
        if (BONOBO_EX (&ev))
                g_warning ("Error getting property bag from control");

        else {

             /* use the property bag client helper function to set a
        double value, this constructs an 'Any' from the double, and
        calls Bonobo_Property_setValue on the remote property bag */
        bonobo_property_bag_client_set_value_gdouble (
                        pb, "value", 0.12345, NULL);


        /* release the Bonobo reference to the property bag
           that was retrieved from the control_frame */
        bonobo_object_release_unref (pb, NULL);

        /* free the exception environment */
        CORBA_exception_free (&ev);




Raw CORBA

The property and control interfaces are slightly unusual in that one interfaces with the Gtk+ widget system in an intimate way, and the other manipulates CORBA_anys which are slightly ugly in C. Thus, having the bonobo_widget_ and bonobo_property_bag_client wrappers makes life far easier for the C programmer.

Most new interfaces, however, will be used directly. For example, to write data to a stream, one would need to use the method (IDL):


A CORBA interface to write data to a stream


        void write (in iobuf buffer)
                    raises (NoPermission, IOError);

        which would be used thus:

        CORBA_Environment   ev;
        Bonobo_Stream_iobuf buf;

        buf._buffer = <a pointer to my byte data>;
        buf._length = <length of the data in bytes>;

        CORBA_exception_init (&ev);
        Bonobo_Stream_write (stream, &buf, &ev);
        if (BONOBO_EX (&ev))
                g_warning ("Fatal stream write error '%s'",
                           bonobo_exception_get_text (&ev));
        CORBA_exception_free (&ev);


Thus the CORBA interface can be used directly, although there is also a bonobo_stream_client_write helper method too. Notice here also the bonobo_exception_get_text method is used to return a translated, user-readable description of the exception that occurred (although this is leaked above for clarity).

Reference counting

The last important thing to take care of in client code is reference counting. Bonobo's lifecycle management is that of reference counting. This means that if you want to ensure that an object is not going to die a sudden, shocking death, you keep a reference to it. The object keeps track of how many references have been taken to it and, when this count reaches zero, it destroys the object.

Thus to ensure that objects are correctly terminated when they are no longer needed, and to ensure that they are not terminated prematurely, it is important to reference count correctly.

Client code should always use the bonobo_object_dup_ref and bonobo_object_release_unref methods. These tolerate NIL references silently, and allow exceptions to be ignored.

By convention, when Bonobo methods return a reference, you need to release the reference when you are finished with it (as above).

Monikers

Finally, the bonobo_widget sugar wrapper also wraps the moniker infrastructure, this means that a much richer object namespace is available to the programmer. The following example:


Sugar wrapping an image reference


          control = bonobo_widget_new_control (
                  "file:/demo/a.png", NULL);


will produce a control (perhaps implemented by Eye Of Gnome) that will render the image.


Conclusions

Below are some key points to take from this second installment of a three-part introduction to Bonobo:

  • It is easy to use Bonobo components.
  • Use method capitalization to distinguish between direct CORBA invocations, and local helper wrappers.
  • Treat references carefully.
  • See the samples in bonobo/samples/controls/ for some working example code.
  • Monikers provide a powerful, abstract object namespace, see bonobo/doc/Monikers for more information.

In the next article, we'll discuss the process of creating your own component and exposing it to the world.


Resources

About the author

Michael is a Christian and enthusiastic believer in Free software. He very much enjoys working for Ximian Inc. where he gets paid to develop various free Gnome components, particularly ORBit2, Bonobo and Gnome 2 infrastructure. Prior to this he worked for Quantel gaining expertise in real time AV editing and playback achieved with high performance focused hardware/software solutions. You can reach him at michael@ximian.com.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and Web services
ArticleID=11601
ArticleTitle=Introduction to Bonobo: Basic Bonobo use
publish-date=09012001
author1-email=michael@ximian.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers