<!doctype book PUBLIC "-//Davenport//DTD DocBook V3.0//EN">
<book id="index">
  <bookinfo>
    <title>Java-GNOME 2.10 GNOME Tutorial</title>
    <authorgroup>
      <author>
	<firstname>Jeffrey</firstname>
	<surname>Morgan</surname>
	<affiliation>
	  <orgname>The Java-GNOME Project</orgname>
	  <address>
          <email>kuzman@gmail.com</email>
        </address>
	</affiliation>
      </author>
    </authorgroup>
    <copyright>
      <year>2000-2005</year>
      <holder>Jeffrey Morgan</holder>
    </copyright>

<abstract>
<para>
This tutorial is an introduction to the Java bindings for the GNOME
desktop environment.  The intended audience is a developer that 
wants to develop a GNOME application in Java. 
</para>
</abstract>
</bookinfo>

<preface id="copyright">
<title>Credits and Copyrights</title>

<para>
All of the code given in this tutorial is under the GNU Library
General Public License.  All examples were written by the
Java-GNOME team.
</para>
</preface>

<chapter id="introduction">
<title>Introduction</title>
<para>
As the popularity of the GNOME Desktop continues to grow, both
inside and outside of the Linux community, more developers are 
going to be interested in targeting their applications for this
environment.  At the same time, the Java programming language
is gaining followers.  Java's object oriented capabilities, 
garbage collection, cross platform capabilities, and class
libraries make it an ideal language for some application
development.  
</para>

<para>
It's only natural that some of the developers that are drawn
to GNOME will want to develop in Java.  The purpose of the
Java-GNOME binding is to make this possible.
</para>

<para>
This tutorial assumes that you are not familiar with GTK or
GNOME.  More seasoned GNOMErs will find it very easy to learn 
this binding.  Developer's that are new to this environment 
may want to also study the GTK and GNOME documentation.  I 
will try to cover as much of this background as possible but 
I am sure to leave something out.
</para>

<para>
I would like to hear from anyone who uses this tutorial to learn
Java-GNOME.  Please let me know how I can improve this document
and ease your learning curve.
</para>

<para>
This document is a work in progress.  You can get the latest
copy at the <ulink url="http://java-gnome.sourceforge.net">
Java-GNOME home page</ulink>
</para>
</chapter>

<chapter id="getting-started">
<title>Let's Get Started</title>
<para>
This tutorial assumes that you have already downloaded the 
Java-GNOME source and have installed it.  Don't forget to include
gtk.jar and gnome.jar in your classpath.
</para>

<sect1><title>My First GNOME Application</title>
<para>
The best place to start is with a small example.  This first example
builds a small window.  It is not very useful at this point but
we will be building upon this example throughout the tutorial.  
</para>

<example>
<title>First.java</title>
<programlisting role="Java">

// First we import the libraries used by this example
import org.gnu.gnome.App;
import org.gnu.gnome.Program;
import org.gnu.gtk.Gtk;

public class First {

    public static void main(String[] args) {
        // Initialization
        Program.initGnomeUI("First", "0.1", args);

        App app = new App("First", "First App");
        app.show();

        Gtk.main();
    }
}
</programlisting>
</example>

<para>
The above program can be compiled using:
</para>

<screen>
<prompt>$</prompt><userinput>javac First.java</userinput>
</screen>

<para>
Once compiled, the example can be ran as follows:
</para>
<screen>
<prompt>$</prompt><userinput>java First</userinput>
</screen>

<para>
This command assumes that gtk.jar and gnome.jar are in your classpath.
If they are not I would recommend doing so now as it will help you
throughout this tutorial.  It might also be a good time to make sure
the Java-GNOME shared objects are in your library path.  This can be
done in one of two ways.  You can specify this path on the command-
line by specifying <emphasis>'-Djava.library.path=path_to_shared_objects'
</emphasis>.  The other way to specify the shared object path is to make sure
the path is included in the <emphasis>LD_LIBRARY_PATH</emphasis> environment
variable.
</para>

<para>
OK! I did say I would provide some background so here it goes.
</para>

<sect2><title>GTK+</title>
<para>
GTK+ (the Gimp Tool Kit) is the GUI foundation that is utilized by the
GNOME project. GTK+ is a general purpose GUI library written in C.  It
depends upon GLib, a utility library that provides memory management 
routines, data structures and the methods to operate on them, debugging
macros, and a portability layer.
</para>

<para>
The GTK+ package actually contains two libraries.  They are GTK and
GDK.  GDK (the Gimp Drawing Kit) is a simplified portability layer
directly over the Windowing System.  GDK is not only for low level
library developers, but it also provides numerous drawing functions
that will be of interest to GUI developers (Color and font manipulation
to name a few).
</para>

<para>
GTK provides a large collection of widgets that can be used to construct
windows.  In addition to the widgets, GTK also provides window layout
capabilities that is based upon the same concept as the Java Layout
Managers.
</para>
</sect2>

<sect2><title>GNOME</title>
<para>
GNOME (the GNU Network Object Model Environment) is an application framework
built upon GTK and numerious other libraries.  From a developers standpoint
it provides a common consistent framework for building applications that
can run in the GNOME desktop environment.
</para>

<para>
Now that I have covered a few of the basics it's time to get back to the
code.
</para>
</sect2>

<sect2><title>Explanation of the code</title>
<para>
The example consists of one class.  The first call you see in main is:
</para>

<example><title>Program.initGnomeUI()</title><programlisting>

Program.initGnomeUI("First", "0.1", args);
</programlisting></example>

<para>
The prototype for the Java method is:
</para>

<funcsynopsis>
<funcdef>public static Program <function>Program.initGnomeUI</function></funcdef>
<paramdef>String <parameter>appId</parameter></paramdef>
<paramdef>String <parameter>version</parameter></paramdef>
<paramdef>String[] <parameter>argv</parameter></paramdef>
</funcsynopsis>

<para>
This call initializes the application.  This sets up all of the GNOME
internals and prepares them.  The first parameter, <emphasis>appId
</emphasis>, is the application id.  The second parameter, <emphasis>
version</emphasis>, should contain the application version.  The third
parameter is the command-line arguments.  Gnome will parse the 
command-line arguments looking for information that it is interested 
in and will take the appropriate actions.  We will be discussing this in 
more detail later in this tutorial.
</para>

<para>
The next call creates a App object.
</para>

<example><title>App constructor</title><programlisting>

App app = new App("First", "First App");
</programlisting></example>

<para>
The prototype for the constructor is:
</para>

<funcsynopsis>
<funcdef>public <function>App</function></funcdef>
<paramdef>String <parameter>appname</parameter></paramdef>
<paramdef>String <parameter>title</parameter></paramdef>
</funcsynopsis>

<para>
The <emphasis>appname</emphasis> parameter is the name of the program
and is used in filesnames and paths for configuration information.  The 
<emphasis>title</emphasis> parameter is the window title for the 
application. Toplevel GNOME applicaions would usually only create one 
<emphasis>App</emphasis> object as their toplevel window.  The 
<emphasis>App</emphasis> class has built-in support for menusbars, 
toolbars, and statusbars.  It also takes care of loading the accelerators 
for you.
</para>

<para>
Finally, we need to tell the App to display itself.
</para>

<example><title>Widget.show()</title><programlisting>

app.show();
</programlisting></example>

<para>
All widgets must call this method in order to be seen.  When the
toplevel widget calls this method the entire window is displayed.
</para>

<para>
The final call in this example is:
</para>

<example><title>Gtk.main()</title><programlisting>

Gtk.main();
</programlisting></example>

<para>
This call places the application in the main event loop.  This must be
called in every GNOME and GTK application.
</para>

</sect2>
</sect1>
</chapter>

<chapter id="menus">
<title>Adding Menus</title>
<para>
In this chapter we will add menus to our application.  But before we begin
to look at menus let's restructure our code from the last chapter.  Since
Java is an object-oriented language we should structure our code to take
advantage of this feature.  Also, let's take our first look at signals, 
the event mechanism in GTK.
</para>

<sect1><title>Event Handling</title>
<para>
All GNOME and GTK applications are event driven.  This means that when actions
take place in the application it causes an event to be sent from a source
and acted upon by a reciever.  In fact, when we made the call <emphasis>
Gtk.main()</emphasis> in the program in the last chapter we were telling
our application to wait for an event and when the event arrives, a function
is called.  The event is called a <emphasis>signal</emphasis> in GNOME and
GTK and the function is called a <emphasis>callback</emphasis>.  
</para>

<para>
The example below demonstrates the concept event handling.  It
is the example from the previous chapter (restructured a little) and adds
callbacks to handle the <emphasis>delete</emphasis> and <emphasis>
destroy</emphasis> events.
</para>

<example>
<title>Second.java - first take</title>
<programlisting role="Java">

import org.gnu.gnome.About;
import org.gnu.gnome.App;
import org.gnu.gnome.Program;
import org.gnu.gnome.UIInfo;
import org.gnu.gtk.Gtk;
import org.gnu.gtk.event.LifeCycleEvent;
import org.gnu.gtk.event.LifeCycleListener;
import org.gnu.gtk.event.MenuItemEvent;
import org.gnu.gtk.event.MenuItemListener;

public class Second implements MenuItemListener {
    private App app = null;
    public static final String appVersion = "0.1";

    public Second() {
        createMainWindow();
        app.showAll();
    }

    private void createMainWindow() {
        app = new App("Second", "Second App");
        app.setDefaultSize(200, 200);
        app.addListener(new LifeCycleListener() {
            public void lifeCycleEvent(LifeCycleEvent event) {}
            public boolean lifeCycleQuery(LifeCycleEvent event) {
                Gtk.mainQuit();
                return false;
            }
        });
    }

    public static void main(String[] args) {
        Program.initGnomeUI("Second", Second.appVersion, args);
        Second second = new Second();
        Gtk.main();
    }
}
</programlisting>
</example>

<para>
First let's look at how we restructured the code.  As you can see I have
added two instance variables to our class. I have also added a constructor and
a method to build the window.  Finally, I have added an inner class to handle
the events featured in this example. 
</para>

<para>
The creation of the window is handled in the <emphasis>createMainWindow
</emphasis> method which is called from the constructor.  This method 
creates our <emphasis>App</emphasis>, sets size information, and provides
a handler for the events. 
</para>

<para>
Most window managers have a "Close" menu item on a menu or the toolbar.  
Selecting this menu option causes an event to be sent to your application.
This event is the <emphasis>delete</emphasis> event.  Your application
should intercept this event and perform any necessary cleanup.  Things
that you might want to do here is close files you have open, close
any socket connections, prompt the user to save changed data, etc.  The 
<emphasis>delete</emphasis> event and other events that relate to
the lifecycle of the application are represented by the <emphasis>
LifeCycleEvent</emphasis> class and are handle by <emphasis>LifeCycleListener
</emphasis>.  The example above handles this event by creating an inner
class that implements <emphasis>LifeCycleListener</emphasis> to
receive the event.  This Interface has two methods.  The method
which returns a boolean is invoked when the <emphasis>delete</emphasis>
event or <emphasis>destroy</emphasis> event is triggered.  If you
return true then the event is not propogated.  In other words, if you
do not wish for the application to exit you should just return true.
This is useful if you wish to prompt the user with a dialog ask
something like "Are you sure you wish to exit?".  If the user
clickes the "No" button you just return true.
</para>

<para>
This has been a very brief introduction to event handling but since
every example will be using them throughout the remainder of this tutorial
we will stop here now and expand our discussions as we explore new topics.
Now on to the menus.
</para>
</sect1>

<sect1 id="adding-menus"><title>Adding menus to our example</title>
<para>
The primary mechanism for creating menus and toolbars in GNOME is
a C structure called <emphasis>GnomeUIInfo</emphasis>.  To ease 
the use of this struct the GNOME developers have provided a set 
of macros to wrap the initialization.  The Java-GNOME bindings 
have a java class called <emphasis>UIInfo</emphasis> that 
wraps the <emphasis>GnomeUIInfo</emphasis> C structure. 
The UIInfo class also provides access to the stock menus provided
by GNOME.
</para>

