boring GtkTutorial stuff..




Hi again,

Yes, I stayed up half the night writing more on the tutorial, and yes, my
wife hates me :)  (just kidding.. she's actually very nice about it :)

I've included the SGML source here again.. hopefuly no one has problems
doing ye old sgml2html or what have you on it.

How does one go about getting more people to help out ?

Maybe we should have a competition to see who can write the most
documentation over the course of the weekend ?  Whatya think ?

If you can write more than me, I'll umm.. umm... umm.. well.. you name it
:)

As usual, any comments are appreciated.  Even better would be additions!
:) hint hint, nudge, nudge...


Ian


------------------------------------------------------------------------



<!-- This is the tutorial marked up in SGML
     (just to show how to write a comment)
-->


<!doctype linuxdoc system>
<article>
<title>GTK Tutorial
<author>Ian Main, <tt><htmlurl url="mailto:slow@intergate.bc.ca"
			      name="slow@intergate.bc.ca"></tt>
<date>July 1997


<sect>Introduction
<p>
GTK (GIMP Toolkit) was originally developed as a toolkit for the GIMP
(General Image Manipulation Program).  GTK is built on top of GDK (GIMP
Drawing Kit)  which is basically wrapper around the Xlib functions.  It's
called the GIMP toolkit because it was original written for developing
the GIMP, but has now been used in several free software projects.  The
authors are
<itemize>
<item> Peter Mattis   <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
			   name="petm@xcf.berkeley.edu"></tt>
<item> Spencer Kimball <tt><htmlurl url="mailto:spencer@xcf.berkeley.edu"
			   name="spencer@xcf.berkeley.edu"></tt>
<item> Josh MacDonald <tt><htmlurl url="mailto:jmacd@xcf.berkeley.edu"
			   name="jmacd@xcf.berkeley.edu"></tt>
</itemize>

<p>
GTK is essentially an object oriented API.  Although written completely in
C, it is implemented using the idea of classes, and callback functions
(pointers to functions).
<p>
There is also a third component called glib which contains a few
replacements for printf, malloc etc.  These are used to increase gtk's
portability, as some of the functions implemented here are not available or
nonstandard on other Unicies such as g_strerror().   Some also contain 
enhancements to the libc versions such as
g_malloc will test for errors for you.
<p>
This tutorial is an attempt to document as much as possible of gtk, t is by 
no means complete.  This
tutorial assumes a good understanding of C, and how to create C programs.
It would be a great benifit for the reader to have previous X programming
experience, but it shouldn't be necessary.  If you are learning gtk as your
first widget set, please comment on how you found this tutorial, and what
you had troubles with.
Note that there is also a C++ API for gtk (gtk--) in the works, so if you
prefer to use C++, you should look into this instead.
<p>
I would very much like to hear any problems you have learning gtk from this
document, and would appreciate input as to how it may be improved.

<sect>Getting Started
<p>
The first thing to do of course, is download the gtk source and install
it.  You can either get the distro with GIMP, or d/l it from Peter
Mattis's home dir ftp.xcf.berkely.edu/pub/pmattis (Hope this is ok :) ..
may change ?).
<p>
We'll start with the simplest GTK program possible.  This program will
create a 200x200 pixel window and has no way of exiting except to be
killed using the shell or window manager.

<tscreen><verb>
#include <gtk/gtk.h>

int main (int argc, char *argv[])
{
    GtkWidget *window;
    
    gtk_init (&amp;argc, &amp;argv);
    
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_widget_show (window);
    
    gtk_main ();
    
    return 0;
}
</verb></tscreen>

All programs will of course include the gtk/gtk.h which declares the
variables and functions used in your gtk application.
<p>
The next line

<tscreen><verb>
gtk_init (&amp;argc, &amp;argv);
</verb></tscreen>

calls the function gtk_init(gint *argc, gchar ***argv) which will be
called in all gtk applications.  This sets up a few things for us such
as the default visual and colormap and then proceeds to call
gdk_init(gint *argc, gchar ***argv).  This function initializes the
library for use, sets up default signal handlers, and checks the
arguements passed to your application on the command line, looking for one
of the following:
<itemize>
<item> <tt/--display/
<item> <tt/--debug-level/
<item> <tt/--no-xshm/
<item> <tt/--sync/
<item> <tt/--show-events/
<item> <tt/--no-show-events/
</itemize>
It removes these from the arguement list, leaving anything it does
not recognize for your application to parse or ignore.	This creates a set
of standard arguments excepted by all gtk applications.
<p>
The next two lines of code create and display a window.

<tscreen><verb>
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_widget_show (window);
</verb></tscreen>

The GTK_WINDOW_TOPLEVEL argument specifies that we want the window to
undergo window manager decoration and placement. Rather than create a
window of 0x0 size, a window without children is set to 200x200 by default
so you can still manipulate it.
<p>
The last line enters the GTK main processing loop.

<tscreen><verb>
gtk_main ();
</verb></tscreen>

gtk_main() is another call you will see in every gtk application.  When
control reaches this point, gtk will sleep waiting for X events (such as
button or keypresses) to occur.
In our simple example however, no events will be registered.


<sect1>Hello World in GTK
<p>
OK, now for a program with a widget (a button).  It's the classic hello
world ala gtk.	This would make a good base for a new gtk application.

<tscreen><verb>

#include <gtk/gtk.h>

/* this is a callback function. the data arguments are ignored in this example..
 * More on callbacks below. */
void hello (GtkWidget *widget, gpointer *data)
{
    g_print ("Hello World\n");
}

/* another callback */
void destroy (void)
{
    gtk_main_quit ();
}

