YATU - Yet Another Tutorial Update




Here you are.

If anyone wants to convert it to SGML please feel free :) 

Also, if someone who has used tooltips could take a look at that section
and fill in all the blanks it'd be much appreciated.

Thanks,

Ian



INTRODUCTION:

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

Peter Mattis    <petm@xcf.berkeley.edu>
Spencer Kimball <spencer@xcf.berkeley.edu>
Josh MacDonald  <jmacd@xcf.berkeley.edu>

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). 

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 on
all unicies.   Some also contain enhancements to the libc versions (such as
g_malloc), and others (such as g_strerror) will even pop up windows for you
printing your error string.

This tutorial is a compilation of other documentation found on gtk, as
well as some I've added and changed.  It is by no means complete.  This
tutorial assumes a good understanding of C, and how to create C programs. 
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. 


GETTING STARTED

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 ?). 

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. 

#include <gtk/gtk.h>

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

  gtk_init (&argc, &argv);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_widget_show (window);

  gtk_main ();

  return 0;
}

All programs will of course include the gtk/gtk.h which declares the
variables and functions used in your gtk application. 

The next line

gtk_init (&argc, &argv);

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: 

--display
--debug-level
--no-xshm 
--sync
--show-events
--no-show-events

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. 

The next two lines of code create and display a window.

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_widget_show (window);

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. 

The last line enters the GTK main processing loop.

  gtk_main ();

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.


HELLO WORLD IN GTK

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. 

     #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 (&argc, &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;
     }


To compile use:

gcc -Wall -g -lgtk -lgdk -lglib -lX11 -lXext -lm hello_world.c -o hello_world

The libraries above must all be in your default search paths, if not, add
-L<library directory> and gcc will look in these directories for the needed
libraries.  For instance, on my Debian Linux box, I have to add
-L/usr/X11R6/lib for it to find the X11 libraries.

Now the explanation:

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.

(You may also add timeout functions and IO checks to take control from 
gtk_main, this is described in the approprate section.)

The general steps to creating a widget in gtk are:

1) gtk_*_new - one of various functions to create a new widget.

2) connect all signals we wish to use to the appropriate handlers.

3) set the attributes of the widget.

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

5) gtk_widget_show()

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.


EVENTS

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

gint gtk_signal_connect (GtkObject *object, gchar *name,
                         GtkSignalFunc func, gpointer func_data);

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).

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. 

The callback function you specify should be of the form:

void callback_func(GtkWidget *widget, gpointer *callback_data);

Generally, the GtkWidget arguement in the callback is unused.

So, if we wanted to pass the above function some data when a button is
pressed, we use:

gtk_signal_connect (GTK_OBJECT (button), "clicked",
                    GTK_SIGNAL_FUNC (callback_func), (gpointer) callback_data);

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. 

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. 

#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 (&argc, &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;
}

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. 


PACKING

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. 

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.

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.

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. 

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. 

<p align=center> <img src="packbox1.gif" width=528 height=235> </p>

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). 

This is the declaration of the gtk_box_pack_start function. 

void gtk_box_pack_start (GtkBox    *box,
                         GtkWidget *child,
                         gint       expand,
                         gint       fill,
                         gint       padding);

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. 

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. 

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. 

[does the above sound right to you ?]

When creating a new box, the function looks like this:

GtkWidget * gtk_hbox_new (gint homogeneous,
                          gint spacing);

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.

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: 

<p align=center> <img src="packbox2.gif" width=509 height=213> </p>


WIDGETS

The stock GTK+ distro comes with the following widgets and functions:

> Buttons
* Toggle Buttons
* Check Buttons
* Radio Buttons
* Tooltips
> Menus
* Scrolled Windows
* Entries
* Lists
* Color Selections
* File Selections
* Dialogs
* Range Controls
* Rulers
* Text Boxes
* Notebooks
* Progress Bars
* Preview Color (good name ?)
* Grey Previews
* Curves
* Pixmaps
> Timeouts
> Idle Calls
> File Descriptor Input Checking

[A section on..
* creating your own widgets
* advanced signal stuff
* more ?
]


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. 

BUTTONS

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. 

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. 