<para>
"What is a stock menu?", you ask.  The GNOME libraries provide a
set of stock menus, toolbar buttons, and dialog buttons that can
be used by applications.  Whenever possible it is suggested you
use these stock widgets as it facilitates a common look-and-
feel from application to application and makes new applications
easier to learn.
</para>

<para>
Let's get started by looking at the UIInfo class.  The table
below shows the initialization parameters and their possible values.
Some of the difficulty in this class is due to the fact that the
C structure contains numerous pointers that actually point to 
different data types depending on other values in the struct.
Don't worry, it is not necessary to understand the C structure
or pointers to use <emphasis>UIInfo</emphasis>.
</para>

<table frame="all"><title>UIInfo</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<thead>
  <row>
    <entry>Type/Name</entry>
    <entry>Description</entry>
  </row>
</thead>
<tbody>
  <row>
    <entry>UIInfoType type</entry>
    <entry>This determines the type of item being described.  The
           list of possible types are listed in a table later in
           this chapter.
    </entry>
  </row>
  <row>
    <entry>String label</entry>
    <entry>This is the label that will appear in the menu item or
           on the toolbar.
    </entry>
  </row>
  <row>
    <entry>String hint</entry>
    <entry>This is the string that will appear in the statusbar for
           a menu or as a tooltip for a toolbar button.
    </entry>
  </row>
  <row>
    <entry>moreInfo</entry>
    <entry>This is one of the more complex elements in the 
           <emphasis>UIInfo</emphasis> class.  If the type is an item, 
           toggle item, or radio item this contains the information
           (method, object/class, and user data) for the callback to 
           be invoked when the menu or toolbar item is activated.  For
           a radio item lead or a subtree type this contains an array of
           <emphasis>UIInfo</emphasis> objects.  For a help type
           this contains the string that specifies the name of the help
           node to load.  We will have more about the help node later
           in this chapter.
    </entry>
  </row>
  <row>
    <entry>UIPixmapType pixmapType</entry>
    <entry>This describes the type of pixmap.  You will find a table of 
           the possible values below.
    </entry>
  </row>
  <row>
    <entry>pixmapInfo</entry>
    <entry>Based upon the type of pixmap, this contains the actual data
           used to render the image.  If the type is none then this value
           contains null.  If the type is filename or stock this contains
           a string that points to the filename or a name describing
           the stock pixmap to use.  If the type is data this contains
           an array of chars that hold the pixmap data.  All of the GNOME
           stock pixmaps are described by the <emphasis>GnomeStockPixmapName
           </emphasis> class.
    </entry>
  </row>
  <row>
    <entry>int acceleratorKey</entry>
    <entry>This defines the accelerator key.  A value of 0 means that
           no accelerator key is used.
    </entry>
  </row>
  <row>
    <entry>GdkModifierType acMods</entry>
    <entry>This contains the mask of modifier keys for the accelerator.
    </entry>
  </row>
  <row>
    <entry>GtkWidget widget</entry>
    <entry>This is a read-only reference to the widget that is created
           by GNOME.  You can use this to get the widget in order to
           enable/disable it, etc.
    </entry>
  </row>
</tbody>
</tgroup>
</table>

<para>
Without further delay let's describe the possible types defined in
<emphasis>GnomeUIInfoType</emphasis>.
</para>

<table frame="all"><title>GnomeUIInfoType</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<thead>
  <row>
    <entry>Type</entry>
    <entry>Description</entry>
  </row>
</thead>
<tbody>
  <row>
    <entry>ENDOFINFO</entry>
    <entry>This is a special entry that lets GNOME know this is the
           end of a list of <emphasis>GnomeUIInfo</emphasis> elements.
    </entry>
  </row>
  <row>
    <entry>ITEM</entry>
    <entry>This is a normal menu item or radio menu item if it is inside
           of a radio list.
    </entry>
  </row>
  <row>
    <entry>TOGGLEITEM</entry>
    <entry>This is a toggle menu item.</entry>
  </row>
  <row>
    <entry>RADIOITEMS</entry>
    <entry>This is a list of radio items that belong to a radio group.</entry>
  </row>
  <row>
    <entry>SUBTREE</entry>
    <entry>This contains an array of sub-menu items.</entry>
  </row>
  <row>
    <entry>SEPARATOR</entry>
    <entry>This is a special entry that represents a separator on a menu
           or toolbar.
    </entry>
  </row>
  <row>
    <entry>HELP</entry>
    <entry>This is a list of help topics for the help menu.  We will
           describe the help mechanism in GNOME in detail later in this
           chapter.
    </entry>
  </row>
  <row>
    <entry>BUILDERDATA</entry>
    <entry>This feature is not currently supported by Java-GNOME.</entry>
  </row>
  <row>
    <entry>CONFIGURABLE</entry>
    <entry>This contains a menu or toolbar item that can be completely
           configured.  More on this later.
    </entry>
  </row>
  <row>
    <entry>SUBTREE_STOCK</entry>
    <entry>This is the same as a SUBTREE except that the hints, lable,
           and icon used come from the Gnome STOCK.
    </entry>
  </row>
</tbody>
</tgroup>
</table>

<para>
The last data element that I wish to describe before moving on is
the <emphasis>GnomeUIPixmapType</emphasis> class.
</para>


<table frame="all"><title>GnomeUIPixmapType</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<thead>
  <row>
    <entry>Type</entry>
    <entry>Description</entry>
  </row>
</thead>
<tbody>
  <row>
    <entry>NONE</entry>
    <entry>This pixmap type tells GNOME that there is no pixmap 
           associated with this menu or toolbar item.
    </entry>
  </row>
  <row>
    <entry>STOCK</entry>
    <entry>This pixmap type tells GNOME to use one of its' stock icons.
    </entry>
  </row>
  <row>
    <entry>DATA</entry>
    <entry>This pixmap type provides GNOME with the raw data used to
           render its' icon.
    </entry>
  </row>
  <row>
    <entry>FILENAME</entry>
    <entry>This pixmap type tells GNOME to read a file that contains the
           pixmap data.
    </entry>
  </row>
</tbody>
</tgroup>
</table>

<para>
As you can see, <emphasis>UIInfo</emphasis> can be quite confusing.
If at all possible you should use one of the convenience methods 
provided by <emphasis>UIInfo</emphasis>.  These convenience methods wrap the
initialization of the UIInfo factory with easy to use static
methods that closely mimic the macros provided by GNOME.  
</para>

<para>
The best way to demonstrate how to use <emphasis>UIInfo</emphasis> is 
through an example.  Let's take our previous example and add a few menus.
</para>


<example>
<title>Second.java - second take</title>
<programlisting role="Java">

import org.gnu.gnome.App;
import org.gnu.gnome.Program;
import org.gnu.gnome.UIInfo;
import org.gnu.gtk.AboutDialog;
import org.gnu.gtk.ButtonsType;
import org.gnu.gtk.DialogFlags;
import org.gnu.gtk.Gtk;
import org.gnu.gtk.MessageDialog;
import org.gnu.gtk.MessageType;
import org.gnu.gtk.event.LifeCycleEvent;
import org.gnu.gtk.event.LifeCycleListener;
import org.gnu.gtk.event.MenuItemEvent;
import org.gnu.gtk.event.MenuItemListener;

public class Second implements MenuItemListener {
    private App app = null;
    public static final String appVersion = "0.1";

    public Second() {
        createMainWindow();
        createMenus();
        app.showAll();
    }

    private void createMainWindow() {
        app = new App("Second", "Second App");
        app.setDefaultSize(200, 200);
        app.addListener(new LifeCycleListener() {
            public void lifeCycleEvent(LifeCycleEvent event) {}
            public boolean lifeCycleQuery(LifeCycleEvent event) {
                Gtk.mainQuit();
                return false;
            }
        });
    }

    private void createMenus() {

        UIInfo fileMenu[] = {
            UIInfo.newItem("New Window", "Open a new application window", this),
            UIInfo.separator(),
            UIInfo.openItem(this),
            UIInfo.saveItem(this),
            UIInfo.saveAsItem(this),
            UIInfo.separator(),
            UIInfo.closeItem(this),
            UIInfo.quitItem(new MenuItemListener() {
                public void menuItemEvent(MenuItemEvent event) {
                  fileExit();
                }
            }),
            UIInfo.end()
        };

        UIInfo editMenu[] = {
            UIInfo.undoItem(this),
            UIInfo.redoItem(this),
            UIInfo.separator(),
            UIInfo.cutItem(this),
            UIInfo.copyItem(this),
            UIInfo.pasteItem(this),
            UIInfo.separator(),
            UIInfo.findItem(this),
            UIInfo.findAgainItem(this),
            UIInfo.replaceItem(this),
            UIInfo.propertiesItem(this),
            UIInfo.end()
        };

        UIInfo moveMenu[] = {
            UIInfo.item("_Up", "Move selection up", this),
            UIInfo.item("D_own", "Move selection down", this),
            UIInfo.end()
        };

        UIInfo helpMenu[] = {
            UIInfo.help("second"),
            UIInfo.aboutItem(new MenuItemListener() {
                public void menuItemEvent(MenuItemEvent event) {
                    helpAbout();
                }
            }),
            UIInfo.end()
        };

        UIInfo mainMenu[] = {
            UIInfo.subtree("_File", fileMenu),
            UIInfo.subtree("_Edit", editMenu),
            UIInfo.subtree("_Move", moveMenu),
            UIInfo.subtree("_Help", helpMenu),
            UIInfo.end()
        };

        app.createMenus(mainMenu);
    }

    public void helpAbout() {
	    String appname = "Java-GNOME Tutorial";
	    String version = "0.1";
	    String license = "GPL";
	    String description = "Java-GNOME Tutorial.";
	    String authors[] = { 
	        "http://java-gnome.sf.net", 
	        "http://www.gtk.org" 
	    };
	    String documentors[] = { 
	        "java-gnome-developer@lists.sf.net>", 
	        "http://www.gnome.org"
	    };
	    String translators = "Language Guys Inc.";
	    String website = "http://java-gnome.sf.net";

	    AboutDialog about = new AboutDialog();
        about.setName(appname);
        about.setVersion(version);
        about.setLicense(license);
        about.setComments(description);
        about.setAuthors(authors);
        about.setDocumenters(documentors);
        about.setTranslatorCredits(translators);
        about.setWebsite(website);
		about.show();
    }

    public void fileExit() {
        Gtk.mainQuit();
    }

    public void menuItemEvent(MenuItemEvent event) {
        MessageDialog dialog = new MessageDialog(app, DialogFlags.MODAL, 
                                             MessageType.INFO, ButtonsType.OK,
                                             "Not implemented", false);
        dialog.run();
        dialog.destroy();
    }

    public static void main(String[] args) {
        Program.initGnomeUI("Second", Second.appVersion, args);
        new Second();
        Gtk.main();
    }
}
</programlisting>
</example>

<para>
Most of the work is done in the <emphasis>createMenus()</emphasis>
method which is called from the constructor.  This method creates an
arrays of <emphasis>UIInfo</emphasis> objects using the static
methods in <emphasis>UIInfo</emphasis>.  In this example we create a
"File" menu and an "Edit" menu that use stock menu items.  We also
create a "Move" menu that creates two generic menu items.  The "Move"
menu uses the <emphasis>item()</emphasis> method of <emphasis>
UIInfo</emphasis>.  This method specifies a generic menu
item with no icon. A few other <emphasis>UIInfo</emphasis>
methods are use in this example. For a reference to all of the methods
for <emphasis>UIInfo</emphasis> please refer to the javadocs. Also
note that all <emphasis>UIInfo</emphasis> arrays end with a call
to <emphasis>UIInfo.end()</emphasis>  This is a requirement
imposed by the GNOME libraries.  
</para> 

<para>
Once the top level <emphasis>UIInfo</emphasis> array is
constructed it is passed to the <emphasis>App</emphasis>
via the <emphasis>createMenus()</emphasis> method.  This is where
GNOME works its' magic.
</para>