int main (int argc, char *argv[])
{
    /* GtkWidget is the storage type for widgets */
    GtkWidget *window;
    GtkWidget *button;
    
    /* this is called in all gtk applications.  Arguements are parsed from
     * the command line and are returned to the application. */
    gtk_init (&amp;argc, &amp;argv);
    
    /* create a new window */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    
    /* when the window is given the "destroy" signal (this can be given
						      * by the application, or the window manager, the function destroy
						      * will be called as defined above.  The NULL passed to this function
						      * is used to pass optional data into this "callback" function. */
    gtk_signal_connect (GTK_OBJECT (window), "destroy",
			GTK_SIGNAL_FUNC (destroy), NULL);
    
    /* sets the border width of the window. */
    gtk_container_border_width (GTK_CONTAINER (window), 10);
    
    /* creates a new button with the label "Hello World". */
    button = gtk_button_new_with_label ("Hello World");
    
    /* When the button receives the "clicked" signal, it will call the
     * function hello() passing it NULL as it's arguement.  The hello() function is
     * defined above. */
    gtk_signal_connect (GTK_OBJECT (button), "clicked",
			GTK_SIGNAL_FUNC (hello), NULL);
    
    /* This will cause the window to be destroyed by calling
     * gtk_widget_destroy(window) when "clicked.  Again, the destroy
     * signal could come from here, or the window manager. */
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
			       GTK_SIGNAL_FUNC (gtk_widget_destroy),
			       GTK_OBJECT (window));
    
    /* this packs the button into the window (a gtk container). */
    gtk_container_add (GTK_CONTAINER (window), button);
    
    /* the final step is to display this newly created widget... */
    gtk_widget_show (button);
    
    /* and the window */
    gtk_widget_show (window);
    
    /* all gtk applications must have a gtk_main().	Control ends here
     * and waits for an event to occur (like a key press or mouse event). */
    gtk_main ();
    
    return 0;
}
</verb></tscreen>


To compile use:

<tscreen><verb>
gcc -Wall -g -lgtk -lgdk -lglib -lX11 -lXext -lm hello_world.c -o hello_world
</verb></tscreen>

The libraries above must all be in your default search paths, if not, add
-L&lt;library directory&gt; and gcc will look in these directories for
the needed
libraries.  For instance, on my Debian Linux box, I have to add
<tt>-L/usr/X11R6/lib</> for it to find the X11 libraries.
<p>
Now the explanation:
<p>
As you can see from this example, it is quite easy to build a simple gtk
application.  GTK is an event driven toolkit, which means it will sleep in
gtk_main until an event occurs and control is passed to the appropriate
function.  By passing in a pointer to the function to be called when the
appropriate event occurs (by connecting a "signal") we tell the
application what we want to have done with when a given event occurs.
<p>
(You may also add timeout functions and IO checks to take control from
gtk_main, this is described in the approprate section.)
<p>
The general steps to creating a widget in gtk are:
<enum>
<item> gtk_*_new - one of various functions to create a new widget.

<item> connect all signals we wish to use to the appropriate handlers.

<item> set the attributes of the widget.

<item> pack the widget into a container using gtk_container_add() or
gtk_box_pack_start() (are there others ?).

<item> gtk_widget_show()
</enum>

gtk_widget_show lets gtk know that we are done setting the attributes
of the widget, and it is ready to be displayed.  You may also use
gtk_widget_hide to make it disappear again.  The order in which you
show the widgets is not important, but I suggest showing the window
last so that it all pops up at once rather than seeing the individual
widgets come up on the screen as they're formed.


<sect>Events
<p>
When setting up a signal handler (better term ?) we use a call to
gtk_signal_connect().  The deceleration of this function is:

<tscreen><verb>
gint gtk_signal_connect (GtkObject *object, gchar *name,
			 GtkSignalFunc func, gpointer func_data);
</verb></tscreen>

There are a few things you'll notice right away from this example.  The
gint, gchar etc. are typedefs to int and char respectively.  This is done
for portabilities sake.  A good example is "gint32" which will be
typedef'd to a 32 bit integer for any platform, whether it be the 64 bit
alpha (int (or short int?)), or the 32 bit Intel (long int or int).  The
typedefs are very straight forward and intuitive.  They are all defined in
glib/glib.h (which gets included from gtk.h).
<p>
The fist arguement to gtk_signal_connect is the object which will generate
the signal, usually a widget (always?).  The second of course is the
signal name, followed by the function you wish to be called.  This function
is called a "callback" function.  Then last arguement is a
pointer to the data you wish to pass to the callback function.
<p>
The callback function you specify should be of the form:

<tscreen><verb>
void callback_func(GtkWidget *widget, gpointer *callback_data);
</verb></tscreen>

Generally, the GtkWidget arguement in the callback is unused.
<p>
So, if we wanted to pass the above function some data when a button is
pressed, we use:

<tscreen><verb>
gtk_signal_connect (GTK_OBJECT (button), "clicked",
		    GTK_SIGNAL_FUNC (callback_func), (gpointer) callback_data);
</verb></tscreen>

This assumes of course that you have already created a button.	The
GTK_OBJECT, GTK_SIGNAL_FUNC are just macros that perform type casting while
adding readability.
<p>
Let's take a look at a slightly improved hello world with better examples
of callbacks.  This will also introduce us to our next topic, packing
widgets.

<tscreen><verb>
#include <gtk/gtk.h>

/* Our new improved callback.  The data passed to this function is printed
 * to stdout. */
void callback (GtkWidget *widget, gpointer *data)
{
    g_print ("Hello again - %s was pressed\n", (char *) data);
}