#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, &mask,
					 &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 (&argc, &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;
}

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

TOOLTIPS

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.

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.

GtkTooltips *gtk_tooltips_new (void);

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

void gtk_tooltips_set_tips   (GtkTooltips *tooltips,
                              GtkWidget   *widget,
			      gchar       *tips_text);

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.

Here's a short example:

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");


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

void gtk_tooltips_destroy    (GtkTooltips *tooltips);

Destroy the created tooltips.

void gtk_tooltips_enable     (GtkTooltips *tooltips);

Enable a disabled set of tooltips.

void gtk_tooltips_disable    (GtkTooltips *tooltips);

Disable an enabled set of tooltips.

void gtk_tooltips_set_delay  (GtkTooltips *tooltips,
                              gint         delay);
			      void        gtk_tooltips_set_tips
			      (GtkTooltips *tooltips,
			      GtkWidget   *widget,
			      gchar       *tips_text);

Sets how long you have to hold you pointer over the widget before the
tooltip will pop up.  I have no idea what all them extra things are or what
time measurement is used for the delay.

void gtk_tooltips_set_colors (GtkTooltips *tooltips,
                              GdkColor    *background,
			      GdkColor    *foreground);

Set the foreground and background color of the tooltips.  Again, I have no
idea how to specify the colors.

And that's all the functions associated with tooltips.  More than you'll
ever want to know :)


MENUS

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 ??) 

In the true tradition of teaching, we'll show you the hard
way first. :)

Let's look at the functions that are used to create menus.
This first function is used to create a new menu.

GtkWidget *gtk_menu_bar_new()

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. 

GtkWidget *gtk_menu_new();

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. 

The next two calls are used to create menu items that are packed into
the menu.

GtkWidget *gtk_menu_item_new()

and

GtkWidget *gtk_menu_item_new_with_label(const char *label)	  

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 

gtk_menu_item_append()

gtk_menu_item_set_submenu()

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. 

The steps to create a menu are outlined below:

- Create a new menu using gtk_menu_new()
- 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.
- 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.
- 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).
- 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.
- Use gtk_menu_bar_append to put the root menu onto the menubar.

And that should about do it.  Let's take a look at an example to help clarify.


#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 (&argc, &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;
}

You may also set a menu item to be insensitive or, using an accelerator
table, bind keys to menu functions.  

[ maybe insert a few calls or something ]

Now that we've shown you the hard way, here's how you do it using the
gtk_menu_factory calls.

[ gtk_menu_factory example and explanation ]

TIMEOUTS, IO, AND IDLE FUNCTIONS

TIMEOUTS

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.

gint gtk_timeout_add (guint32 interval,
                      GtkFunction function,
		      gpointer data);

The first arguement is the number of milliseconds [doublecheck this] 
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:

void gtk_timeout_remove (gint tag);

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:

gint timeout_callback (gpointer data);

MONITORING IO

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:

gint gdk_input_add (gint source,
                    GdkInputCondition condition,
		    GdkInputFunction  function,
                    gpointer data);

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:

GDK_INPUT_READ - Call your function when there is data ready for reading on
your file descriptor.

GDK_INPUT_WRITE - Call your function when the file descriptor is ready for
writing.

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.

The return value is a tag that may be used to stop gdk from monitoring this
file descriptor using the following function.

void gdk_input_remove (gint tag);

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.)


IDLE FUNCTIONS

What about if you have a function you want called when nothing else is
happening ?  

gint gtk_idle_add (GtkFunction function,
                   gpointer data);
		  
void gtk_idle_remove (gint tag);
				  
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.



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


This tutorial is written and maintained by Ian Main <slow@intergate.bc.ca>
This is free software, you may use any of these examples in any of your
code without including any disclaimer or copyright notice. 

Many thanks to the contributors

Bawer Dagdeviren <chamele0n@geocities.com>  (tutorial on menus)

And to the following from whom I stole^H^H^H^H^H borrowed some material

Raph Levien <raph@acm.org> (widget packing code and examples)
Peter Mattis <petm@xcf.berkeley.edu>

And to all of you who commented and helped refine this document.

Thanks.




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