<para>
One of the menu items warrents further discussion.  The <emphasis>
help()</emphasis> method of <emphasis>UIInfo</emphasis>
creates one or more menu items under a help menu that utilize
the GNOME help facilities.  The string that is passed as a parameter
is the name of the help node that the system tries to load.  The
list of help topics available is read from a data file located in
the <emphasis>/usr/share/gnome/help/"help_node"/C</emphasis> directory
by default.  The "help_node" portion of the directory is the string 
specified in the <emphasis>help()</emphasis> method.  "C" is the 
default directory but if another language is specified it will be used.  
For example, if the users current locale settings specify the German 
language the directory would be <emphasis>
/usr/share/gnome/help/"help_node"/de</emphasis>.
</para>

<para>
Our example application uses a help node of "second".  Therefore, our
data file should be located in the <emphasis>/usr/share/ome/help/second/C/
</emphasis> directory.  The file should always be named <emphasis>"
topic.dat"</emphasis> and could contain the following information:
</para>


<example>
<title>second.dat</title>
<programlisting>
index.html Help _Topics
overview.html _Overview
userguide.html _User Guide
</programlisting>
</example>

<para>
This would add three menu items to our help menu. The help system
will invoke the browser with the appropriate page when the user
selects the menu item.  The "html" files should be located in the
same directory as the ".dat" file.
</para>

</sect1>
</chapter>


<chapter id="tb-sb">
<title>Toolbars and Statusbars</title>
<para>
In this chapter we will focus on adding a toolbar and statusbar
to our application.  GNOME uses many of the same facilities to 
create toolbars as it uses to create menus; namely <emphasis>
UIInfo</emphasis>.  This chapter will use the same class
from the previous chapter to create a toolbar and add it to the 
application.  We will also be creating a standard statusbar and 
connecting the "hints" from the <emphasis>UIInfo</emphasis> 
classes to the statusbar.
</para>

<sect1><title>Toolbars</title>
<para>
A Toolbar is a container used to group widgets in a dockable frame.
Typically the toolbar contains buttons and is positioned across the
top of the main window of an application.  Let's 
get started by extending the example from the last chapter by 
adding a toolbar.
</para>

<example>
<title>Third.java - first take</title>
<programlisting role="Java">

import org.gnu.gnome.App;
import org.gnu.gnome.Program;
import org.gnu.gnome.UIInfo;
import org.gnu.gtk.AboutDialog;
import org.gnu.gtk.ButtonsType;
import org.gnu.gtk.DialogFlags;
import org.gnu.gtk.Gtk;
import org.gnu.gtk.GtkStockItem;
import org.gnu.gtk.MessageDialog;
import org.gnu.gtk.MessageType;
import org.gnu.gtk.StatusBar;
import org.gnu.gtk.event.ButtonEvent;
import org.gnu.gtk.event.ButtonListener;
import org.gnu.gtk.event.LifeCycleEvent;
import org.gnu.gtk.event.LifeCycleListener;
import org.gnu.gtk.event.MenuItemEvent;
import org.gnu.gtk.event.MenuItemListener;

public class Third implements MenuItemListener, ButtonListener {
	private App app = null;
 	private StatusBar statusbar = null;
 	public static final String appVersion = "0.1";

	public Third() {
		createMainWindow();
		createMenus();
		createToolbar();
		app.showAll();
	}

	private void createMainWindow() {
		app = new App("Third", "Third App");
		app.setDefaultSize(350, 200);
		app.addListener(new LifeCycleListener() {
			public void lifeCycleEvent(LifeCycleEvent event) {}
			public boolean lifeCycleQuery(LifeCycleEvent event) {
				Gtk.mainQuit();
				return false;
			}
		});
	}

	private void createMenus() {

		UIInfo fileMenu[] = {
			UIInfo.newItem("New Window", "Open a new application window", this),
			UIInfo.separator(),
			UIInfo.openItem((MenuItemListener) this),
			UIInfo.saveItem((MenuItemListener) this),
			UIInfo.saveAsItem((MenuItemListener) this),
			UIInfo.separator(),
			UIInfo.closeItem((MenuItemListener) this),
			UIInfo.quitItem(new MenuItemListener() { 
				public void menuItemEvent(MenuItemEvent event) { 
					fileExit();
				}
			}), 
			UIInfo.end()
		};

		UIInfo editMenu[] = {
			UIInfo.undoItem((MenuItemListener) this),
			UIInfo.redoItem((MenuItemListener) this),
			UIInfo.separator(),
			UIInfo.cutItem((MenuItemListener) this),
			UIInfo.copyItem((MenuItemListener) this),
			UIInfo.pasteItem((MenuItemListener) this),
			UIInfo.separator(),
			UIInfo.findItem((MenuItemListener) this),
			UIInfo.findAgainItem((MenuItemListener) this),
			UIInfo.replaceItem((MenuItemListener) this),
			UIInfo.propertiesItem((MenuItemListener) this),
			UIInfo.end()
		};

		UIInfo moveMenu[] = {
			UIInfo.item("_Up", "Move selection up", (MenuItemListener) this),
			UIInfo.item("D_own", "Move selection down", (MenuItemListener) this),
			UIInfo.end()
		};

		UIInfo helpMenu[] = {
			UIInfo.help("second"),
			UIInfo.aboutItem(new MenuItemListener() { 
				public void menuItemEvent(MenuItemEvent event) { 
					helpAbout();
				}
			}), 
			UIInfo.end()
		};

		UIInfo mainMenu[] = {
			UIInfo.subtree("_File", fileMenu),
			UIInfo.subtree("_Edit", editMenu),
			UIInfo.subtree("_Move", moveMenu),
			UIInfo.subtree("_Help", helpMenu),
			UIInfo.end()
		};
		app.createMenus(mainMenu);
	}

	private void createToolbar() {

		UIInfo toolbar[] = {
			UIInfo.itemStock("New", "Create a new file", 
					(ButtonListener) this, GtkStockItem.NEW),
			UIInfo.itemStock("Open", "Open a file", 
					(ButtonListener) this, GtkStockItem.OPEN),
			UIInfo.separator(),
			UIInfo.itemStock("Save", "Save this file", 
					(ButtonListener) this, GtkStockItem.SAVE),
			UIInfo.itemStock("Save As", "Save this file as", 
					(ButtonListener) this, GtkStockItem.SAVE_AS),
			UIInfo.separator(),
			UIInfo.itemStock("Close", "Close this file", 
					(ButtonListener) this, GtkStockItem.CLOSE),
			UIInfo.end()
		};

		app.createToolBar(toolbar);
	}

	public void helpAbout() {
	    String appname = "Java-GNOME Tutorial";
	    String version = "0.1";
	    String license = "GPL";
	    String description = "Java-GNOME Tutorial.";
	    String authors[] = { 
	        "http://java-gnome.sf.net", 
	        "http://www.gtk.org" 
	    };
	    String documentors[] = { 
	        "java-gnome-developer@lists.sf.net", 
	        "http://www.gnome.org"
	    };
	    String translators = "Language Guys Inc.";
	    String website = "http://java-gnome.sf.net";

	    AboutDialog about = new AboutDialog();
        about.setName(appname);
        about.setVersion(version);
        about.setLicense(license);
        about.setComments(description);
        about.setAuthors(authors);
        about.setDocumenters(documentors);
        about.setTranslatorCredits(translators);
        about.setWebsite(website);
		about.show();
	}

	public void fileExit() {
		Gtk.mainQuit();
	}

	public void menuItemEvent(MenuItemEvent event) {
		displayMessage();
	}

	public void buttonEvent(ButtonEvent event) {
		displayMessage();
	}
	
	private void displayMessage() {
		MessageDialog dialog = new MessageDialog(app, DialogFlags.MODAL, 
				MessageType.INFO, ButtonsType.OK,
				"Not implemented", false);
		dialog.run();
		dialog.destroy();
	}

	public static void main(String[] args) {
		Program.initGnomeUI("Third", Third.appVersion, args);
		new Third();
		Gtk.main();
	}
}

</programlisting>
</example>

<para>
In this example we have added a new method <emphasis>createToolbar()
</emphasis>.  This method creates a single array of  <emphasis>
UIInfo</emphasis> objects.  As you can see, we are creating toolbar buttons
with stock images with the <emphasis>itemStock()</emphasis> method.
</para>

<para>
Once we have created the array of <emphasis>UIInfo</emphasis>
objects we create the toolbar with a call to <emphasis>createToolbar()
</emphasis>.  This causes a dockable toolbar to be created and 
attached to the top of the <emphasis>App</emphasis> window just below
the menu.
</para>

</sect1>

<sect1 id="statusbar"><title>Statusbars</title>
<para>
A statusbar is a widget that usually is displayed along the bottom of
the main application window and is used to display helpful text messages
to the user.  The GTK implementation of a statusbar works like a FIFO
queue.  Messages are "pushed" on the statusbar and are "popped" off the
statusbar.  In GNOME we have the added benefit of being able to connect
the statusbar to the <emphasis>UIInfo</emphasis> hints associated
with menu items.  Let's extend the example and add a statusbar to see
how this works.  We will only show the updated code in the following
listing.
</para>

<example>
<title>Third.java - second take</title>
<programlisting role="Java">


public class Third {
    private GnomeApp app = null;
    private GtkStatusbar statusbar = null;
    public static final String appVersion = "0.1";

    public Third() {
        createMainWindow();
        createMenusAndStatusbar();
        createToolbar();
        app.showAll();
    }

    ...

    private void createMenusAndStatusbar() {

    ...

        app.createMenus(mainMenu);

 		statusbar = new StatusBar();
		app.setStatusBar(statusbar);
		app.installMenuHints(mainMenu);
    }

    ...
}

</programlisting>
</example>

<para>
In this updated example we have renamed our <emphasis>createMenus()
</emphasis> method to <emphasis>createMenusAndStatusbar()</emphasis>.
At the end of this method we create a <emphasis>Statusbar</emphasis>
object, add it to the <emphasis>App</emphasis> with a call to
<emphasis>setStatusBar()</emphasis> and install the menu's hints with
a call to <emphasis>installMenuHints()</emphasis>.  When you
move your mouse over any menu item you will see it's hints displayed in
the statusbar.
</para>

<para>
So far we have put together a shell that contains a menu, a toolbar and
a statusbar.  Now it is time to move on to adding widgets to our application.
But first we must understand the concept of layout managers, which is
the topic of the next chapter.
</para>

</sect1>

</chapter>

<chapter id="layout">
<title>Layout Management</title>
<para>
This chapter discusses placing widgets on windows.  The positioning is
accomplished with the help of layout managers.  In GNOME this is called
<emphasis>packing</emphasis> widgets.  GTK provides two such layout managers,
a table and a box, and we will discuss both in this chapter.  This chapter
will not discuss the need for layout managers as most Java developers
already use them for GUI development.
</para>


<sect1><title>Boxes</title>
<para>
Those of you who have used the JDK's Box layout manager shoud be familiar
with the concept of the GTK boxes.  They layout their children either side
by side (the horizontal box) or one on top of the other (the vertical box).
That is where their similarities end.  The GNOME boxes offer more flexibility
in the way widgets behave once they are under the control of a box.
</para>

<para>
<emphasis>VBox</emphasis> and <emphasis>HBox</emphasis> are the two
box classes that provide vertical and horizontal layout.  The constructors
for these classes are as follows:
</para>

<funcsynopsis>
<funcdef>public  <function>VBox</function></funcdef>
<paramdef>boolean <parameter>homogenous</parameter></paramdef>
<paramdef>int <parameter>spacing</parameter></paramdef>
</funcsynopsis>

<funcsynopsis>
<funcdef>public  <function>HBox</function></funcdef>
<paramdef>boolean <parameter>homogenous</parameter></paramdef>
<paramdef>int <parameter>spacing</parameter></paramdef>
</funcsynopsis>

<para>
The first parameter to the constructor, <emphasis>boolean homogenous
</emphasis>, tells the <emphasis>VBox</emphasis> if all children
should occupy the same height and tells the <emphasis>HBox</emphasis>
if all children should occupy the same width.  The second parameter, 
<emphasis>int spacing</emphasis> is the number of pixels of space to
place around every child widget in the box.  If you specify 4 for this
parameter it will place 8 pixels between each widget due to the fact
that it places this space on each side of the widget.
</para>

<para>
Once a box is created, widgets can be added with the following calls:
</para>

