Bongo in-depth

Bongo is a user interface runtime and builder, which used to be available in the FreeBongo.org open-source project until 2002.

Introduction

Bongo is a tool that allows you to quickly create a user interface using a variety of widgets, which might contain images, audio, and animation. After the user interface is done it can be scripted in Java or easily hooked up to a Java program.
A Bongo user interface is called presentation. A presentation can be saved to a gui file which can be loaded from Java code or again using Bongo.

Widget properties
Each of the user interface elements in Bongo (the widgets) has a list of properties that affect how it ap-pears on screen. Adjusting these properties is as easy as making selections from popup lists, clicking checkboxes, and filling in the one-line textfields.

As an example, the property list for the FolderWidget looks like this:


the main Bongo window with the property list for the FolderWidget

Note that the list is only the table with the two columns "property" and "value". The top area are the more general properties that involves every widget. First of all there is a bounds property with which the bounds of a widget can be set by specifying the absolute pixel positions relative to the parent widget. The other properties in that area are:

  • Name - this is a name with which the selected widget can be retrieved later on from Java code. If I would name this folder widget "myFolder", I could somewhere else get this folder by calling getWidget with the name as parameter: getWidget("myFolder"). Since the method getWidget, which is de-fined in the class Widget, returns an object of the type Widget, it needs to be casted explicitly to FolderWidget to be able to access FolderWidget features.
  • Visible - a widget can be hidden by setting visible to false. This means that it cannot get any input and will not be visible in the user interface.
  • Font - this is the font that can be used by the widget for strings: in the FolderWidget it only affects the content of the pages, since the font for the tabs can be set separately using "tabFont" and "select-Font". A font consists of the name, size and whether it is bold and/or italic.
  • Colors - the first category is "widget", the other one "scrollbar". As can be seen in the FolderWidget example, the "scrollbar" category is disabled. That is because the scrollbar colors are only defined for certain widgets that use scrollbars, like the RichTextBoxWidget, TableWidget and ScrollingContain-erWidget. The "widget" colors are the general widget colors that are defined for all widgets. There are three colors that can be set: the foreground color, hilite color, and background color. A good illustration of what these colors are affecting is the TextBoxWidget: the text is painted in the foreground color, the box is filled using the hilite color and the lines around it are affected by the background color.
  • Global - there are two things that can be set: "Locale" which defines a location and "Encoding" which defines the way native characters are mapped to unicode characters when keyboard input is re-ceived. Both properties will be discussed in the Internationalization chapter later on.

All the properties discussed now are inheritable. One of the great features of Bongo is that a widget can contain any other widget. This means that for example the RichTextBoxWidget can re-use the Scrollbar-Widget for its scrollbars, by just adding a ScrollbarWidget to itself instead of re-doing all that functional-ity. If a property is inheritable, that means that when a widget sets that property, all its children that did not set it yet will inherit that value!
A good example - again - is the FolderWidget: if one sets the foreground color of the FolderWidget to red, all the textboxes inside the pages of the FolderWidget will suddenly paint its text in red, if they did not set the foreground color yet. This is very powerful and means that a developer can save himself a lot of work by carefully choosing which settings need to be overridden and which don't.

PropertyList
Let's get back to the actual property list, the table below the area we just discussed. In the first column the property names are listed. At the right the value of each property can be set. The way this works is that the class Widget implements the interface PropertyObject that looks like this:

public interface PropertyObject {
    /**
     * Get the properties from the object and store
     * them in the property list.
     */
    void getProperties(PropertyList list);

    /**
     * Set the object's properties from the property list.
     */
    void setProperties(PropertyList list);
}

When the Bongo builder wants to get the property list of the widget with its current values it calls get-Properties with a PropertyList object in which the properties are stored. The PropertyList interface looks like this:

public abstract class PropertyList {
    public abstract void setBoolean(String nm, boolean val, boolean def);
    public abstract void setInteger(String nm, int val, int def);
    public abstract void setLong(String nm, long val, long def);
    public abstract void setString(String nm, String val, String def);
    public abstract void setOption(String nm, Options opts, int val, int def);
    public abstract void setColor(String nm, Color val, Color def);
    public abstract void setFont(String nm, Font val, Font def);
    public abstract void setURL(String nm, String val, String def);
    public abstract void setObject(String nm, Object val, Object def);
    public abstract void setByteArray(String nm, byte data[], byte def[]);
    public abstract void setObjectArray(String nm, int len, Object val[]);

    public abstract boolean 	getBoolean(String nm, boolean def);
    public abstract int 		getInteger(String nm, int def);
    public abstract long 	getLong(String nm, long def);
    public abstract String 	getString(String nm, String def);
    public abstract int		getOption(String nm, Options opts, int def);
    public abstract Color 	getColor(String nm, Color def);
    public abstract Font 	getFont(String nm, Font def);
    public abstract String 	getURL(String nm, String def);
    public abstract Object 	getObject(String nm, Object def);
    public abstract byte[]	getByteArray(String nm, byte def[]);
    public abstract Object[] 	getObjectArray(String nm, Object[] def);
}

As can be seen, properties can be set and get in a PropertyList. All properties are accessed using a case-sensitive name. By giving a default value, the property list knows when it doesn't have to save any special information about a property which is more efficient.

The way this translates to the property list in the Bongo builder is that if the builder finds a boolean, it will put the name with a checkbox in a table row, for an integer the name and a textbox that only accepts dig-its, for an option the name and a DropDownListBoxWidget, etc...

The PropertyList class and PropertyObject interface are part of Marimba's persistence model, which can be found in the marimba.persist package. It is a powerful and very efficient way to persistify data and save it to file and restore it.

Containers
As mentioned earlier in this chapter, a widget can contain any number of other widgets. This essentially means that any widget is a container. But Bongo also has a ContainerWidget, which means that the con-tainer can store widgets directly (like any other widget) or in its content, which adds one more level.
This - for example - makes the ScrollingContainerWidget possible: it adds two ScrollbarWidgets to itself directly and reshapes the content so it fills the rest of the space. The content then can contain any other number of widgets, since the content is just another Widget. The scrollbars then scroll the content widget, so the developer does not have to anything for that.


The ScrollingContainerWidget - scrollbars and a content

The top-level container is the Presentation, which means that everything starts and ends with Widgets! Since any widget can contain any other number of widgets, the Presentation can contain widgets. The Presentation does some more special things to make sure it is properly embedded in the Window and things like that.
Getting back to the PropertyList topic: by getting the PropertyList from the presentation, the entire presen-tation can be persistified! How is that possible? Well... widgets also save its children in the PropertyList using the PropertyList.setObjectArray method. So the Presentation saves its children, the children save their children, etc...

Summarized
Bongo has been programmed in an Object Oriented language (Java), which immediately pays off: since everything is OO, objects (in this case Widgets) can be easily re-used and extended. Add the container structure of Bongo and you get a very powerful concept.

When working with Bongo, you begin to appreciate these things more and more over time and find that developing new Widgets and extending the behavior of Widgets is very easy: now you can fully concentrate on the actual problems without having to re-do a lot of things.

The widget set

The Bongo widget set contains a lot of widgets. At the right side the full widget hierarchy can be seen. By following a strict class hierarchy and creating certain categories (like TextWidget and ShapeWid-get) we were able to re-use a lot of functionality by just inheriting it all.

By re-using entire widgets using the container capabilities of Widgets, functionality can be even more re-used. That has nothing to do with this class hierarchy though, but is more a delegation-like type of re-use.

As can be seen in the widget hierarchy, there are many widgets which cannot all be covered here. A few are worth mentioning though...


CommandButtonWidget
A command button could be made not-focusable, which means that in no way the button can get keyboard focus.


two command buttons