/* another callback */
void destroy (void)
{
    gtk_main_quit ();
}

int main (int argc, char *argv[])
{
    /* GtkWidget is the storage type for widgets */
    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *box1;

    /* this is called in all gtk applications.	Arguements are parsed from
     * the command line and are returned to the application. */
    gtk_init (&amp;argc, &amp;argv);

    /* create a new window */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    /* this is a new call, this just sets the title of our
     * new window to "Hello Buttons!" */
    gtk_window_set_title (GTK_WINDOW (window), "Hello Buttons!");

    /* It's a good idea to do this for all windows. */
    gtk_signal_connect (GTK_OBJECT (window), "destroy",
			GTK_SIGNAL_FUNC (destroy), NULL);


    /* sets the border width of the window. */
    gtk_container_border_width (GTK_CONTAINER (window), 10);

    /* we create a box to pack widgets into.  this is described in detail
     * in the "packing" section below.  The box is not really visible, it
     * is just used as a tool to arrange widgets. */
    box1 = gtk_hbox_new(FALSE, 0);

    /* put the box into the main window. */
    gtk_container_add (GTK_CONTAINER (window), box1);

    /* creates a new button with the label "Button 1". */
    button = gtk_button_new_with_label ("Button 1");

    /* Now when the button is clicked, we call the "callback" function
     * with a pointer to "button 1" as it's arguement */
    gtk_signal_connect (GTK_OBJECT (button), "clicked",
			GTK_SIGNAL_FUNC (callback), (gpointer) "button 1");

    /* instead of gtk_container_add, we pack this button into the invisible
     * box, which has been packed into the window. */
    gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0);

    /* always remember this step, this tells gtk that our preparation for
     * this button is complete, and it can be displayed now. */
    gtk_widget_show(button);

    /* do these same steps again to create a second button */
    button = gtk_button_new_with_label ("Button 2");

    /* call the same callback function with a different arguement,
     * passing a pointer to "button 2" instead. */
    gtk_signal_connect (GTK_OBJECT (button), "clicked",
			GTK_SIGNAL_FUNC (callback), (gpointer) "button 2");

    gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0);

    /* we show the widgets in the reverse order we created them,
     * someone want to explain to me why ?? :) or is it even
     * necessary ? */
    gtk_widget_show(button);

    gtk_widget_show(box1);

    gtk_widget_show (window);

    /* rest in gtk_main and wait for the fun to begin! */
    gtk_main ();

    return 0;
}
</verb></tscreen>
<p>
Compile this program using the same linking arguements as above.  You'll notice in
this example there is no easy way to exit the program, you have to use
your window manager or command line to kill it.  A good exercise for the
reader would be to insert a third "Quit" button that will exit the
program.  Try playing with resizing the window.
<p>
Just as a side note, there is another useful define for gtk_window_new() -
GTK_WINDOW_DIALOG.  This interacts with the window manager a little
differently and should be used for transient windows.

<sect>Packing Widgets
<p>
When creating an application, you'll want to put more than one button
inside a window.  Our first hello world example only used one widget so we
could simply use a gtk_container_add call to "pack" the widget into the
window.  But when you want to put more than one widget into a window, how
do you control where that widget is positioned ?  This is where packing
comes in.
<p>
Most packing is done by creating boxes as in the example above.  These are
invisible widget containers that we can pack our widgets into and come in
two forms, a horizontal box, and a vertical box.  When packing widgets
into a horizontal box, the objects are inserted horizontally from left to
right or right to left depending on the call used. In a vertical box,
widgets are packed from top to bottom or vice versa.  You may use any
combination of boxes inside or beside other boxes to create the desired
effect.
<p>
To create a new horizontal box, we use a call to gtk_hbox_new(), and for
vertical boxes, gtk_vbox_new().  The gtk_box_pack_start() and
gtk_box_pack_end() functions are used to place objects inside of these
containers.  The gtk_box_pack_start() function will start at the top and
work its way down in a vbox, and pack left to right in an hbox.
gtk_box_pack_end() will do the opposite, packing from bottom to top in a
vbox, and right to left in an hbox.  Using these functions allow us to
right justify or left justify our widgets and may be mixed in any way to
achieve the desired effect.  We will use gtk_box_pack_start() in most of
our examples.  An object may be another container or a widget.	And in
fact, many widgets are actually containers themselves including the
button, but we usually only use a label inside a button.
<p>
By using these calls, gtk knows where you want to place your widgets so it
can do automatic resizing and other nifty things.  there's also a number
of options as to how your widgets should be packed. As you can imagine,
this method gives us a quite a bit of flexibility when placing and
creating widgets.
<p>
Because of this flexibility, packing boxes in GTK+ can be confusing at
first. There are a lot of options, and it's not immediately obvious how
they all fit together.	In the end however, there are basically five
different styles you can get.

<tscreen><verb>    <!-- Don't know the tag for images, but there is one -->
&lt;p align=center> &lt;img src="packbox1.gif" width=528 height=235> &lt;/p>
</verb></tscreen>

Each line contains one horizontal box (hbox) with several buttons. The
call to gtk_box_pack is shorthand for the call to pack each of the buttons
into the hbox. Each of the buttons is packed into the hbox the same way
(i.e. same arguments to the gtk_box_pack_start () function).
<p>
This is the declaration of the gtk_box_pack_start function.

<tscreen><verb>
void gtk_box_pack_start (GtkBox    *box,
			 GtkWidget *child,
			 gint	    expand,
			 gint	    fill,
			 gint	    padding);
</verb></tscreen>