<funcsynopsis>
<funcdef>public void <function>packStart</function></funcdef>
<paramdef>GtkWidget <parameter>child</parameter></paramdef>
<paramdef>boolean <parameter>expand</parameter></paramdef>
<paramdef>boolean <parameter>fill</parameter></paramdef>
<paramdef>int <parameter>padding</parameter></paramdef>
</funcsynopsis>
<funcsynopsis>
<funcdef>public void <function>packStart</function></funcdef>
<paramdef>Widget <parameter>child</parameter></paramdef>
<paramdef>boolean <parameter>expand</parameter></paramdef>
<paramdef>boolean <parameter>fill</parameter></paramdef>
</funcsynopsis>
<funcsynopsis>
<funcdef>public void <function>packStart</function></funcdef>
<paramdef>GtkWidget <parameter>child</parameter></paramdef>
<paramdef>boolean <parameter>expand</parameter></paramdef>
</funcsynopsis>
<funcsynopsis>
<funcdef>public void <function>packStart</function></funcdef>
<paramdef>Widget <parameter>child</parameter></paramdef>
</funcsynopsis>

<funcsynopsis>
<funcdef>public void <function>packEnd</function></funcdef>
<paramdef>Widget <parameter>child</parameter></paramdef>
<paramdef>boolean <parameter>expand</parameter></paramdef>
<paramdef>boolean <parameter>fill</parameter></paramdef>
<paramdef>int <parameter>padding</parameter></paramdef>
</funcsynopsis>
<funcsynopsis>
<funcdef>public void <function>packEnd</function></funcdef>
<paramdef>Widget <parameter>child</parameter></paramdef>
<paramdef>boolean <parameter>expand</parameter></paramdef>
<paramdef>boolean <parameter>fill</parameter></paramdef>
</funcsynopsis>
<funcsynopsis>
<funcdef>public void <function>packEnd</function></funcdef>
<paramdef>Widget <parameter>child</parameter></paramdef>
<paramdef>boolean <parameter>expand</parameter></paramdef>
</funcsynopsis>
<funcsynopsis>
<funcdef>public void <function>packEnd</function></funcdef>
<paramdef>Widget <parameter>child</parameter></paramdef>
</funcsynopsis>

<funcsynopsis>
<funcdef>public void <function>packStartDefaults</function></funcdef>
<paramdef>Widget <parameter>child</parameter></paramdef>
</funcsynopsis>
<funcsynopsis>
<funcdef>public void <function>packEndDefaults</function></funcdef>
<paramdef>Widget <parameter>child</parameter></paramdef>
</funcsynopsis>

<para>
This seems like a lot but it is actually very simple.  The <emphasis>
packStart()</emphasis> series of methods pack widgets starting at the
beginning of the box.  The <emphasis>packEnd()</emphasis> series of 
methods pack widgets starting at the end of the box.  The parameters
tell the box how to layout the child in its' position.  As the methods
eliminate one or more of the parameters a default value is used.  The
next few paragraphs discuss the parameters to the methods and what impact
they have on the child widget.
</para>

<para>
The <emphasis>expand</emphasis> parameter causes the children to use
the entire box.  This parameter doesn't affect that actual size of the
widget, but it does center the widget in a region whose size is determined
by the <emphasis>homogenous</emphasis> parameter, the number of widgets,
and the size of the box.  If the <emphasis>homogenous</emphasis> parameter
is <emphasis>true</emphasis>, each widget gets the same area.  Otherwise,
the area is determined on a percentage basis which is based on the widget's
minimum size.  The default value for the methods that do not include this
paramater is <emphasis>true</emphasis>.
</para>

<para>
The <emphasis>fill</emphasis> parameter instructs the children to grow
to fill all of the area that is allocated to them.  By default a widgets
size is it's minimum size (width for horizontal boxes, and height for
vertical boxes) but the <emphasis>fill</emphasis> parameter will cause
it to expand until it fills it's entire area (either height or width 
depending on the type of box).  The default value for the methods that
do not include this parameter is <emphasis>true</emphasis>.
</para>

<para>
The <emphasis>padding</emphasis> parameter places additional pixels on
both sides of the child.  For vertical boxes, it places a space that is
<emphasis>padding</emphasis> pixes in size on the top and bottom of
a child.  For horizontal boxes, it places a space on the left and right.
The default value for this parameter is <emphasis>0</emphasis>.
</para>

<para>
One thing that should be noted is that if you set the <emphasis>
homogenous</emphasis> value in the constructor to true the <emphasis>
expand</emphasis> parameter has no effect.  As you recall, the <emphasis>
homogenous</emphasis> parameter tells the control to space all widgets
the same size.
</para>

<para>
That is about all there is to boxes.  It seems very simple and you
might be asking how can you achieve an advanced layout with such a simple
layout manager.  Many GNOME applications position their widgets by placing
boxes within boxes to achieve a high degree of flexibility in their
layout.
</para>
</sect1>

<sect1 id="tables"><title>Tables</title>
<para>
A Table is a layout manager that allows you to place you widgets in rows
and columns.  In an AWT world a <emphasis>Table</emphasis> would 
probably fall somewhere between a <emphasis>GridLayout</emphasis> and 
a <emphasis>GridBagLayout</emphasis>.  
</para>

<para>
The constructor for the Table class is as follows:
</para>

<funcsynopsis>
<funcdef>public  <function>Table</function></funcdef>
<paramdef>int <parameter>rows</parameter></paramdef>
<paramdef>int <parameter>columns</parameter></paramdef>
<paramdef>boolean <parameter>homogenous</parameter></paramdef>
</funcsynopsis>

<para>
The first and second parameters specify the number of rows
and columns.  If you specify 2 rows and 2 columns you would get
a table layout similar to the figure below.  The final parameter
specifies if all cells in the table should be the same size.  If 
homogenous is set to true all cells are set to the size of the 
largest cell in the table.
</para>

<figure><title>A 2 X 2 table grid</title>
<programlisting>
      0             1              2
    0 +-------------+--------------+
      |             |              |
      |             |              |
    1 +-------------+--------------+
      |             |              |
      |             |              |
    2 +-------------+--------------+
</programlisting>
</figure>

<para>
The method to insert widgets into the table is as follows:
</para>

<funcsynopsis>
<funcdef>public void <function>attach</function></funcdef>
<paramdef>Widget <parameter>child</parameter></paramdef>
<paramdef>int <parameter>left_attach</parameter></paramdef>
<paramdef>int <parameter>right_attach</parameter></paramdef>
<paramdef>int <parameter>top_attach</parameter></paramdef>
<paramdef>int <parameter>bottom_attach</parameter></paramdef>
<paramdef>AttachOptions <parameter>xoptions</parameter></paramdef>
<paramdef>AttachOptions <parameter>yoptions</parameter></paramdef>
<paramdef>int <parameter>xpadding</parameter></paramdef>
<paramdef>int <parameter>ypadding</parameter></paramdef>
</funcsynopsis>

<para>
On first appearances the method looks overwhelming.  Like the
<emphasis>Box</emphasis> layout managers, <emphasis>
Table</emphasis> provides numerous methods that have
default values for the parameters.  Let's discuss the 
parameters for the <emphasis>attach()</emphasis> method and
their default values.
</para>

<para>
The first parameter, <emphasis>child</emphasis> is the widget
that is to be placed in the table.
</para>

<para>
The next four parameters, <emphasis>left_attach</emphasis>,
<emphasis>right_attach</emphasis>, <emphasis>top_attach
</emphasis>, and <emphasis>bottom_attach</emphasis> define the
location of the widget within the grid.  For example, to place
a widget in the upper left corner of the grid shown above you
would use 0, 1, 0, 1.  To place a widget in the grid that would 
occupy the entire bottom row you would use 0, 2, 1, 2.  These
values are always required.
</para>

<para>
The next two parameters, <emphasis>xoptions</emphasis> and
<emphasis>yoptions</emphasis> define how the widget places and
sizes itself within the cell(s).  They are of type <emphasis>
AttachOptions</emphasis> and the possible values are
described in the table below.  You can provide multiple options
by or'ing them together with the <emphasis>or()</emphasis>
method of <emphasis>AttachOptions</emphasis>.  The default
values for these options is <emphasis>EXPAND</emphasis> and
<emphasis>FILL</emphasis>.
</para>


<table frame="all"><title>AttachOptions</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<thead>
  <row>
    <entry>Value</entry>
    <entry>Description</entry>
  </row>
</thead>
<tbody>
  <row>
    <entry>FILL</entry>
    <entry>The widget expands to fill the cell(s) and grows
           as the table grows.
    </entry>
  </row>
  <row>
    <entry>SHRINK</entry>
    <entry>If the cell is smaller than the widget it contains
           minimum size then the widget can shrink to fit the
           available size.
    </entry>
  </row>
  <row>
    <entry>EXPAND</entry>
    <entry>This tells the table to expand to use any unused
           space around it.
    </entry>
  </row>
</tbody>
</tgroup>
</table>

<para>
The final two parameters, <emphasis>xpadding</emphasis> and <emphasis>
ypadding</emphasis>, specify the number of pixels of padding to
place on the sides of the widget. For example, if you set the value
for <emphasis>xpadding</emphasis> to 5 for all of your widgets then
there will be 10 pixels between them.  5 on the left and 5 on the
right.  The default values for the padding is 0.
</para>

<para>
As we stated before, there are many <emphasis>attach()</emphasis>
methods for <emphasis>Table</emphasis>, each eliminating one
or more of the parameters and utilizing the defaults.  For a 
complete listing of these methods please refer to the javadoc
for the <emphasis>Java-GNOME</emphasis> project.
</para>

<para>
Let's put what we have learned about the GNOME layout managers
to use.  In the next section we will expand on our example to
demonstrate their use.
</para>

</sect1>

<sect1 id="using-lm"><title>Using Layout Managers</title>
<para>
In this section we will expand our example by adding widgets using both
the box and table layout managers.  The example used in this section
is Fourth.java and I recommend you run the program and look at the
layout of the widgets as we discuss the code.
</para>

<para>
We add two tabs to our example, one to demonstrate the <emphasis>
Box</emphasis> classes and one to demonstrate <emphasis>
Table</emphasis>.  The tabs layout the same set of widgets
so you contrast the code necessary to build the window.  Since our
primary objective is to understand how to use the GNOME layout 
managers we have only fully implemented the first tab by connecting 
callbacks.  
</para>

<para>
Unlike the previous chapters, we will only show the code that has 
changed in this version of the example.  The first thing we do is 
declare a few variables that will be used in the example.
</para>

<example>
<title>Fourth.java - variable declaration</title>
<programlisting role="Java">
	// widgets used for the boxes tab
	private VScale vscale = null;
	private HScale hscale = null;
	private CheckButton button = null;
	private Adjustment adj = null;
	private ComboBox valuePosition = null;
	private ComboBox updatePolicy = null;

	// widgets used for the table tab
	private VScale vscale2 = null;
	private HScale hscale2 = null;
	private CheckButton button2 = null;
	private Adjustment adj2 = null;
	private ComboBox valuePosition2 = null;
	private ComboBox updatePolicy2 = null;
</programlisting>
</example>

<para>
Next we add a call to <emphasis>createView()</emphasis> in our 
constructor.
</para>

<example>
<title>Fourth.java - constructor</title>
<programlisting role="Java">
    public Fourth() {
        createMainWindow();
        createMenusAndStatusbar();
        createToolbar();
        createView();
        app.showAll();
    }
</programlisting>
</example>

<para>
The <emphasis>createView()</emphasis> method is quite simple.  We
just create a <emphasis>Notebook</emphasis>, add it to the
<emphasis>App</emphasis> with the <emphasis>setContents()
</emphasis> method and then add two pages.
</para>

<example>
<title>Fourth.java - createView()</title>
<programlisting role="Java">
 	private void createView() {
		// Create a notebook to hold our two example pages
		Notebook notebook = new Notebook();
		notebook.setTabPosition(PositionType.TOP);
		app.setContent(notebook);

		notebook.appendPage(buildBoxPage(), new Label("GtkBox"));
		notebook.appendPage(buildTablePage(), new Label("GtkTable"));
	}
 </programlisting>
</example>

<para>
It is in the <emphasis>buildBoxPage()</emphasis> and <emphasis>
buildTablePage()</emphasis> methods that the real work begins.  
Let's take a peak.
</para>