Another cool thing about the CommandButtonWidget is that it can be the default button, which means that it can be used for the default action. The default behavior of Bongo is to execute the default button whenever Enter is pressed in any widget, unless that widget intercepts the Enter key.

There is also the ability to make a button the cancel button. By default Bongo executes the cancel button if Esc is pressed in any other widget. The "default" and "cancel" properties are most useful in Dialogs, where users are usually pressing Enter when they are done and pressing Esc when they want to cancel the operation.


DropDownListBoxWidget
This widget makes it possible to dropdown a list with items. It has one mentionable property, which is called "fixedWidth". If true, the menu that drops down will always have the same size as the widget itself, like the one in the next figure. If false, the menu will automatically make sure it is wide enough for the largest string in the list of choices.


    a dropdown listbox with fixedWidth set to true

FolderWidget
Allows showing only one content area at the same time. Interesting is that if the tabs do not fit, they would just 'disappear'. This is nicely demonstrated in the figure below.


Oooops.... the tabs do not fit

This is of course screaming for a solution, and good news: there is a mechanism that figures out how the tabs should be placed. The folder widget has a property called "extendMode" - one of its possible values is "layers". The "layers" extend-Mode does the following:


This is much better.... now the tabs are arranged in layers

As can be seen, the tabs are automatically arranged, there are no additional properties involved which keeps it very simple. The way this works is that during layout, the FolderWidget takes the tab label of each page and calculates its width, after which it calculates the bounds (the x, y, width, and height); the tabs are arranged so they fill each layer to the right side. When the user clicks on a tab that is in a layer that is currently not in front, that layer will become the front layer. A global layerOrder array in the FolderWid-get keeps track of which layer is where currently.

Note that all this is separate from painting: calculating where what goes is done as a layout process. After that the FolderWidget can be repainted, after which the FolderWidget's paint method goes to work; using this approach it only needs the array with the bounds for each tab that were calculated by layout, which simplifies the paint process greatly.

Because more than one layer of tabs takes more space, a second extendMode called "scrolling" is available. In the following figure his extend-mode can be seen: at the right of the tab area there are two scroll buttons that can be clicked, in which case the tab area scrolls to the left or right.


a different approach - scroll the tab area

The Builder Environment

The Bongo Builder is a interface builder that uses the widget set described in the previous chapter. It goes too far to describe every little feature in detail here. That is why I will stick with giving you some insight in the features that were added to the 1.1 release of Bongo. Some of the new features in version 1.1 included:

  • separate scrollbar colors - the colors of the scrollbar can be set separately;
  • browse file system - this is the addition of a button which pops up a file dialog if clicked and with which a resource file can be chosen (like images or sounds);
  • options dialog - with this dialog certain preferences can be set;
  • window menu - this menu lists all the presentation windows that are edited using Bongo right now;

Window menu
The window menu speaks for itself - the result can be seen below.


The Window Menu in action


Scrollbar colors
Previously, the colors of the widget affected the colors of the scrollbars that a widget possibly contained. Good examples of this are the RichTextBoxWidget and ScrollingContainerWidget. Especially in the case of the RichTextBoxWidget this could be very annoying: changing the foreground color, so the text would change color, would also affect the foreground color of its scrollbar.
The obvious solution would be to let those widgets set properties for the scrollbar colors, which would then show up in the property list. But that would mean that the property list would suddenly be three items larger, while the property lists are already pretty large and advanced. This made us decide to look for a different solution: add a second "color palette", like the one that was already there. Below a close-up of this.

 
the colors for a widget without scrollbars (left) and with scrollbars (right)

If a widget is selected that does not support scrollbar colors, that palette is disabled and grayed out. Otherwise they will be disabled. Then the scrollbar colors can be set, in which case they override the widget colors. If they are not set than the widget colors are used like before. All a widget needs to do for this is set three color properties with the names "scrollbarForeground", "scrollbarHilite" and "scrollbarBack-ground". The builder then automatically recognizes whether those properties are there and disables/enables the scrollbar palette.