The first arguement is the box you are packing the object into, the second
is this object.  The objects will all be buttons for now, so we'll be
packing buttons into boxes.
<p>
The expand argument to gtk_box_pack_start() or gtk_box_pack_end() controls
whether the widgets are laid out in the box to fill in all the extra space
in the box so the box is expanded to fill the area alloted to it (TRUE).
Or the box is shrunk to just fit the widgets (FALSE).  Setting expand to
FALSE will allow you to do right and left
justifying of your widgets.  Otherwise, they will all expand to fit in the
box, and the same effect could be achieved by using only one of
gtk_box_pack_start or pack_end functions.
<p>
The fill argument to the gtk_box_pack functions control whether the extra
space is allocated to the objects themselves (TRUE), or as extra padding
in the box around these objects (FALSE). It only has an effect if the
expand argument is also TRUE.
<p>
[does the above sound right to you ?]
<p>
When creating a new box, the function looks like this:

<tscreen><verb>
GtkWidget * gtk_hbox_new (gint homogeneous,
			  gint spacing);
</verb></tscreen>

The homogeneous argument to gtk_hbox_new (and the same for gtk_vbox_new)
controls whether each object in the box has the same size (i.e. the same
width in an hbox, or the same height in a vbox). If it is set, the expand
argument to the gtk_box_pack routines is always turned on.
<p>
What's the difference between spacing (set when the box is created) and
adding (set when elements are packed)? Spacing is added between objects,
and padding is added on either side of an object.  The following figure
should make it clearer:

<tscreen><verb>    <!-- Don't know the tag for images, but there is one -->
&lt;p align=center> &lt;img src="packbox2.gif" width=509 height=213> &lt;/p>
</verb></tscreen>

<p>
Someone write about table widgets! :)

<sect>Widget Overview
<p>
We'll further our exploration of gtk by examining each widget in turn,
creating a few simple functions to display them.  Another good source is
the testgtk.c program that comes with gtk.  It can be found in
gtk/testgtk.c.
<p>
For your reference, here is the class hierarchy tree used to implement widgets.

<tscreen><verb>
    GtkObject
    +-- GtkData
    |	\-- GtkAdjustment
    |
    \-- GtkWidget
	+-- GtkContainer
	|   +-- GtkBin
	|   |	+-- GtkAlignment
	|   |	+-- GtkFrame
	|   |	|   *-- GtkAspectFrame
	|   |	|
	|   |	+-- GtkItem
	|   |	|   +-- GtkListItem
	|   |	|   +-- GtkMenuItem
	|   |	|   |	+-- GtkCheckMenuItem
	|   |	|   |	    *-- GtkRadioMenuItem
	|   |	|   |
	|   |	|   *-- GtkTreeItem
	|   |	|
	|   |	+-- GtkViewport
	|   |	\-- GtkWindow
	|   |	    +-- GtkDialog
	|   |	    \-- GtkFileSelection
	|   |
	|   +-- GtkBox
	|   |	+-- GtkHBox
	|   |	\-- GtkVBox
	|   |	    +-- GtkColorSelection
	|   |	    \-- GtkCurve
	|   |
	|   +-- GtkButton
	|   |	+-- GtkOptionMenu
	|   |	\-- GtkToggleButton
	|   |	    \-- GtkCheckButton
	|   |		\-- GtkRadioButton
	|   |
	|   +-- GtkList
	|   +-- GtkMenuShell
	|   |	+-- GtkMenu
	|   |	\-- GtkMenuBar
	|   |
	|   +-- GtkNotebook
	|   +-- GtkScrolledWindow
	|   +-- GtkTable
	|   \-- GtkTree
	|
	+-- GtkDrawingArea
	+-- GtkEntry
	+-- GtkMisc
	|   +-- GtkArrow
	|   +-- GtkImage
	|   +-- GtkLabel
	|   \-- GtkPixmap
	|
	+-- GtkPreview
	+-- GtkProgressBar
	+-- GtkRange
	|   +-- GtkScale
	|   |	+-- GtkHScale
	|   |	\-- GtkVScale
	|   |
	|   \-- GtkScrollbar
	|	+-- GtkHScrollbar
	|	\-- GtkVScrollbar
	|
	+-- GtkRuler
	|   +-- GtkHRuler
	|   \-- GtkVRuler
	|
	\-- GtkSeparator
	    +-- GtkHSeparator
	    \-- GtkVSeparator

</verb></tscreen>

<sect1>The Button Widget
<p>
We've almost seen all there is to see of the button widget.  It's pretty
simple.  There is however two ways to create a button.	You can use the
gtk_button_new_with_label() to create a button with a label, or use
gtk_button_new() to create a blank button.  It's then up to you to pack a
label or pixmap into this new button.  To do this, create a new box, and
then pack your objects into this box using the usual gtk_box_pack_start,
and then use gtk_container_add to pack the box into the button.
<p>
Here's an example of using gtk_button_new to create a button with a
picture and a label in it.  I've broken the code to create a box up from
the rest so you can use it in your programs.


<tscreen><verb>
#include <gtk/gtk.h>


/* create a new hbox with an image and a label packed into it
 * and return the box.. */