<sect2 id="box"><title>Box</title>
<para>
The <emphasis>buildBoxPage()</emphasis> method demonstrates the
<emphasis>Box</emphasis> classes.  The method creates a
<emphasis>VBox</emphasis> object and proceeds to create
a series of <emphasis>HBox</emphasis> objects to hold the
widgets that represent a row and add it to the main vbox.  The
only exception to this rule is the first <emphasis>HBox
</emphasis> which also contains a <emphasis>VBox</emphasis>
to contain the two widgets in the upper right corner.  Let's
look at the code.
</para>

<example>
<title>Fourth.java - buildBoxPage()</title>
<programlisting role="Java">
	private Widget buildBoxPage() {

		VBox mainBox = new VBox(false, 0);

		HBox hbox = new HBox(false, 10);
		hbox.setBorderWidth(10);
		mainBox.packStart(hbox, true, true, 0);

		Adjustment adj1 = new Adjustment(0.0, 0.0, 101.0, 0.1, 1.0, 1.0);
		vscale = new VScale(adj1);
		setDefaultValues(vscale);
		hbox.packStart(vscale, true, true, 0);

		VBox vbox = new VBox(false, 10);
		hbox.packStart(vbox, true, true, 0);

		hscale = new HScale(adj1);
		hscale.setMinimumSize(200, 30);
		setDefaultValues(hscale);
		vbox.packStart(hscale, true, true, 0);

		HScrollBar scrollbar = new HScrollBar(adj1);
		scrollbar.setUpdatePolicy(UpdateType.CONTINUOUS);
		vbox.packStart(scrollbar, true, true, 0);

		hbox = new HBox(false, 10);
		hbox.setBorderWidth(10);
		mainBox.packStart(hbox, true, true, 0);

		button = new CheckButton("Display value on scale widgets", false);
		button.setState(true);
		button.addListener(new ButtonListener() {
			public void buttonEvent(ButtonEvent event) {
				if (event.isOfType(ButtonEvent.Type.CLICK))
					drawValue();
			}
		});
		hbox.packStart(button, true, true, 0);

		hbox = new HBox(false, 10);
		hbox.setBorderWidth(10);

		Label label = new Label("Scale Value Position:");
		hbox.packStart(label, false, false, 0);

		valuePosition = new ComboBox();
		valuePosition.appendText("Top");
		valuePosition.appendText("Bottom");
		valuePosition.appendText("Left");
		valuePosition.appendText("Right");
		valuePosition.addListener(new ComboBoxListener() {
			public void comboBoxEvent(ComboBoxEvent event) {
				int index = valuePosition.getActive();
				if (0 == index)
					posMenuSelect(PositionType.TOP);
				else if (1 == index)
					posMenuSelect(PositionType.BOTTOM);
				else if (2 == index)
					posMenuSelect(PositionType.LEFT);
				else
					posMenuSelect(PositionType.RIGHT);
			}
		});
		hbox.packStart(valuePosition, true, true, 0);

		mainBox.packStart(hbox, true, true, 0);

		hbox = new HBox(false, 10);
		hbox.setBorderWidth(10);

		label = new Label("Scale Update Policy:");
		hbox.packStart(label, false, false, 0);

		updatePolicy = new ComboBox();
		updatePolicy.appendText("Continuous");
		updatePolicy.appendText("Discontinuous");
		updatePolicy.appendText("Delayed");
		updatePolicy.addListener(new ComboBoxListener() {
			public void comboBoxEvent(ComboBoxEvent event) {
				int index = updatePolicy.getActive();
				if (0 == index)
					updateMenuSelect(UpdateType.CONTINUOUS);
				else if (1 == index)
					updateMenuSelect(UpdateType.DISCONTINUOUS);
				else 
					updateMenuSelect(UpdateType.DELAYED);
			}
		});
		hbox.packStart(updatePolicy, true, true, 0);

		mainBox.packStart(hbox, true, true, 0);

		hbox = new HBox(false, 10);
		hbox.setBorderWidth(10);

		label = new Label("Scale Digits:");
		hbox.packStart(label, false, false, 0);

		adj = new Adjustment(1.0, 0.0, 5.0, 1.0, 1.0, 0.0);
		adj.addListener(new AdjustmentListener() {
			public void adjustmentEvent(AdjustmentEvent event) {
				digitsScale();
			}
		});
		HScale scale = new HScale(adj);
		scale.setDigits(0);
		hbox.packStart(scale, true, true, 0);

		mainBox.packStart(hbox, true, true, 0);

		return mainBox;
	}
</programlisting>
</example>

</sect2>

<sect2 id="table"><title>Table</title>
<para>
The <emphasis>buildTablePage()</emphasis> method demonstrates the
<emphasis>Table</emphasis> class.  The method builds a 6 X 3
table and proceeds to add the widgets to the grid.
</para>

<example>
<title>Fourth.java - buildBoxPage()</title>
<programlisting role="Java">
	private Widget buildTablePage() {

		Table table = new Table(6, 3, false);

		Adjustment adj1 = new Adjustment(0.0, 0.0, 101.0, 0.1, 1.0, 1.0);
		vscale2 = new VScale(adj1);
		setDefaultValues(vscale2);
		table.attach(vscale2, 0, 1, 0, 2, AttachOptions.FILL, AttachOptions.FILL, 2, 10);

		hscale2 = new HScale(adj1);
		hscale2.setMinimumSize(200, 30);
		setDefaultValues(hscale2);
		table.attach(hscale2, 1, 3, 0, 1, AttachOptions.FILL, AttachOptions.FILL, 5, 5);

		HScrollBar scrollbar = new HScrollBar(adj1);
		scrollbar.setUpdatePolicy(UpdateType.CONTINUOUS);
		table.attach(scrollbar, 1, 3, 1, 2, AttachOptions.FILL, AttachOptions.FILL, 5, 5);

		button2 = new CheckButton("Display value on scale widgets", false);
		button2.setState(true);
		table.attach(button2, 0, 3, 2, 3, AttachOptions.FILL, AttachOptions.FILL, 5, 10);

		Label label = new Label("Scale Value Position:");
		table.attach(label, 0, 2, 3, 4, AttachOptions.FILL, AttachOptions.FILL, 2, 10);

		valuePosition2 = new ComboBox();
		valuePosition2.appendText("Top");
		valuePosition2.appendText("Bottom");
		valuePosition2.appendText("Left");
		valuePosition2.appendText("Right");
		valuePosition2.addListener(new ComboBoxListener() {
			public void comboBoxEvent(ComboBoxEvent event) {
				int index = valuePosition2.getActive();
				if (0 == index)
					posMenuSelect(PositionType.TOP);
				else if (1 == index)
					posMenuSelect(PositionType.BOTTOM);
				else if (2 == index)
					posMenuSelect(PositionType.LEFT);
				else
					posMenuSelect(PositionType.RIGHT);
			}
		});
		table.attach(valuePosition2, 2, 3, 3, 4, AttachOptions.FILL, AttachOptions.FILL, 2, 10);

		label = new Label("Scale Update Policy:");
		table.attach(label, 0, 2, 4, 5, AttachOptions.FILL, AttachOptions.FILL, 2, 10);

		updatePolicy2 = new ComboBox();
		updatePolicy2.appendText("Continuous");
		updatePolicy2.appendText("Discontinuous");
		updatePolicy2.appendText("Delayed");
		updatePolicy2.addListener(new ComboBoxListener() {
			public void comboBoxEvent(ComboBoxEvent event) {
				int index = updatePolicy2.getActive();
				if (0 == index)
					updateMenuSelect(UpdateType.CONTINUOUS);
				else if (1 == index)
					updateMenuSelect(UpdateType.DISCONTINUOUS);
				else 
					updateMenuSelect(UpdateType.DELAYED);
			}
		});
		table.attach(updatePolicy2, 2, 3, 4, 5, AttachOptions.FILL, AttachOptions.FILL, 2, 10);

		label = new Label("Scale Digits:");
		table.attach(label, 0, 1, 5, 6, AttachOptions.FILL, AttachOptions.FILL, 2, 5);

		adj2 = new Adjustment(1.0, 0.0, 5.0, 1.0, 1.0, 0.0);
		HScale scale = new HScale(adj2);
		scale.setDigits(0);
		table.attach(scale, 1, 3, 5, 6, AttachOptions.FILL, AttachOptions.FILL, 3, 5);
		return table;
	}
</programlisting>
</example>

<para>
Laying out widgets for a window is an art that requires a lot of
practice.  Quite often to achieve the best effect you will want
to use a combination of boxes and tables.
</para>
</sect2>
</sect1>

</chapter>


<chapter id="containers">
<title>Containers</title>
<para>
There are widgets in GTK and GNOME whose sole purpose is to contain
other widgets.  This chapter will discuss a few of these widgets.
</para>

<sect1 id="alignment"><title>Alignment</title>
<para>
The <emphasis>Alignment</emphasis> container is designed to hold
a single widget and control the widgets size and position.  The 
widget is positioned in the container a certain percentage of the
distance from the top and left sides of the container.  The widget
is sized a certain percentage of the containers height and width.
Let's look at a small example to demonstrate it usage.
</para>

<example>
<title>Alignment.java - Alignment</title>
<programlisting role="Java">
import org.gnu.gtk.Alignment;
import org.gnu.gtk.Button;
import org.gnu.gtk.Gtk;
import org.gnu.gtk.Window;
import org.gnu.gtk.WindowType;
import org.gnu.gtk.event.LifeCycleEvent;
import org.gnu.gtk.event.LifeCycleListener;

public class AlignmentExample {

	public AlignmentExample() {

		Window window = new Window(WindowType.TOPLEVEL);
		window.setTitle("Alignment Example");
		window.addListener(new LifeCycleListener() {
			public void lifeCycleEvent(LifeCycleEvent event) {}
			public boolean lifeCycleQuery(LifeCycleEvent event) {
				Gtk.mainQuit();
				return false;
			}
		});
		window.setDefaultSize(100, 100);

		Button button = new Button("The Button Label", false);
		// We will create an Alignment and add the button to it.
		// The x alignment is set to 1.0, which keeps the button 
		// against the right edge.  The y alignment is 0.5, keeping
		// the widget vertically centered.  The scale value is set
		// to 0.15 in both directions, which limits the expansion
		// of the button to no more than 15 percent of the area 
		// made available to it.
		Alignment alignment = new Alignment(1.0, 0.5, 0.15, 0.15);
		alignment.add(button);
		window.add(alignment);

		window.showAll();
	}

	public static void main(String[] args) {
		// Initialize GTK 
		Gtk.init(args);

		AlignmentExample align = new AlignmentExample();

		Gtk.main();
	}
}
</programlisting>
</example>

</sect1>

<sect1 id="frame"><title>Frame</title>
<para>
The <emphasis>Frame</emphasis> container is capable of containing
one widget and it places a rectangle box around it.  You can also
have a label attached to the box.  The box can have different
appearances which is described by type <emphasis>ShadowType
</emphasis>.  The possible vales for <emphasis>ShadowType
</emphasis> are as follows:
</para>


<table frame="all"><title>ShadowType</title>
<tgroup cols="1" align="left" colsep="1" rowsep="1">
<tbody>
  <row>
    <entry>NONE</entry>
  </row>
  <row>
    <entry>IN</entry>
  </row>
  <row>
    <entry>OUT</entry>
  </row>
  <row>
    <entry>ETCHED_IN</entry>
  </row>
  <row>
    <entry>ETCHED_OUT</entry>
  </row>
</tbody>
</tgroup>
</table>