Browsing the file system
If a widget's property list contains a url, like the path to an image file, it always had to be typed explicitly. There was no such thing as a browser button. This was fixed in the 1.1 release. See the property list of the TreeNodeWidget below this paragraph and notice the "..." buttons next to the image properties! The special thing about it is that when browsing for a file, the process recognizes and converts relative path names.


the property list of the TreeNodeWidget

The way the property list really works in the builder is that each property is represented by a BuilderPropertyItem object. This item stores which type it is (for example "boolean" or "url"). Based on the type it will use a different widget for the value of the property.
The addition of the browse button works like this: if the type of the BuilderPropertyItem is URL, it will first add a TextBoxWidget in which the path/url can be entered and will be displayed, next to it a CommandButtonWidget with the label "..." is added.

Next, if the browse button is pressed, the handleEvent method of the BuilderPropertyItem will intercept that action and popup the FileDialog. It will set the initial directory of the FileDialog to an absolute path by converting a (possible) relative path. When a file is chosen, an algorithm will convert the path to a relative path (relative to the gui file).
There are several problem involved with this, since we're running on more than just one platform!

  • file names are case sensitive on some platforms, but other platforms do not have that (like Win-dows95);
  • the directory delimiter is different on each platform which makes parsing the path harder;
  • constructing a relative path can be impossible if a file on a different 'volume'/'drive' is chosen, on Windows95 for example this could be a file on drive D while the gui file is on drive C;
  • Bongo has something called "base" directories, which are certain directories that can be accessed us-ing a special name for that location. Those names can be used by prepending a '~' character, Bongo for example has a "~builder" name that points to the builder resource directory. When a File from such a directory is chosen, the base name should be used.

A relatively easy looking problem turned out to be a little more complicated. The code for this problem can be found in the downloads section of the JAVA gazette. The solution was tested on several platforms and turned out to be working great. It all comes down to the one method getRelativePath that expects the from and to path and returns the relative path.
This should have been used to create a BrowseButtonWidget that supports relative file names, saving Bongo developers some time when they need it. This did not happen in the 1.1 release unfortunately.


Options dialog
The options dialog is used to set preferences in the builder. Below a screenshot of this necessary addition.


the preferences dialog with which Bongo can be slightly customized

The first two preferences (Builder Locale and Bongo Encoding) have to do with internationalization and will be discussed in that chapter. That leaves only two preferences.
The first one is grid size; this used to be 5 all the time, but since some people like that and some people don't we decided to make it customizable. This involved code changes in the way the grid is painted when a presentation is being edited.
The second on is show tips and speaks for itself: some people find it horribly annoying to see tips all the time. That was why it can now be turned off.

The builder used to access properties like simple class variables in the same class. That turned out to become a mess, which resulted in a major re-architecture of this in Bongo 1.1. The end result can be seen in the next figure.


a rough overview of the Bongo class structure

This class structure means this: the Builder class uses Editor objects for editing widgets and EditorFrame objects in which the gui files can be edited. The EditorFrame contains an EditorPanel that deals with editing events that come up from the Presentation.

The new thing in this picture is that the properties of the builder are now stored in a separate dedicated object BuilderProperties. The BuilderProperties class takes care of loading, saving and accessing the properties of the builder. It also contains methods to popup the Options dialog, process the current settings in that dialog and to hide the options dialog.

Separating this out greatly simplified the Builder code.

Event model engine

Bongo has its own event model engine. This is necessary because:

  • Bongo needs to distribute events from the AWT to the correct widget by itself
  • Bongo uses a different event model (JDK1.0.2 event model), so it has to to translate the events itself

When the JDK started to ship with the delegation event model, the biggest problem with keyboard input was, that the keyboard event had changed. The new event model distinguishes pressed keys and typed keys: pressed keys are the keys that you press, each key the user presses results in a key press event, typed keys are the end results meaning that if a character has to be typed using a sequence of keys only the end result causes a key typed event.