GtkWidget *xpm_label_box (GtkWidget *parent, gchar *xpm_filename, gchar *label_text)
{
    GtkWidget *box1;
    GtkWidget *label;
    GtkWidget *pixmapwid;
    GdkPixmap *pixmap;
    GdkBitmap *mask;
    GtkStyle *style;

    /* create box for xpm and label */
    box1 = gtk_hbox_new (FALSE, 0);
    gtk_container_border_width (GTK_CONTAINER (box1), 2);

    /* get style of button.. I assume it's to get the background color.
     * if someone knows the real reason, please enlighten me. */
    style = gtk_widget_get_style(parent);

    /* now on to the xpm stuff.. load xpm */
    pixmap = gdk_pixmap_create_from_xpm (parent->window, &amp;mask,
					 &amp;style->bg[GTK_STATE_NORMAL],
					 xpm_filename);
    pixmapwid = gtk_pixmap_new (pixmap, mask);

    /* create label for button */
    label = gtk_label_new (label_text);

    /* pack the pixmap and label into the box */
    gtk_box_pack_start (GTK_BOX (box1),
			pixmapwid, FALSE, FALSE, 3);

    gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 3);

    gtk_widget_show(pixmapwid);
    gtk_widget_show(label);

    return (box1);
}

/* our usual callback function */
void callback (GtkWidget *widget, gpointer *data)
{
    g_print ("Hello again - %s was pressed\n", (char *) data);
}


int main (int argc, char *argv[])
{
    /* GtkWidget is the storage type for widgets */
    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *box1;

    gtk_init (&amp;argc, &amp;argv);

    /* create a new window */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    gtk_window_set_title (GTK_WINDOW (window), "Pixmap'd Buttons!");

    /* It's a good idea to do this for all windows. */
    gtk_signal_connect (GTK_OBJECT (window), "destroy",
			GTK_SIGNAL_FUNC (gtk_exit), NULL);


    /* sets the border width of the window. */
    gtk_container_border_width (GTK_CONTAINER (window), 10);

    /* create a new button */
    button = gtk_button_new ();

    /* You should be getting used to seeing most of these functions by now */
    gtk_signal_connect (GTK_OBJECT (button), "clicked",
			GTK_SIGNAL_FUNC (callback), (gpointer) "cool button");

    /* this calls our box creating function */
    box1 = xpm_label_box(window, "info.xpm", "cool button");

    /* pack and show all our widgets */
    gtk_widget_show(box1);

    gtk_container_add (GTK_CONTAINER (button), box1);

    gtk_widget_show(button);

    gtk_container_add (GTK_CONTAINER (window), button);

    gtk_widget_show (window);

    /* rest in gtk_main and wait for the fun to begin! */
    gtk_main ();

    return 0;
}
</verb></tscreen>

The xpm_label_box function can be used to put xpm's and labels in many
different widgets (I think :-).


<sect1> Toggle Buttons
<p>
Toggle buttons are very similar to normal buttons, except they will always
be in one of two states, alternated by a click.  They may be depressed, and
when you click again, they will pop back up.  Click again, and they will pop
back down. 

Toggle buttons are the basis for check buttons and radio buttons, as such,
many of the calls used for toggle buttons are inhereted by radio and check
buttons.  I will point these out when we come to them.

Creating a new toggle button:

<tscreen><verb>
GtkWidget* gtk_toggle_button_new (void);

GtkWidget* gtk_toggle_button_new_with_label (gchar *label);
</verb></tscreen>
<p>
As you can imagine, these work identically to the normal button widget
calls.  The first creates a blank toggle button, and the second, one with a
label widget already packed into it.
<p>
To retreive the state of the toggle widget, including radio and check
buttons, we use a macro as shown in our example below.  This tests the state
of the toggle in a callback.  The signal of interest emitted to us by toggle
buttons (the toggle button, check button, and radio button widgets), is the
"toggled" signal.  To check the state of these buttons, use a callback to
catch this signal that looks something like this:

<tscreen><verb>
void toggle_button_callback (GtkWidget *widget, gpointer   data)
{
    if (GTK_TOGGLE_BUTTON (widget)->active) 
    {
        /* If control reaches here, the toggle button is depressed. */
    }
}
</verb></tscreen>

The above is in need of testing.. It's just a guess..

<tscreen><verb>
guint gtk_toggle_button_get_type (void);
</verb></tscreen>
<p>
No idea... they all have this, but I dunno what it is :)

<tscreen><verb>
void gtk_toggle_button_set_mode (GtkToggleButton *toggle_button,
                                 gint draw_indicator);
</verb></tscreen>
<p>
No idea.

<tscreen><verb>
void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
                                  gint state);
</verb></tscreen>
<p>
The above call can be used to set the state of the toggle button, and it's
children the radio and check buttons.  Passing
in your created button as the first arguement, and a TRUE or FALSE
for the second state arguement to specify whether it should be up (released) or
down (depressed).  Default is up, or FALSE.

<tscreen><verb>
void       gtk_toggle_button_toggled        (GtkToggleButton *toggle_button);
</verb></tscreen>
<p>
Hmm.. judging from the gtk code, it toggles the button, and emits the
"toggled" signal..

<sect1> Check Buttons
<p>
Check buttons inherent many properties and functions from the the toggle buttons above, 
but look a little
different.  Rather than being buttons with text inside them, they are small
squares with the text to the right of them.  These are often seen for
toggling options on and off in applications.

The two creation functions are the same as for the normal button.

<tscreen><verb>
GtkWidget* gtk_check_button_new (void);

GtkWidget* gtk_check_button_new_with_label (gchar *label);
</verb></tscreen>

The new_with_label function creates a check button with a label beside it.

Checking the state of the check button is identical to that of the toggle
button.

<sect1> Radio Buttons
<p>
Radio buttons are similar to check buttons except they are grouped so that
only one may be selected/depressed at a time.  This is good for places in
your application where you need to select from a short list of options.

Creating a new radio button is done with one of these calls:

<tscreen><verb>
GtkWidget* gtk_radio_button_new (GSList *group);

GtkWidget* gtk_radio_button_new_with_label (GSList *group,
                                            gchar *label);
</verb></tscreen>
<p>
You'll notice the extra arguement to these calls.  They require a group to
perform they're duty properly.  The first call should pass NULL as the first
arguement.  You then create a group using:

<tscreen><verb>
GSList* gtk_radio_button_group (GtkRadioButton *radio_button);
</verb></tscreen>

<p>
You then pass this group as the first arguement to each subsequent call to
gtk_radio_button_new or new_with_label.  It is also a good idea to
explicitly set which button should be the default depressed button with:

<tscreen><verb>
void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
                                  gint state);
</verb></tscreen>
<p>
This is described in the section on toggle buttons, and works in exactly the
same way.
<p>
[Insert an example here of how to use these guys cause I think it could do a
lot of good at this point.]

<sect1>The Tooltips Widget
<p>
These are the little text strings that pop up when you leave your pointer
over a button or other widget for a few seconds.  They are easy to use, so I
will just explain them without giving an example.  If you want to see some
code, take a look at the testgtk.c program distributed with gtk.
<p>
The first call you will use to create a new tooltip.  You only need to do
this once in a given function.	The GtkTooltip this function returns can be
used to create multiple tooltips.

<tscreen><verb>
GtkTooltips *gtk_tooltips_new (void);
</verb></tscreen>

Once you have created a new tooltip, and the widget you wish to use it on,
simply use this call to set it.

<tscreen><verb>
void gtk_tooltips_set_tips   (GtkTooltips *tooltips,
			      GtkWidget   *widget,
			      gchar	  *tips_text);
</verb></tscreen>

The first arguement is the tooltip you've already created, followed by the
widget you wish to have this tooltip pop up for, and the text you wish it to
say.
<p>
Here's a short example:

<tscreen><verb>
GtkTooltips *tooltips;
GtkWidget *button;
...
tooltips = gtk_tooltips_new ();
button = gtk_button_new_with_label ("button 1");
...
gtk_tooltips_set_tips (tooltips, button, "This is button 1");
</verb></tscreen>



There are other calls used with tooltips.  I will just list them with a
brief description of what they do.

<tscreen><verb>
void gtk_tooltips_destroy    (GtkTooltips *tooltips);
</verb></tscreen>

Destroy the created tooltips.

<tscreen><verb>
void gtk_tooltips_enable     (GtkTooltips *tooltips);
</verb></tscreen>

Enable a disabled set of tooltips.

<tscreen><verb>
void gtk_tooltips_disable    (GtkTooltips *tooltips);
</verb></tscreen>

Disable an enabled set of tooltips.

<tscreen><verb>
void gtk_tooltips_set_delay  (GtkTooltips *tooltips,
			      gint	   delay);
			      void	  gtk_tooltips_set_tips
			      (GtkTooltips *tooltips,
			      GtkWidget   *widget,
			      gchar	  *tips_text);
</verb></tscreen>

Sets how many milliseconds you have to hold you pointer over the widget before the
tooltip will pop up.  The default is 1000 milliseconds or 1 second.

<tscreen><verb>
void gtk_tooltips_set_colors (GtkTooltips *tooltips,
			      GdkColor	  *background,
			      GdkColor	  *foreground);
</verb></tscreen>

Set the foreground and background color of the tooltips.  Again, I have no
idea how to specify the colors.
<p>
And that's all the functions associated with tooltips.  More than you'll
ever want to know :)



<sect1> Scrolled Windows
<p>
Please write about me :)

<sect1> Text Entries
<p>
Please write about me :)

<sect1> Lists
<p>
Please write about me :)

<sect1> Color Selections
<p>
Please write about me :)

<sect1> File Selections
<p>
Please write about me :)

<sect1> Dialogs
<p>
Please write about me :)

<sect1> Range Controls
<p>
Please write about me :)

<sect1> Rulers
<p>
Please write about me :)

<sect1> Text Boxes
<p>
Please write about me :)

<sect1> Notebooks
<p>
Please write about me :)

<sect1> Progress Bars
<p>
Please write about me :)

<sect1> Color Previews
<p>
Please write about me :)

<sect1> Grey Previews
<p>
Please write about me :)

<sect1> Curves
<p>
Please write about me :)

<sect1> Pixmaps
<p>
Please write about me :)


<sect1>Menu Widgets
<p>
There are two ways to create menus, there's the easy way, and there's the
hard way.  Both have their uses, but you can usually use the menu_factory
(the easy way).  The "hard" way is to create all the menus using the calls
directly.  The easy way is to use the gtk_menu_factory calls.  This is
much simpler, but I've had problems because of the '/' that is used to
seperated menus.  Other than this, there is no real reason to use the
manual method (is this true ??)
<p>
In the true tradition of teaching, we'll show you the hard
way first. <tt>:)</>
<p>
Let's look at the functions that are used to create menus.
This first function is used to create a new menu.

<tscreen><verb>
GtkWidget *gtk_menu_bar_new()
</verb></tscreen>

This rather self explanatory function creates a new menu bar.  You use
gtk_container_add to pack this into a window, or the box_pack functions to
pack it into a box - the same as buttons.

<tscreen><verb>
GtkWidget *gtk_menu_new();
</verb></tscreen>

This function returns a pointer to a new menu, it is never actually shown
(with gtk_widget_show), it just holds the menu items.  Hopefully this will
become more clear when you look at the example below.
<p>
The next two calls are used to create menu items that are packed into
the menu.