<para>
Here is a little bit of the <emphasis>createLabels()</emphasis>
method from <emphasis>TestGTK</emphasis> to demonstrate the usage
of <emphasis>Frame</emphasis>.
</para>
<example>
<title>TestGTK.java - Frame</title>
<programlisting role="Java">
	public void createLabels() {
		Window win = new Window(WindowType.TOPLEVEL);
		win.setTitle("labels");
		win.setBorderWidth(5);

		VBox vbox1 = new VBox(false, 5);
		HBox hbox = new HBox(false, 5);
		win.add(hbox);
		hbox.packStart(vbox1, false, false, 0);

		Frame frame = new Frame("Normal Label");
		Label label = new Label("This is a Normal Label");
		frame.add(label);
		vbox1.packStart(frame, false, false, 0);

		frame = new Frame("Multi-Line Label");
		label = new Label("This is a Multi-Line Label. \nSecond line\nThird line");
		frame.add(label);
		vbox1.packStart(frame, false, false, 0);

		frame = new Frame("Left Justiied Label");
		label = new Label("This is a Left-Justified\nMulti-line label.\nThird line");
		label.setJustification(Justification.LEFT);
		frame.add(label);
		vbox1.packStart(frame, false, false, 0);
</programlisting>
</example>
</sect1>


<sect1 id="aspectframe"><title>AspectFrame</title>
<para>
The <emphasis>AspectFrame</emphasis> container contains one
widget and forces it to maintain the same aspect ration no
matter how large the window gets resized.  It extends <emphasis>
Frame</emphasis> so all of the border types are valid for this
type as well.  Let's look at an example.
</para>
<example>

<title>aspectframe.java - AspectFrame</title>
<programlisting role="Java">
import org.gnu.gtk.AspectFrame;
import org.gnu.gtk.DrawingArea;
import org.gnu.gtk.Gtk;
import org.gnu.gtk.Window;
import org.gnu.gtk.WindowType;
import org.gnu.gtk.event.LifeCycleEvent;
import org.gnu.gtk.event.LifeCycleListener;

public class aspectframe {

	public static void main(String[] args) {
		Gtk.init(args);

		Window window = new Window(WindowType.TOPLEVEL);
		window.setTitle("Aspect Fame");
		window.setBorderWidth(10);
		window.addListener(new LifeCycleListener() {
			public void lifeCycleEvent(LifeCycleEvent event) {}
			public boolean lifeCycleQuery(LifeCycleEvent event) {
				Gtk.mainQuit();
				return false;
			}
		});

		// Create an AspectFrame and add it to our toplevel window
		AspectFrame aspect_frame = new AspectFrame(
				"2x1", // label
				0.5, // center x
				0.5, // center y
				2, // xsize/yxsize = 2
				false); // ignore childs aspect

		window.add(aspect_frame);
		aspect_frame.show();

		// Now add a child widget to the AspectFrame
		DrawingArea drawing_area = new DrawingArea();

		// Ask for a 200x200 window, but the AspectFrame will give us a 200x100
		// window since we are forcing a 2x1 aspect ratio
		drawing_area.setMinimumSize(200, 200);
		aspect_frame.add(drawing_area);
		drawing_area.show();

		window.show();
		Gtk.main();
	}
}
</programlisting>
</example>
</sect1>

<sect1 id="paned"><title>Paned</title>
<para>
The <emphasis>Paned</emphasis> container can hold two widgets
either side by side or one above the other with a divider between
them.  The divider has a button on it that can be used to resize 
the two widgets.  Below is an example.
</para>

<example>
<title>Paned.java - Paned</title>
<programlisting role="Java">
import org.gnu.gtk.AttachOptions;
import org.gnu.gtk.Gtk;
import org.gnu.gtk.PolicyType;
import org.gnu.gtk.ScrolledWindow;
import org.gnu.gtk.SimpleList;
import org.gnu.gtk.Table;
import org.gnu.gtk.TextBuffer;
import org.gnu.gtk.TextView;
import org.gnu.gtk.VPaned;
import org.gnu.gtk.Window;
import org.gnu.gtk.WindowType;
import org.gnu.gtk.event.LifeCycleEvent;
import org.gnu.gtk.event.LifeCycleListener;

public class PanedExample {

	TextView text = null;
	TextBuffer textBuffer = null;

	public PanedExample() {

		Window window = new Window(WindowType.TOPLEVEL);
		window.setTitle("Panned Windows");
		window.setBorderWidth(10);
		window.setDefaultSize(450, 400);
		window.addListener(new LifeCycleListener() {
			public void lifeCycleEvent(LifeCycleEvent event) {}
			public boolean lifeCycleQuery(LifeCycleEvent event) {
				Gtk.mainQuit();
				return false;
			}
		});

		VPaned vpaned = new VPaned();
		window.add(vpaned);
		vpaned.show();

		ScrolledWindow list = createList();
		vpaned.add1(list);
		list.show();

		Table table = createText();
		vpaned.add2(table);
		table.show();

		window.show();
	}

	// create the list of messages
	public ScrolledWindow createList() {
		// Create a new scrolled window with scrollbars only if needed.
		ScrolledWindow scrolledWindow = new ScrolledWindow(null, null);
		scrolledWindow.setPolicy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);

		// Create a new list and put it in the scrolled window
		SimpleList list = new SimpleList();
		scrolledWindow.addWithViewport(list);
		list.show();

		// Add some messages to the window
		for (int i = 0; i < 10; i++) {
			list.addEnd("Message #" + i);
		}
		return scrolledWindow;
	}

	// Create a scrolled text area that displays a "message"
	public Table createText() {
		// Create a table to hold the widget and scrollbars
		Table table = new Table(2, 2, false);

		// Put a text widget in the upper left hand corner.  Note the
		// use of _SHRINK in the y direction.
		textBuffer = new TextBuffer();
		text = new TextView(textBuffer);
		table.attach(
			text,
			0,
			1,
			0,
			1,
			AttachOptions.EXPAND.or(AttachOptions.FILL),
			AttachOptions.EXPAND.or(AttachOptions.FILL.or(AttachOptions.SHRINK)),
			0,
			0);
		String str =
			"From: pathfinder@nasa.gov\n"
				+ "To: mom@nasa.gov\n"
				+ "\n"
				+ "We just got in this morning. The weather has been\n"
				+ "great - clear but cold, and there are lots of fun sights.\n"
				+ "Sojourner says hi. See you soon.\n"
				+ " -Path\n";
		textBuffer.insertText(str);
		text.show();

		return table;
	}

	public static void main(String[] args) {
		// Initialize GTK 
		Gtk.init(args);

		PanedExample panned = new PanedExample();

		Gtk.main();
	}
}
</programlisting>
</example>
</sect1>

<sect1 id="scrolledwindow"><title>ScrolledWindow</title>
<para>
A <emphasis>ScrolledWindow</emphasis> displays a portion
of a larger window through a view with scrollbars.  The user can
use the scrollbars to view the entirety of the child window.
Let's take a look at an example from <emphasis>TestGTK</emphasis>.
</para>

<example>
<title>TestGTK.java - ScrolledWindow</title>
<programlisting role="Java">
	public void createScrolledWindows() {
		final Dialog win = new Dialog();
		win.setTitle("Scrolled Windows");
		win.setDefaultSize(200, 200);

		ScrolledWindow scrolled_window = new ScrolledWindow(null, null);
		scrolled_window.setBorderWidth(10);
		scrolled_window.setPolicy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
		((Box) (win.getDialogLayout())).packStart(scrolled_window);
		scrolled_window.show();

		Table table = new Table(20, 20, true);
		table.setRowSpacing(10);
		table.setColumnSpacing(10);
		scrolled_window.addWithViewport(table);
		table.show();

		for (int i = 0; i < 20; i++)
			for (int j = 0; j < 20; j++) {
				ToggleButton button =
					new ToggleButton("button (" + i + "," + j + ")", false);
				table.attach(button, i, i + 1, j, j + 1,
					AttachOptions.EXPAND.or(AttachOptions.FILL),
					AttachOptions.EXPAND.or(AttachOptions.FILL),
					0, 0);
				button.show();
			}
		Button button = new Button("close", false);
		button.addListener(new ButtonListener() {
			public void buttonEvent(ButtonEvent event) {
				if (event.isOfType(ButtonEvent.Type.CLICK)) {
					win.destroy();
				}
			}
		});
		((Box) win.getActionArea()).packStart(button, false, false, 10);
		button.show();
		win.show();
		win.run();
	}
</programlisting>
</example>
</sect1>

<sect1 id="notebook"><title>Notebook</title>
<para>
The <emphasis>Notebook</emphasis> container contains numerous
windows.  Each window is accessed by selecting a tab with a label
identifying the window.  Below is a complete example.
</para>

<example>
<title>Notebook.java - Notebook</title>
<programlisting role="Java">
import org.gnu.gtk.Button;
import org.gnu.gtk.CheckButton;
import org.gnu.gtk.Frame;
import org.gnu.gtk.Gtk;
import org.gnu.gtk.Label;
import org.gnu.gtk.Notebook;
import org.gnu.gtk.PositionType;
import org.gnu.gtk.Table;
import org.gnu.gtk.Window;
import org.gnu.gtk.WindowType;
import org.gnu.gtk.event.ButtonEvent;
import org.gnu.gtk.event.ButtonListener;
import org.gnu.gtk.event.LifeCycleEvent;
import org.gnu.gtk.event.LifeCycleListener;
import org.gnu.gtk.event.NotebookEvent;
import org.gnu.gtk.event.NotebookListener;

public class NotebookExample implements LifeCycleListener {

	protected Notebook notebook = null;
	private int tabPosition = 1;
	private boolean displayTabs = true;
	private boolean displayBorder = false;

	public NotebookExample() {

		Window window = new Window(WindowType.TOPLEVEL);
		window.setBorderWidth(10);

		Table table = new Table(3, 6, false);
		window.add(table);

		// Create a new notebook, place the position of the tabs
		notebook = new Notebook();
		notebook.setTabPosition(PositionType.TOP);
		table.attach(notebook, 0, 6, 0, 1);
		notebook.show();

		// Let's append a bunch of pages to the notebook
		for (int i = 0; i < 5; i++) {
			String bufferf = "Append Frame " + (i + 1);
			String bufferl = "Page " + (i + 1);

			Frame frame = new Frame(bufferf);
			frame.setBorderWidth(10);
			frame.setMinimumSize(100, 75);
			frame.show();

			Label lbl = new Label(bufferf);
			frame.add(lbl);
			lbl.show();

			lbl = new Label(bufferl);
			notebook.appendPage(frame, lbl);
		}

		// Now let's add a page to a specific spot
		CheckButton checkButton = new CheckButton("Click me please!", false);
		checkButton.setMinimumSize(100, 75);
		checkButton.show();

		Label label = new Label("Add page");
		notebook.insertPage(checkButton, label, 2);

		// Now finally let's perpend pages to the notebook
		for (int i = 0; i < 5; i++) {
			String bufferf = "Prepend Frame " + (i + 1);
			String bufferl = "PPage " + (i + 1);

			Frame frame = new Frame(bufferf);
			frame.setBorderWidth(10);
			frame.setMinimumSize(100, 75);
			frame.show();

			Label lbl = new Label(bufferf);
			frame.add(lbl);
			lbl.show();

			lbl = new Label(bufferl);
			notebook.prependPage(frame, lbl);
		}

		// Set what page to start at (page 4)
		notebook.setCurrentPage(3);
		notebook.addListener(new NotebookListener() {
			public void notebookEvent(NotebookEvent event) {
				if (event.isOfType(NotebookEvent.Type.SWITCH_PAGE)) {
					System.out.println(
						"User just selected page " + event.getPageNumber());
				}
			}
		});

		// Quit this application.
		Button button = new Button("close", false);
		button.addListener(new ButtonListener() {
			public void buttonEvent(ButtonEvent event) {
				if (event.isOfType(ButtonEvent.Type.CLICK)) {
					Gtk.mainQuit();
				}
			}
		});
		table.attach(button, 0, 1, 1, 2);
		button.show();

		// Move the Notebook to the next page.
		button = new Button("next page", false);
		button.addListener(new ButtonListener() {
			public void buttonEvent(ButtonEvent event) {
				if (event.isOfType(ButtonEvent.Type.CLICK)) {
					notebook.nextPage();
				}
			}
		});
		table.attach(button, 1, 2, 1, 2);
		button.show();

		// Move the Notebook to the previous page.
		button = new Button("prev page", false);
		button.addListener(new ButtonListener() {
			public void buttonEvent(ButtonEvent event) {
				if (event.isOfType(ButtonEvent.Type.CLICK)) {
					notebook.prevPage();
				}
			}
		});
		table.attach(button, 2, 3, 1, 2);
		button.show();

		// Change the position of the tabs on this Notebook
		button = new Button("tab position", false);
		button.addListener(new ButtonListener() {
			public void buttonEvent(ButtonEvent event) {
				if (event.isOfType(ButtonEvent.Type.CLICK)) {
					if (tabPosition == 4)
						tabPosition = 0;
					notebook.setTabPosition(PositionType.intern(++tabPosition));
				}
			}
		});
		table.attach(button, 3, 4, 1, 2);
		button.show();

		// Display/hode the tabs and border.
		button = new Button("tabs/border on/off", false);
		button.addListener(new ButtonListener() {
			public void buttonEvent(ButtonEvent event) {
				if (event.isOfType(ButtonEvent.Type.CLICK)) {
					displayTabs = !displayTabs;
					displayBorder = !displayBorder;
					notebook.setShowTabs(displayTabs);
					notebook.setShowBorder(displayBorder);
				}
			}
		});
		table.attach(button, 4, 5, 1, 2);
		button.show();

		// Remove the currently selected page.
		button = new Button("remove page", false);
		button.addListener(new ButtonListener() {
			public void buttonEvent(ButtonEvent event) {
				if (event.isOfType(ButtonEvent.Type.CLICK)) {
					int page = notebook.getCurrentPage();
					notebook.removePage(page);
				}
			}
		});
		table.attach(button, 5, 6, 1, 2);
		button.show();

		table.show();

		window.show();
	}