The JDK1.0.2 event model did not have these two key events, it only had a key press event which did almost the same as the 1.1 key typed event. JavaSoft made the mistake of only passing the 1.1 key press events on to 1.0.2 event model classes. This means that Java applications that use the 1.0.2 event model, do not accept keyboard input properly!


Bongo's event handling Before continuing it is important to get a good insight in how Bongo handles events.


the 1.0.2 event handling in Bongo

As can be seen in the previous figure, the AWT generates an event which goes to the top-level Component. The only Component that Bongo uses in that respect is the PlayerPanel. The PlayerPanel has an event handler which handles the events of presentations. The event goes to the PlayerEventHandler that puts it in an event queue. It will also put events of its own in there, for example when a repaint is neces-sary or when we want to generate a rest or wake event.

The PlayerEventHandler is a thread that is constantly watching the event queue and will pass them on to the PlayerPanel whenever necessary. That is step 4 and 5. The PlayerPanel than determines where the event should go: it will locate the widget in its presentation that should get the event.


Key event conversion
Because the problems with keyboard input were affecting a lot of problems, we decided to look for a solution. The result was a conversion mechganism that generates 1.0.2 key press events when a 1.1 key typed event arrives. In some cases it generates a 1.0.2 key press event when a 1.1 key press event arrives.

This of course requires that Bongo receives 1.1 events now and converts them itself. The conversion algorithm was added to the PlayerEventHandler which would then use that before putting the converted event in the event queue. Unfortunately there was a problem with that solution. The JITs (Just In Time compilers) compile a class to a native representation ahead of time. This also means that it will verify certain things, including whether it can find the class. If Bongo presentations were running in a 1.0.2 JIT it would cause a verify error, because the PlayerEventHandler was handling 1.1 event classes, that were unknown to that JIT! This oc-curred in Netscape's Netcaster, that has a 1.0.2 AWT.

The solution turned out to let the PlayerPanel use different PlayerEventHandlers for the 1.0 and 1.1 VM's. A subclass of the original PlayerEventHandler was created: the PlayerEventHandler11 class. That event handler is used if the jdk version returns 1.1, otherwise it uses the old one. The result is that 1.0.2 JITs still give one verify error for the PlayerEventHandler11, but since that class is not being used it does not matter anymore!


Summarized
If a Bongo presentation is running in a 1.0.2 VM, the event handling still goes the same. In 1.1 VM's though the PlayerPanel will use the new event handler, as can be seen in the next figure.


the 1.1 event handling in Bongo

Notice that the new steps are labeled with an italic number, so it is clearer what is new here. The new event handler is just a subclass of the original PlayerEventHandler with the addition event conversion (steps 3 and 4). Starting at step 6 everything is back to normal.

Now that the most important things regarding the switch to the JDK1.1 have been described, it is time for the internationalization features that Bongo 1.1 supports.

Internationalization

In this chapter you will find a short overview of the internationalization support in Bongo 1.1. All this became possible when JavaSoft released the JDK 1.1 that enabled internationalization in a standardized way.


Locales
In the JDK 1.1, a locale is simply an identifier for a particular combination of language and region. It is not a collection of locale-specific attributes. Instead, each locale-sensitive class maintains its own locale-specific information. With this design, there is no difference in how user and system objects maintain their locale-specific resources. Both use the standard localization mechanism.

Java programs are not assigned a single global locale. All locale-sensitive operations may be explicitly given a locale as an argument. This greatly simplifies multilingual programs. While a global locale is not enforced, a system wide default locale is available for programs that do not wish to manage locales explicitly. A default locale also makes it possible to affect the behavior of the entire presentation with a single choice.

In Bongo we decided to give each widget a locale, which means that a Bongo presentation can be multi-lingual. Even better: the widget accesses the resource that belongs to its resource, which will be discussed later in this chapter (string resources).


