Previous Next Contents

3. The Next Step

3.1 Data Types

There are a few things you probably noticed in the previous examples that need explaining. The gint, gchar etc. that you see are typedefs to int and char respectively. This is done to get around that nasty dependency on the size of simple data types when doing calculations. A good example is "gint32" which will be typedef'd to a 32 bit integer for any given platform, whether it be the 64 bit alpha, or the 32 bit i386. The typedefs are very straight forward and intuitive. They are all defined in glib/glib.h (which gets included from gtk.h).

You'll also notice the ability to use GtkWidget when the function calls for a GtkObject. GTK is an object oriented design, and a widget is an object.

3.2 More on Signal Handlers

Lets take another look at the gtk_signal_connect declaration.

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

Notice the gint return value ? This is a tag that identifies your callback function. As said above, you may have as many callbacks per signal and per object as you need, and each will be executed in turn, in the order they were attached. This tag allows you to remove this callback from the list by using:

void gtk_signal_disconnect (GtkObject *object,
                            gint id);
So, by passing in the widget you wish to remove the handler from, and the tag or id returned by one of the signal_connect functions, you can disconnect a signal handler.

You may also use:

gtk_signal_disconnect_by_data (GtkObject *object,
                               gpointer data);

Which I have never used and I don't really know for sure how it works :)

Another function to remove all the signal handers from an object is:

gtk_signal_handlers_destroy (GtkObject *object);

This call is fairly self explanatory. It simply removes all the current signal handlers from the object passed in as the first argument.

3.3 An Upgraded Hello World

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 (GtkWidget *widget, gpointer *data)
{
    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.  arguments 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 argument */
    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 argument,
     * 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);

    /* The order in which we show the buttons is not really important, but I
     * recommend showing the window last, so it all pops up at once. */
    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 arguments as our first example. You'll notice this time 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. You may also wish to play with the options to gtk_box_pack_start() while reading the next section. Try resizing the window, and observe the behavior.

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.


Previous Next Contents