	// Method to cause the application to exit
	public void lifeCycleEvent(LifeCycleEvent event) {
		if (event.isOfType(LifeCycleEvent.Type.DESTROY) || 
			event.isOfType(LifeCycleEvent.Type.DELETE)) {
			Gtk.mainQuit();
		}
	}

	public static void main(String[] args) {
		// Initialize GTK 
		Gtk.init(args);

		NotebookExample notebook = new NotebookExample();

		Gtk.main();
	}
}
</programlisting>
</example>
</sect1>

<sect1 id="buttonbox"><title>ButtonBox</title>
<para>
The <emphasis>ButtonBox</emphasis> container is designed to
contain a group of buttons.  An example from <emphasis>TestGTK
</emphasis> is below.
</para>

<example>
<title>TestGTK.java - ButtonBox</title>
<programlisting role="Java">
	private Frame createButtonBox(boolean horizontal, String title, int spacing, 
		int childWidth, int childHeight, ButtonBoxStyle layout) {
		ButtonBox box;

		Frame frame = new Frame(title);
		if (horizontal) {
			box = new HButtonBox();
		} else {
			box = new VButtonBox();
		}

		box.setBorderWidth(5);
		frame.add(box);

		box.setLayout(layout);
		box.setSpacing(spacing);

		Button button;
		button = new Button("OK", false);
		box.add(button);
		button = new Button("Cancel", false);
		box.add(button);
		button = new Button("Help", false);
		box.add(button);

		return frame;
	}
</programlisting>
</example>
</sect1>

</chapter>

<chapter id="non-visual">
<title>Non-Visual Classes</title>
<para>
GNOME is comprised of numerous libraries that provide the APIs
used by developers.  The two main libraries are <emphasis>libgnomeui
</emphasis> and <emphasis>libgnome</emphasis>.  <emphasis>libgnomeui
</emphasis> contains the graphical user interface functionality while 
<emphasis>libgnome</emphasis> contains common routines that may be
used by your application.  In this chapter we will look at a few
of the classes that wrap functionality contained in <emphasis>
libgnome</emphasis>.  To get the entire API for each of these classes
please refer to the javadocs.
</para>

<sect1 id="config"><title>Config</title>
<para>
It is possible to save and retrieve configuration information to
a file from your GNOME application.  You can use this capability
to store user preferences like fonts, colors, size, etc. or you
can use this to store userid and passwords.  The information
is stored in a file in one of two locations depending on the
methods used.  The two files are ${HOME}/.gnome/"appname" and 
${HOME}/.gnome_private/"appname".  The .gnome directory is
viewable to the world while the .gnome_private directory is only
viewable by the user of the application.
</para>

<para>
To demonstrate the usage of the <emphasis>Config</emphasis>
class we have two small examples.
</para>

<example>
<title>WriteConfig.java - Config</title>
<programlisting role="Java">
import org.gnu.gnome.Config;
import org.gnu.gnome.Program;

public class WriteConfig {

	public WriteConfig() {

		// items to appear in .gnome directory
		Config.setString("writeconfig/cool/bindings", "java-gnome");
		Config.setInt("writeconfig/cool/width", 16);
		Config.setDouble("writeconfig/cool/version", 0.6);
		Config.setBoolean("writeconfig/cool/very_cool", true);

		// items to appear in .gnome_private directory
		Config.setPrivateString("writeconfig/user/password", "javaman");
		Config.setPrivateInt("writeconfig/user/age", 2);
		Config.setPrivateDouble("writeconfig/user/salary", 100.10);
		Config.setPrivateBoolean("writeconfig/user/is_lazy", true);

		Config.sync();
	}

	public static void main(String[] args) {

		Program.initGnome("WriteConfig Example", "1.0", args);

		WriteConfig write = new WriteConfig();

	}
}
</programlisting>
</example>

<para>
This simple example will write information to two separate files.
The <emphasis>Config.set...()</emphasis> series of methods
will write information to the ${HOME}/.gnome/writeconfig file
while the <emphasis>Config.setPrivate...()</emphasis> series
of methods will write information to the ${HOME}/.gnome_private/writeconfig
file.  The information in the .gnome file is placed under a section
with the heading [cool] while the information in the .gnome_private
file is placed under a section with the heading [user].  After all
of the data is set a call is placed to the <emphasis>Config.sync()
</emphasis> method to flush all of the information to disk.  To read this
information you could use the following example:
</para>

<example>
<title>ReadConfig.java - Config</title>
<programlisting role="Java">
import org.gnu.gnome.Config;
import org.gnu.gnome.Program;

public class ReadConfig {

	public ReadConfig() {

		// items to appear in .gnome directory
		System.out.println("Bindings = " + 
			Config.getString("writeconfig/cool/bindings"));
		System.out.println("Width = " + 
			Config.getInt("writeconfig/cool/width"));
		System.out.println("Version = " + 
			Config.getDouble("writeconfig/cool/version"));
		System.out.println("Very Cool = " + 
			Config.getBoolean("writeconfig/cool/very_cool"));

		// items to appear in .gnome_private directory
		System.out.println("Private password = " + 
			Config.getPrivateString("writeconfig/user/password"));
		System.out.println("Private age = " + 
			Config.getPrivateInt("writeconfig/user/age"));
		System.out.println("Private salary = " + 
			Config.getPrivateDouble("writeconfig/user/salary"));
		System.out.println("Private is lazy = " + 
			Config.getPrivateBoolean("writeconfig/user/is_lazy"));
	}

	public static void main(String[] args) {

		Program.initGnome("ReadConfig Example", "1.0", args);
		ReadConfig read = new ReadConfig();
	}
}
</programlisting>
</example>

<para>
This example will read and print the values written to the
configuration files by the previous example.  
</para>
</sect1>

<sect1 id="sound"><title>Sound</title>
<para>
Java-GNOME provides the ability to play sounds through the
<emphasis>Sound</emphasis> class.  This class is so simple
to use that an example will be the best demonstration.
</para>

<example>
<title>Sound.java - Sound</title>
<programlisting role="Java">
import org.gnu.gnome.Program;
import org.gnu.gnome.Sound;

public class SoundTest {

	public SoundTest() {

		Sound.play("startup.wav");
		Sound.shutdown();
	}

	public static void main(String[] args) {

		Program.initGnome("Sound Example", "1.0", args);

		SoundTest sound = new SoundTest();

	}
}
</programlisting>
</example>

<para>
As you might guess, this example plays a file called "startup.wav".
Before you exit the application you should place a call to <emphasis>
Sound.shutdown()</emphasis> to free up the resources taken
by the sound daemon.
</para>
</sect1>

<sect1 id="url"><title>Url</title>
<para>
<emphasis>Url</emphasis> provides the capability to display
the contents of a url in the registered browser.  There is only
one static method in thic class.  An example follows:
</para>

<example>
<title>UrlTest.java - Url</title>
<programlisting role="Java">
import org.gnu.gnome.Program;
import org.gnu.gnome.Url;

public class UrlTest {

	public UrlTest() {

		Url.show("http://java-gnome.sourceforge.net");
	}

	public static void main(String[] args) {

		Program.initGnome("Url Example", "1.0", args);

		UrlTest url = new UrlTest();

	}
}
</programlisting>
</example>

<para>
This simple example will display the Java-GNOME home page in
the registered browser.
</para>
</sect1>
</chapter>

<chapter id="widgets">
<title>The Widgets</title>
<para>
It is probably about time to start talking about
the widgets that are available in Java-GNOME.  There
are so many widgets that I will not be able to cover
them all.  Instead I will just focus on a few
of the most used and most complex.
A good resource to learn about other widgets 
available in GTK is the GTK tutorial which is on 
the gtk <ulink url="http://www.gtk.org">web site</ulink>.
Although it discusses the C implimentation it is very
useful for Java-GNOME developers since there are
many similarities.
</para>

<sect1 id="entry"><title>Gtk Entry</title>
<para>
The <emphasis>Entry</emphasis> widget in GTK is a sinle line text entry
widget.  A fairly large set of key bindings are supported by default.  If
the entered text is longer than the allocation of the widget, the widget
will scroll so that the cursor position is visible.  It fires three <emphasis>
EditableEvent</emphasis> events; <emphasis>changed</emphasis>, <emphasis>
delete_text</emphasis>, and <emphasis>insert_text</emphasis>.
</para>

<para>
Below is a very brief example of how you can create an <emphasis>Entry
</emphasis> widget.
</para>

<example>
<title>EntryDemo.java - Gtk Entry</title>
<programlisting role="Java">
import org.gnu.gtk.Entry;
import org.gnu.gtk.Gtk;
import org.gnu.gtk.HBox;
import org.gnu.gtk.Widget;
import org.gnu.gtk.Window;
import org.gnu.gtk.WindowType;
import org.gnu.gtk.event.LifeCycleEvent;
import org.gnu.gtk.event.LifeCycleListener;

public class EntryDemo {

	public static Widget createEntryWidget(){
		HBox b = new HBox(true,0);
		Entry entry = new Entry();
		entry.setText("Entry widget demo.");
		entry.setVisible(true);
		b.add(entry);
		return b;
	}
		
	public static void main(String[] args) {
		Gtk.init(args);
		Window w = new Window(WindowType.TOPLEVEL);
		w.addListener(new LifeCycleListener() {
			public void lifeCycleEvent(LifeCycleEvent event) {}
			public boolean lifeCycleQuery(LifeCycleEvent event) {
				Gtk.mainQuit();
				return false;
			}
		});
		w.setDefaultSize(200,30);
		w.setBorderWidth(5);
		w.setTitle("The Java-Gnome team");
		w.add(createEntryWidget());
		w.showAll();
		Gtk.main();
	}
}
</programlisting>
</example>
</sect1>

<sect1 id="checkbutton"><title>CheckButton</title>
<para>
A <emphasis>CheckButton</emphasis> widget displays a small button 
with a label next to it. The button can be toggled on or off by the 
mouse, and will retain its state until it is toggled again.  This
widget fires a <emphasis>ToggleEvent</emphasis> when it's state
changes.
</para>

<para>
The following is an example that demonstrates how to create and
handle the events for a <emphasis>CheckButton</emphasis>
</para>

<example>
<title>CheckButtonExample.java - CheckButton</title>
<programlisting role="Java">
import org.gnu.gtk.CheckButton;
import org.gnu.gtk.Gtk;
import org.gnu.gtk.VBox;
import org.gnu.gtk.Window;
import org.gnu.gtk.WindowType;
import org.gnu.gtk.event.LifeCycleEvent;
import org.gnu.gtk.event.LifeCycleListener;
import org.gnu.gtk.event.ToggleEvent;
import org.gnu.gtk.event.ToggleListener;

public class CheckButtonExample implements LifeCycleListener {
	CheckButton checkButton1;
	CheckButton checkButton2;
	CheckButton checkButton3;