Character encoding
Java uses Unicode as its native character encoding; however, many Java programs still need to handle text data in other encodings. Java therefore provides a set of classes that convert many standard character en-codings to and from Unicode. Java programs that need to deal with non-Unicode text data will typically convert that data into Unicode, process the data as Unicode, then convert the result back to the external character encoding.

The JDK 1.1 has methods for doing this in the String class. In Bongo we are supporting different charac-ter encodings per widget. This makes it possible to have input in another language than the default plat-form language. Character encoding only affects keyboard input in Bongo.

A good example of what the per widget character encoding enables you to do is a presentation with a two page folder. The first page might be a Greek one, the second page a Japanese one. By setting the character encoding of the first page to Greek and the encoding of the second page to Japanese, the presentation has two parts that supports input of both languages on any machine.


String resources
For multilingual applications, a solution is to develop a Bongo presentation for each language, which means a large collection of .gui files that have to be maintained separately. Since that is not an ideal situa-tion, we decided to find a better solution: StringResources.

The class StringResources uses a JDK 1.1 ResourceBundle to access an external text file with (key, value) pairs. The ResourceBundle class enables developers to access resources customized for the locales they support. To aid in the process of localization, it helps to have these resources grouped together by locale and separated from the locale-neutral parts of the program, which is exactly what the ResourceBundle class enables.

So how does this exactly work? In the Presentation, there is a property called "keyResource" that is the name of the resource file; all widgets inside that presentation will use the resource with that name. The ResourceBundle class automatically does the following: it looks up a text file in the classpath with one of the following names in the specified order:

  • resourceName + "_" + language1 + "_" + country1 + "_" + variant1
  • resourceName + "_" + language1 + "_" + country1
  • resourceName + "_" + language1
  • resourceName + "_" + language2 + "_" + country2 + "_" + variant2
  • resourceName + "_" + language2 + "_" + country2
  • resourceName + "_" + language2
  • resourceName

with the filename extension ".properties", where language1, country1 and variant1 are taken from the given locale and language2, country2 and variant2 are taken from the default locale (depends on the sys-tem that the Presentation is running on).
The fact that it automatically tries to use a given locale, enables each Widget to use its own locale for the given Presentation keyResource (called "resourceName" in the list). A good example: the Presentation keyResource is set to "myName", one TextBoxWidget has the locale set to US English and another has it set to Dutch. The first textbox will get its value from a file called "myName_en_US.properties" and the second one from "myName_nl_NL.properties".

There is one other issue though: developers do not want to have every string looked up from an external property file. This is why Bongo by default does not do a lookup from a StringResources object, but only does this if the strings starts and ends with an '@' character. So keys are identified in Bongo by their '@' signs, for example: @hello@ for a key called hello.

The end result of all this is that one Presentation (one .gui file) can be used for more than one language by externalizing its strings in a separate .properties file. Bongo uses the StringResources class for that, this class can also be used directly by developers if they want to.


Other internationalization issues
Another important addition is what the encoding and locale of the builder have to be set to. As mentioned earlier in this report this can be set in the Options dialog of Bongo. For both there are three options that can be useful:

  • system - the system encoding/locale is used for Bongo, the encoding and locale never change;
  • selected widget - the encoding/locale is set to that of the currently selected widget, which can for ex-ample be very useful when you have a Japanese textbox: in that case developers might want to input Japanese in the property list, in which case the encoding of Bongo has to be set to this option;
  • choose - a chosen encoding/locale is used for Bongo, the encoding and locale is different from the default system encoding/locale, but they never change.

All these internationalization features result in the ability to localize Bongo and any Presentation. The StringResources solution enables us to externalize strings easily per locale and by using the locale and encoding properties parts of a presentation can be used for other languages.

FREE Bongo

Bongo used to be an open-source project, which could be found at http://www.freebongo.org *. That community has 'died', waiting for someone to pick it up or always remain a memory.

The company that originally created Bongo is Marimba, and can be found at http://www.marimba.com *.