<tscreen><verb>
GtkWidget *gtk_menu_item_new()
</verb></tscreen>

and

<tscreen><verb>
GtkWidget *gtk_menu_item_new_with_label(const char *label)
</verb></tscreen>

These calls are used to create the menus that are to be displayed.
Remember to differentiate between a "menu" as created with gtk_menu_new
and a "menu item" as created by the gtk_menu_item_new functions.  The
menu item will be an actual button with an associated action,
whereas a menu will be a container holding these

<tscreen><verb>
gtk_menu_item_append()

gtk_menu_item_set_submenu()
</verb></tscreen>

The gtk_menu_new_with_label and plain gtk_menu_new functions are just as you'd expect after
reading about the buttons.  One creates a new menu item with a label
already packed into it, and the other just creates a blank menu item.
<p>
The steps to create a menu are outlined below:
<itemize>
<item>	Create a new menu using gtk_menu_new()
<item>	Create a menu item using gtk_menu_item_new().  This will be the root of
  the menu, the text appearing here will be on the menu bar itself.
<item>	Use multiple calls to gtk_menu_item_new for each item you wish to have on
  your menu.  And use gtk_menu_item_append() to put each of these new items on
  together.  This creates a list of menu items.
<item>	Use gtk_menu_item_set_submenu() to attach the newly created menu_items to
  the root menu item (The one created in the second step).
<item>	Create a new menu bar using gtk_menu_bar_new.  This step only needs
  to be done once when creating a series of menus on one menu bar.
<item> Use gtk_menu_bar_append to put the root menu onto the menubar.
</itemize>
And that should about do it.  Let's take a look at an example to help clarify.


<tscreen><verb>
#include <gtk/gtk.h>

int main (int argc, char *argv[])
{

    GtkWidget *window;
    GtkWidget *menu;
    GtkWidget *menu_bar;
    GtkWidget *root_menu;
    GtkWidget *menu_items;
    char buf[128];
    int i;

    gtk_init (&amp;argc, &amp;argv);

    /* create a new window */
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW (window), "GTK Menu Test");
    gtk_signal_connect(GTK_OBJECT (window), "destroy",
		       (GtkSignalFunc) gtk_exit, NULL);

    /* Init the menu-widget, and remember -- never
     * gtk_show_widget() the menu widget!! */
    menu = gtk_menu_new();

    /* This is the root menu, and will be the label will be the menu name displayed on
     * the menu bar.  There won't be
     * a signal handler attached, as it only pops up the rest of the menu when pressed. */
    root_menu = gtk_menu_item_new_with_label("Root Menu");

    gtk_widget_show(root_menu);

    /* Next we make a little loop that makes three menu-entries for "test-menu".
     * Notice the call to gtk_menu_append.  Here we are adding a list of menu items
     * to our menu.  Normally, we'd also catch the "clicked" signal on each of the
     * menu items and setup a callback for it, but it's omitted here to save space. */

    for(i = 0; i < 3; i++)
	{
	    /* Copy the names to the buf. */
	    sprintf(buf, "Test-undermenu - %d", i);

	    /* Create a new menu-item with a name... */
	    menu_items = gtk_menu_item_new_with_label(buf);

	    /* ...and add it to the menu. */
	    gtk_menu_append(GTK_MENU (menu), menu_items);

	    /* Show the widget */
	    gtk_widget_show(menu_items);
	}

    /* Now we specify that we want our newly created "menu" to be the menu for the "root menu" */
    gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);

    /* Create a menu-bar to hold the menus and add it to our main window*/
    menu_bar = gtk_menu_bar_new();
    gtk_container_add(GTK_CONTAINER(window), menu_bar);
    gtk_widget_show(menu_bar);

    /* And finally we append the menu-item to the menu-bar -- this is the "root"
     * menu-item I have been raving about =) */
    gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu);

    /* always display the window as the last step so it all splashes on the screen at once. */
    gtk_widget_show(window);

    gtk_main ();

    return 0;
}
</verb></tscreen>

You may also set a menu item to be insensitive or, using an accelerator
table, bind keys to menu functions.
<p>
[ maybe insert a few calls or something ]
<p>
Now that we've shown you the hard way, here's how you do it using the
gtk_menu_factory calls.
<p>
[ gtk_menu_factory example and explanation ]

<sect>Timeouts, IO and Idle Functions
<p>
<sect1>Timeouts
<p>
You may be wondering how you make gtk do useful work when in gtk_main.
Well, you have several options.  Using the following functions you can
create a timeout function.

<tscreen><verb>
gint gtk_timeout_add (guint32 interval,
		      GtkFunction function,
		      gpointer data);
</verb></tscreen>

The first arguement is the number of milliseconds
between calls to your function.  The second arguement is the function
you wish to have called, and
the third, the data passed to this callback function.  The return value is
an integer "tag" which may be used to stop the timeout by calling:

<tscreen><verb>
void gtk_timeout_remove (gint tag);
</verb></tscreen>

You may also stop the timeout function by returning zero or FALSE from
your callback function.  Obviously this means if you want your function to
continue to be called, it should return a non-negative value, ie TRUE.

The declaration of you callback should look something like this:

<tscreen><verb>
gint timeout_callback (gpointer data);
</verb></tscreen>

<sect1>Monitoring IO
<p>
Another nifty feature of gtk, is the ability to have it check for data on a
file descriptor for you (as returned by open(2) or socket(2)).	This is
especially useful for networking applications.	The function:

<tscreen><verb>
gint gdk_input_add (gint source,
		    GdkInputCondition condition,
		    GdkInputFunction  function,
		    gpointer data);
</verb></tscreen>