	public CheckButtonExample() {

		Window window = new Window(WindowType.TOPLEVEL);
		window.setTitle("CheckButton Example");
		window.addListener(this);
		window.setBorderWidth(20);

		VBox vbox = new VBox(false, 10);
		window.add(vbox);

		checkButton1 = new CheckButton("Disable Button 3", false);
		checkButton1.addListener(new ToggleListener() {
			public void toggleEvent(ToggleEvent event) {
				if (checkButton1.getState())
					checkButton3.setSensitive(false);
				else
					checkButton3.setSensitive(true);
			}
		});
		vbox.packStart(checkButton1, true, true, 0);

		checkButton2 = new CheckButton("Disable Button 1", false);
		checkButton2.addListener(new ToggleListener() {
			public void toggleEvent(ToggleEvent event) {
				if (checkButton2.getState())
					checkButton1.setSensitive(false);
				else
					checkButton1.setSensitive(true);
			}
		});
		vbox.packStart(checkButton2, true, true, 0);

		checkButton3 = new CheckButton("Say Hello", false);
		checkButton3.addListener(new ToggleListener() {
			public void toggleEvent(ToggleEvent event) {
				if (checkButton2.getState())
					System.out.println("Hello");
			}
		});
		vbox.packStart(checkButton3, true, true, 0);

		window.showAll();
	}

	public void lifeCycleEvent(LifeCycleEvent event) {
		if (event.isOfType(LifeCycleEvent.Type.DESTROY) || 
			event.isOfType(LifeCycleEvent.Type.DELETE)) {
			Gtk.mainQuit();
		}
	}

	public static void main(String[] args) {
		Gtk.init(args);
		CheckButtonExample cb = new CheckButtonExample();
		Gtk.main();
	}
}
</programlisting>
</example>
</sect1>

<sect1 id="label"><title>Label</title>
<para>
The <emphasis>Label</emphasis> widget displays a small amount of text. As the 
name implies, most labels are used to label another widget such as a <emphasis>
Button</emphasis>, a <emphasis>MenuItem</emphasis>, or a <emphasis>
OptionMenu</emphasis>.
</para>

<para>
<emphasis>Labels</emphasis> may contain mnemonics. Mnemonics are underlined 
characters in the label, used for keyboard navigation. Mnemonics are created by 
providing a string with an underscore before the mnemonic character, such as "_File", 
to the constructor or <emphasis>setText</emphasis> method, with the 
<emphasis>hasMnemonic</emphasis> parameter true. 
</para>

<para>
Mnemonics automatically activate any activatable widget the label is 
inside, such as a <emphasis>Button</emphasis>; if the label is not inside the 
mnemonic's target widget, you have to tell the label about the target using 
setMnemonicWidget.
</para>

<para>
To make it easy to format text in a <emphasis>Label</emphasis> (changing 
colors, fonts, etc.), label text can be provided in a simple markup format. Here's 
how to create a label with a small font:
</para>

<example>
<title>Label with Markup</title>
<programlisting role="Java">
	label = new Label("");
 	label.setMarkup("&lt;small&gt;Small text\&lt;/small&gt;");
</programlisting>
</example>
 
<para>
Markup strings are just a convenient way to set the <emphasis>PangoAttrList
</emphasis> on a <emphasis>Label</emphasis>; <emphasis>setAttributes
</emphasis> may be a simpler way to set attributes in some cases. 
Be careful though; <emphasis>PangoAttrList</emphasis> tends to cause 
internationalization problems, unless you're applying attributes to the entire string . The 
reason is that specifying the startIndex and endIndex for a <emphasis>PangoAttribute 
</emphasis> requires knowledge of the exact string being displayed, so translations 
will cause problems.
</para>

<para>
Labels can be made selectable with <emphasis>setSelectable</emphasis>. 
Selectable labels allow the user to copy the label contents to the clipboard. Only labels 
that contain useful-to-copy information - such as error messages - should 
be made selectable.
</para>

<para>
A label can contain any number of paragraphs, but will have performance 
problems if it contains more than a small number. Paragraphs are separated 
by newlines or other paragraph separators understood by <emphasis>Pango
</emphasis>.  Labels can automatically wrap text if you call <emphasis>setLineWrap
</emphasis>.  <emphasis>setJustify</emphasis> sets how the lines in a label 
align with one another. If you want to set how the label as a whole aligns in its 
available space, see <emphasis>Misc.setAlignment</emphasis>.
</para>
</sect1>

<sect1 id="combo"><title>ComboBox</title>
<para>
The <emphasis>ComboBox</emphasis> widget displays a single item and provides a 
pull-down list of items that can be selected. The drop-down list is displayed when
the user clicks on a small arrow button to the right of the entry field.
</para>
</sect1>

<sect1 id="dateedit"><title>DateEdit</title>
<para>
 A control which allows the user to select a date.
</para>
</sect1>

<sect1 id="about"><title>About</title>
<para>
The <emphasis>About</emphasis> widget dialog window is designed to pop up from the
About button on an application menu. Its purpose is to standardize the form and content.
</para>
</sect1>

<sect1 id="treeview"><title>TreeView</title>
<para>
The <emphasis>TreeView</emphasis> object is a widget for displaying trees and lists. 
The sole purpose of this widget is for displaying the data on the screen and setting
other parts of the view using the associated objects. 
</para>

<para>
The standard tree and list widgets in Gtk are very powerful, but somewhat
 complex. For your conveniance, the Java-Gnome project has derived a number of
 simpler classes for common uses of these widgets (ex. SimpleList).
 If you choose not to use those, or your requirements are such that you
 cannot use them, you will have to learn how to use the full objects. 
 </para>
 
 <para>
There is only one widget which is placed in any applications to create
trees, lists and tables. This is the <emphasis>TreeView</emphasis>. An application can
have any number of treeviews and they can be placed as can normal widgets.
The data for the widget, and the method in which it is displayed is
controlled by other classes. Gtk has been designed so that any number of
treeview widgets can be linked to the same data store.  <emphasis>TreeViewColumn
</emphasis>s, <emphasis>CellRenderer</emphasis>s and <emphasis>TreeSelection
</emphasis>s are created for each view, so Views can use the same data store 
but have their own column layout, data display within those columns (linked to any 
of the <emphasis>dataColumns</emphasis> in the store); and their own selections.
</para>

<para>
Models are used to store data. Data is stored in what could be considered
a table. There are a number of <emphasis>DataColumn</emphasis>s, which could 
be considered as data columns (in fact, in the C version of gtk, they are always refered 
to as columns; but this can get confused with <emphasis>TreeViewColumns
</emphasis> which are quite a different matter). These <emphasis>dataColumns</emphasis>
each store one type of data (String, boolean, int, etc.). The 'rows' of the data table, 
or the individual records in the data store are accessible using iterators called <emphasis>
TreeIter</emphasis>s. These are used extensively in many methods. Setting data 
involves getting an iterator (usually be creating a new row) and then setting the
value for each of the <emphasis>dataColumns</emphasis>. The ordering of these 
dataColumns has absolutely no meaning - you can decide exactly which blocks are used on
screen by setting Attribute mappings to the <emphasis>CellRenderer</emphasis>s 
which render data on the screen (see below).
</para>

<para>
Currently, there are two implementations of <emphasis>TreeModel</emphasis>.
<emphasis>ListStore</emphasis> is used for tables and lists. Data is organised
in rows and columns.  <emphasis>TreeStore</emphasis> is for the hierarchical 
trees. Data is organised using <emphasis>TreePath</emphasis>'s.
</para>

<para>
Both trees and lists can have multiple columns of data. Columns are added
and removed using the <emphasis>TreeView</emphasis> class. Columns are objects which
determine how the data is displayed. They have settings such as the column
title, whether the column can be resized, and even whether the columns can be
reorganized (by dragging the columns). Each <emphasis>TreeView</emphasis>
widget has it's own set of columns. Determining how the data is displayed in the 
columns is done by <emphasis>CellRenderers</emphasis> (see below). Any number 
of renderers can be added to the same column.
</para>

<para>
Tree and list `cells' may contain a large variety of data types.
Determining how they are displayed is done by the <emphasis>CellRenderer</emphasis>
family of classes. If the data is unusual, or you want to combine a number of data
types in a single column, you may construct your own renderer. However, you
are recommended to stick with the regular choices.
</para>

<para>
The <emphasis>CellRenderer</emphasis>'s need data to be able to display. This is 
set using <emphasis>TreeViewColumn.addAttributeMapping(CellRenderer,
CellRendererAttribute, int)</emphasis>. The renderer attributes vary with each
renderer, for example <emphasis>CellRendererText</emphasis> has a <emphasis>
TEXT</emphasis> attribute for the text the be displayed. The final parameter is 
for the dataColumn in the store in which the data is contained.
</para>
</sect1>

<sect1 id="textview"><title>TextView</title>
<para>
java-gnome has an extremely powerful framework for multiline text editing. The
primary objects involved in the process are <emphasis>TextBuffer</emphasis>, 
which represents the text being edited, and <emphasis>TextView</emphasis>, 
a widget which can display a TextBuffer. Each buffer can be displayed by any 
number of views.
</para>

<para>
One of the important things to remember about text in java-gnome is that it's in
the UTF-8 encoding. This means that one character can be encoded as multiple
bytes. Character counts are usually referred to as offsets, while byte counts
are called indexes. If you confuse these two, things will work fine with
ASCII, but as soon as your buffer contains multibyte characters, bad things
will happen.
</para>

<para>
Text in a buffer can be marked with tags. A tag is an attribute that can
be applied to some range of text. For example, a tag might be called "bold"
and make the text inside the tag bold. However, the tag concept is more
general than that; tags don't have to affect appearance. They can instead
affect the behavior of mouse and key presses, "lock" a range of text so the
user can't edit it, or countless other things. A tag is represented by a
<emphasis>TextTag</emphasis> object. One <emphasis>TextTag</emphasis>
can be applied to any number of text ranges in any number of buffers.
</para>

<para>
Each tag is stored in a <emphasis>TextTagTable</emphasis>. A tag table defines a set of
tags that can be used together. Each buffer has one tag table associated with
it; only tags from that tag table can be used with the buffer. A single tag
table can be shared between multiple buffers, however.
</para>

<para>
Tags can have names, which is convenient sometimes (for example, you can
name your tag that makes things bold "bold"), but they can also be anonymous
(which is convenient if you're creating tags on-the-fly).
</para>

<para>
Most text manipulation is accomplished with iterators, represented by a
<emphasis>TextIter</emphasis>. An iterator represents a position between two 
characters in the text buffer. Iterators are not valid indefinitely; whenever the buffer is
modified in a way that affects the number of characters in the buffer, all
outstanding iterators become invalid. (Note that deleting 5 characters and
then reinserting 5 still invalidates iterators, though you end up with the
same number of characters you pass through a state with a different number).
</para>

<para>
Because of this, iterators can't be used to preserve positions across
buffer modifications. To preserve a position, the <emphasis>TextMark</emphasis>
object is ideal. You can think of a mark as an invisible cursor or insertion point; it
floats in the buffer, saving a position. If the text surrounding the mark is
deleted, the mark remains in the position the text once occupied; if text is
inserted at the mark, the mark ends up either to the left or to the right of
the new text, depending on its gravity. The standard text cursor in
left-to-right languages is a mark with right gravity, because it stays to the
right of inserted text.
</para>

<para>
Like tags, marks can be either named or anonymous. There are two marks
built-in to <emphasis>TextBuffer</emphasis>; these are named <emphasis>
insert</emphasis> and <emphasis>selection_bound</emphasis> and refer to 
the insertion point and the boundary of the selection which is not the insertion 
point, respectively. If no text is selected, these two marks will be in the same 
position. You can manipulate what is selected and where the cursor appears 
by moving these marks around.
</para>

<para>
Text buffers always contain at least one line, but may be empty (that is,
buffers can contain zero characters). The last line in the text buffer never
ends in a line separator (such as newline); the other lines in the buffer
always end in a line separator. Line separators count as characters when
computing character counts and character offsets. Note that some Unicode line
separators are represented with multiple bytes in UTF-8, and the
two-character sequence "\r\n" is also considered a line separator.
</para>
</sect1>

</chapter>

</book>