Where the first arguement is the file descriptor you wish to have watched,
and the second specifies what you want gdk to look for.  This may be one of:
<p>
GDK_INPUT_READ - Call your function when there is data ready for reading on
your file descriptor.
<p>
GDK_INPUT_WRITE - Call your function when the file descriptor is ready for
writing.
<p>
As I'm sure you've figured out already, the third arguement is the function
you wish to have called when the above conditions are satisfied, and the
fourth is the data to pass to this function.
<p>
The return value is a tag that may be used to stop gdk from monitoring this
file descriptor using the following function.

<tscreen><verb>
void gdk_input_remove (gint tag);
</verb></tscreen>
<p>
As with the gtk_timeout_add function above, a FALSE return from your
callback function will also cause gtk to stop monitoring the file
descriptor. (will have to verify this.)


<sect1>Idle Functions
<p>
What about if you have a function you want called when nothing else is
happening ?

<tscreen><verb>
gint gtk_idle_add (GtkFunction function,
		   gpointer data);

void gtk_idle_remove (gint tag);
</verb></tscreen>
<p>
I won't explain the meaning of the arguements as they follow very much like
the ones above.  The function pointed to by the first arguement to
gtk_idle_add will be called whenever the opportunity arises.  As with the
others, returning FALSE will stop the idle function from being called.


<sect>glib Functions
<p>
There are several useful function that we recomend you use when creating gdk
and gtk applications.  I will list them all here with a brief explanation.
They are all duplicates of standard libc functions so I won't go into
detail.  This is mostly to be used as a reference, so you know what is
available for use.


<tscreen><verb>
gpointer g_malloc      (gulong    size);
</verb></tscreen>

This is a replacement for malloc().  You do not need to check the return
vaule as it is done for you in this function.

<tscreen><verb>
gpointer g_malloc0     (gulong    size);
</verb></tscreen>

Same as above, but zero's the memory before returning a pointer to it.

<tscreen><verb>
gpointer g_realloc     (gpointer  mem,
                        gulong    size);
</verb></tscreen>

Relocates "size" bytes of memory starting at "mem".  Obviously, the memory should have been
previously allocated.

<tscreen><verb>
void     g_free        (gpointer  mem);
</verb></tscreen>

Free's memory.  Easy one.

<tscreen><verb>
gchar* g_strdup    (const gchar *str);
</verb></tscreen>

Replacement strdup function.  Allocates memory for a new string and returns
a pointer to it.

<tscreen><verb>
gchar* g_strerror  (gint errnum);
</verb></tscreen>

I recomend using this for all error messages.  It's much nicer, and more
portable than perror() or others.  The output is usually of the form:

<tscreen><verb>
program name:function that failed:file or further description:strerror
</verb></tscreen>

Here's an example of one such call used in our hello_world program:

<tscreen><verb>
g_print("hello_world:open:%s:%s\n", filename, g_strerror(errno));
</verb></tscreen>

And our last function:

<tscreen><verb>
gchar* g_strsignal (gint signum);
</verb></tscreen>

Prints out the name of the signal given the signal number.

There are many more such functions in glib/glib.h, but I will not go into
depth on them.  If anyone wants to add some short descriptions of other
functions in here, like for the linked list stuff, please do.



<sect>gtkrc Files
<p>
GTK has it's own way of dealing with application defaults, by using rc
files.  The following functions are used:

<tscreen><verb>

void gtk_rc_init (void);

void gtk_rc_parse (char *filename);

GtkStyle* gtk_rc_get_style (GtkWidget *widget);

void gtk_rc_add_widget_name_style (GtkStyle *style,
                                   char *pattern);
				   
void gtk_rc_add_widget_class_style (GtkStyle *style,
                                    char *pattern);
					 
</verb></tscreen>
<p>
The most common one is gtk_rc_init() which will cause 
GTK to parse the file given as its arguementand and use the defaults found in it.
This can be used to set colors, pixmaps for backgrounds etc.
<p>
If someone could expand further on this... I'd like to see examples of gtkrc
files too.  This is something that still mystifies me.

<sect>Writing Your Own Widgets
<p>
It's in the works.


<sect>Contributing

<p>
This document, like so much other great software out there, was created for
free by volenteers.  If you are at all knowledgable about any aspect of gtk
that does not already have documentation, please consider contributing to
this document.
<p>
If you do decide to contribute, please mail your text to me, Ian Main, 
<tt><htmlurl url="mailto:slow@intergate.bc.ca"
name="slow@intergate.bc.ca"></tt>. Also, be aware that the entirity of this 
document is free, and any addition by yourself must also be free.  That is, 
people may use any portion of your examples in their programs, and copies 
of this document may be distributed at will etc.
<p>
Thank you.


<sect>Credits
<p>
I would like to thank the following for their contributions to this text.

<itemize>
<item>Bawer Dagdeviren, <tt><htmlurl url="mailto:chamele0n@geocities.com"
			      name="chamele0n@geocities.com"></tt>
for the menus tutorial.			      

<item>Raph Levien, <tt><htmlurl url="mailto:raph@acm.org"
			 name="raph@acm.org"></tt>
for hello world ala gtk, and the widget packing.

<item>Peter Mattis, <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
                          name="petm@xcf.berkeley.edu"></tt>
for the simplest gtk program.

<item>Werner Koch <tt><htmlurl url="mailto:werner.koch@guug.de"
name="werner.koch@guug.de"></tt> for converting the original plain text to
SGML, and the widget class hierarchy.

</itemize>
<p>
And to all of you who commented and helped refine this document.
<p>
Thanks.

</article>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]