Next Previous Contents

10. Container Widgets

10.1 Notebooks

The NoteBook Widget is a collection of 'pages' that overlap each other, each page contains different information. This widget has become more common lately in GUI programming, and it is a good way to show blocks of similar information that warrant separation in their display.

The first function call you will need to know, as you can probably guess by now, is used to create a new notebook widget.

GtkWidget *gtk_notebook_new( void );

Once the notebook has been created, there are 12 functions that operate on the notebook widget. Let's look at them individually.

The first one we will look at is how to position the page indicators. These page indicators or 'tabs' as they are referred to, can be positioned in four ways: top, bottom, left, or right.

void gtk_notebook_set_tab_pos( GtkNotebook     *notebook,
                               GtkPositionType  pos );

GtkPostionType will be one of the following, and they are pretty self explanatory:

GTK_POS_TOP is the default.

Next we will look at how to add pages to the notebook. There are three ways to add pages to the NoteBook. Let's look at the first two together as they are quite similar.

void gtk_notebook_append_page( GtkNotebook *notebook,
                               GtkWidget   *child,
                               GtkWidget   *tab_label );

void gtk_notebook_prepend_page( GtkNotebook *notebook,
                                GtkWidget   *child,
                                GtkWidget   *tab_label );

These functions add pages to the notebook by inserting them from the back of the notebook (append), or the front of the notebook (prepend). child is the widget that is placed within the notebook page, and tab_label is the label for the page being added.

The final function for adding a page to the notebook contains all of the properties of the previous two, but it allows you to specify what position you want the page to be in the notebook.

void gtk_notebook_insert_page( GtkNotebook *notebook,
                               GtkWidget   *child,
                               GtkWidget   *tab_label,
                               gint         position );

The parameters are the same as _append_ and _prepend_ except it contains an extra parameter, position. This parameter is used to specify what place this page will be inserted into.

Now that we know how to add a page, lets see how we can remove a page from the notebook.

void gtk_notebook_remove_page( GtkNotebook *notebook,
                               gint         page_num );

This function takes the page specified by page_num and removes it from the widget pointed to by notebook.

To find out what the current page is in a notebook use the function:

gint gtk_notebook_current_page( GtkNotebook *notebook );

These next two functions are simple calls to move the notebook page forward or backward. Simply provide the respective function call with the notebook widget you wish to operate on. Note: when the NoteBook is currently on the last page, and gtk_notebook_next_page is called, the notebook will wrap back to the first page. Likewise, if the NoteBook is on the first page, and gtk_notebook_prev_page is called, the notebook will wrap to the last page.

void gtk_notebook_next_page( GtkNoteBook *notebook );

void gtk_notebook_prev_page( GtkNoteBook *notebook );

This next function sets the 'active' page. If you wish the notebook to be opened to page 5 for example, you would use this function. Without using this function, the notebook defaults to the first page.

void gtk_notebook_set_page( GtkNotebook *notebook,
                            gint         page_num );

The next two functions add or remove the notebook page tabs and the notebook border respectively.

void gtk_notebook_set_show_tabs( GtkNotebook *notebook,
                                 gint         show_tabs);

void gtk_notebook_set_show_border( GtkNotebook *notebook,
                                   gint         show_border );

show_tabs and show_border can be either TRUE or FALSE.

Now lets look at an example, it is expanded from the testgtk.c code that comes with the GTK distribution, and it shows all 13 functions. This small program creates a window with a notebook and six buttons. The notebook contains 11 pages, added in three different ways, appended, inserted, and prepended. The buttons allow you rotate the tab positions, add/remove the tabs and border, remove a page, change pages in both a forward and backward manner, and exit the program.

/* example-start notebook notebook.c */

#include <gtk/gtk.h>

/* This function rotates the position of the tabs */
void rotate_book (GtkButton *button, GtkNotebook *notebook)
{
    gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos +1) %4);
}

/* Add/Remove the page tabs and the borders */
void tabsborder_book (GtkButton *button, GtkNotebook *notebook)
{
    gint tval = FALSE;
    gint bval = FALSE;
    if (notebook->show_tabs == 0)
            tval = TRUE; 
    if (notebook->show_border == 0)
            bval = TRUE;
    
    gtk_notebook_set_show_tabs (notebook, tval);
    gtk_notebook_set_show_border (notebook, bval);
}

/* Remove a page from the notebook */
void remove_book (GtkButton *button, GtkNotebook *notebook)
{
    gint page;
    
    page = gtk_notebook_current_page(notebook);
    gtk_notebook_remove_page (notebook, page);
    /* Need to refresh the widget -- 
     This forces the widget to redraw itself. */
    gtk_widget_draw(GTK_WIDGET(notebook), NULL);
}

void delete (GtkWidget *widget, GtkWidget *event, gpointer data)
{
    gtk_main_quit ();
}

int main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *table;
    GtkWidget *notebook;
    GtkWidget *frame;
    GtkWidget *label;
    GtkWidget *checkbutton;
    int i;
    char bufferf[32];
    char bufferl[32];
    
    gtk_init (&argc, &argv);
    
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    
    gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                        GTK_SIGNAL_FUNC (delete), NULL);
    
    gtk_container_border_width (GTK_CONTAINER (window), 10);
    
    table = gtk_table_new(2,6,TRUE);
    gtk_container_add (GTK_CONTAINER (window), table);
    
    /* Create a new notebook, place the position of the tabs */
    notebook = gtk_notebook_new ();
    gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
    gtk_table_attach_defaults(GTK_TABLE(table), notebook, 0,6,0,1);
    gtk_widget_show(notebook);
    
    /* lets append a bunch of pages to the notebook */
    for (i=0; i < 5; i++) {
        sprintf(bufferf, "Append Frame %d", i+1);
        sprintf(bufferl, "Page %d", i+1);
        
        frame = gtk_frame_new (bufferf);
        gtk_container_border_width (GTK_CONTAINER (frame), 10);
        gtk_widget_set_usize (frame, 100, 75);
        gtk_widget_show (frame);
        
        label = gtk_label_new (bufferf);
        gtk_container_add (GTK_CONTAINER (frame), label);
        gtk_widget_show (label);
        
        label = gtk_label_new (bufferl);
        gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, label);
    }
    
    
    /* now lets add a page to a specific spot */
    checkbutton = gtk_check_button_new_with_label ("Check me please!");
    gtk_widget_set_usize(checkbutton, 100, 75);
    gtk_widget_show (checkbutton);
    
    label = gtk_label_new ("Add spot");
    gtk_container_add (GTK_CONTAINER (checkbutton), label);
    gtk_widget_show (label);
    label = gtk_label_new ("Add page");
    gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), checkbutton, label, 2);
    
    /* Now finally lets prepend pages to the notebook */
    for (i=0; i < 5; i++) {
        sprintf(bufferf, "Prepend Frame %d", i+1);
        sprintf(bufferl, "PPage %d", i+1);
        
        frame = gtk_frame_new (bufferf);
        gtk_container_border_width (GTK_CONTAINER (frame), 10);
        gtk_widget_set_usize (frame, 100, 75);
        gtk_widget_show (frame);
        
        label = gtk_label_new (bufferf);
        gtk_container_add (GTK_CONTAINER (frame), label);
        gtk_widget_show (label);
        
        label = gtk_label_new (bufferl);
        gtk_notebook_prepend_page (GTK_NOTEBOOK(notebook), frame, label);
    }
    
    /* Set what page to start at (page 4) */
    gtk_notebook_set_page (GTK_NOTEBOOK(notebook), 3);
    
    
    /* create a bunch of buttons */
    button = gtk_button_new_with_label ("close");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               GTK_SIGNAL_FUNC (delete), NULL);
    gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,1,2);
    gtk_widget_show(button);
    
    button = gtk_button_new_with_label ("next page");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               (GtkSignalFunc) gtk_notebook_next_page,
                               GTK_OBJECT (notebook));
    gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,1,2);
    gtk_widget_show(button);
    
    button = gtk_button_new_with_label ("prev page");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               (GtkSignalFunc) gtk_notebook_prev_page,
                               GTK_OBJECT (notebook));
    gtk_table_attach_defaults(GTK_TABLE(table), button, 2,3,1,2);
    gtk_widget_show(button);
    
    button = gtk_button_new_with_label ("tab position");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               (GtkSignalFunc) rotate_book, GTK_OBJECT(notebook));
    gtk_table_attach_defaults(GTK_TABLE(table), button, 3,4,1,2);
    gtk_widget_show(button);
    
    button = gtk_button_new_with_label ("tabs/border on/off");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               (GtkSignalFunc) tabsborder_book,
                               GTK_OBJECT (notebook));
    gtk_table_attach_defaults(GTK_TABLE(table), button, 4,5,1,2);
    gtk_widget_show(button);
    
    button = gtk_button_new_with_label ("remove page");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               (GtkSignalFunc) remove_book,
                               GTK_OBJECT(notebook));
    gtk_table_attach_defaults(GTK_TABLE(table), button, 5,6,1,2);
    gtk_widget_show(button);
    
    gtk_widget_show(table);
    gtk_widget_show(window);
    
    gtk_main ();
    
    return 0;
}
/* example-end */

Hopefully this helps you on your way with creating notebooks for your GTK applications.

10.2 Scrolled Windows

Scrolled windows are used to create a scrollable area inside a real window. You may insert any type of widget into a scrolled window, and it will be accessible regardless of the size by using the scrollbars.

The following function is used to create a new scrolled window.

GtkWidget *gtk_scrolled_window_new( GtkAdjustment *hadjustment,
                                    GtkAdjustment *vadjustment );

Where the first argument is the adjustment for the horizontal direction, and the second, the adjustment for the vertical direction. These are almost always set to NULL.

void gtk_scrolled_window_set_policy( GtkScrolledWindow *scrolled_window,
                                     GtkPolicyType      hscrollbar_policy,
                                     GtkPolicyType      vscrollbar_policy );

This sets the policy to be used with respect to the scrollbars. The first argument is the scrolled window you wish to change. The second sets the policy for the horizontal scrollbar, and the third the policy for the vertical scrollbar.

The policy may be one of GTK_POLICY AUTOMATIC, or GTK_POLICY_ALWAYS. GTK_POLICY_AUTOMATIC will automatically decide whether you need scrollbars, whereas GTK_POLICY_ALWAYS will always leave the scrollbars there.

Here is a simple example that packs 100 toggle buttons into a scrolled window. I've only commented on the parts that may be new to you.

/* example-start scrolledwin scrolledwin.c */

#include <gtk/gtk.h>

void destroy(GtkWidget *widget, gpointer data)
{
    gtk_main_quit();
}

int main (int argc, char *argv[])
{
    static GtkWidget *window;
    GtkWidget *scrolled_window;
    GtkWidget *table;
    GtkWidget *button;
    char buffer[32];
    int i, j;
    
    gtk_init (&argc, &argv);
    
    /* Create a new dialog window for the scrolled window to be
     * packed into.  A dialog is just like a normal window except it has a 
     * vbox and a horizontal separator packed into it.  It's just a shortcut
     * for creating dialogs */
    window = gtk_dialog_new ();
    gtk_signal_connect (GTK_OBJECT (window), "destroy",
                        (GtkSignalFunc) destroy, NULL);
    gtk_window_set_title (GTK_WINDOW (window), "dialog");
    gtk_container_border_width (GTK_CONTAINER (window), 0);
    gtk_widget_set_usize(window, 300, 300);
    
    /* create a new scrolled window. */
    scrolled_window = gtk_scrolled_window_new (NULL, NULL);
    
    gtk_container_border_width (GTK_CONTAINER (scrolled_window), 10);
    
    /* the policy is one of GTK_POLICY AUTOMATIC, or GTK_POLICY_ALWAYS.
     * GTK_POLICY_AUTOMATIC will automatically decide whether you need
     * scrollbars, whereas GTK_POLICY_ALWAYS will always leave the scrollbars
     * there.  The first one is the horizontal scrollbar, the second, 
     * the vertical. */
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
    /* The dialog window is created with a vbox packed into it. */                                                              
    gtk_box_pack_start (GTK_BOX (GTK_DIALOG(window)->vbox), scrolled_window, 
                        TRUE, TRUE, 0);
    gtk_widget_show (scrolled_window);
    
    /* create a table of 10 by 10 squares. */
    table = gtk_table_new (10, 10, FALSE);
    
    /* set the spacing to 10 on x and 10 on y */
    gtk_table_set_row_spacings (GTK_TABLE (table), 10);
    gtk_table_set_col_spacings (GTK_TABLE (table), 10);
    
    /* pack the table into the scrolled window */
    gtk_container_add (GTK_CONTAINER (scrolled_window), table);
    gtk_widget_show (table);
    
    /* this simply creates a grid of toggle buttons on the table
     * to demonstrate the scrolled window. */
    for (i = 0; i < 10; i++)
       for (j = 0; j < 10; j++) {
          sprintf (buffer, "button (%d,%d)\n", i, j);
          button = gtk_toggle_button_new_with_label (buffer);
          gtk_table_attach_defaults (GTK_TABLE (table), button,
                                     i, i+1, j, j+1);
          gtk_widget_show (button);
       }
    
    /* Add a "close" button to the bottom of the dialog */
    button = gtk_button_new_with_label ("close");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               (GtkSignalFunc) gtk_widget_destroy,
                               GTK_OBJECT (window));
    
    /* this makes it so the button is the default. */
    
    GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
    gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button, TRUE, TRUE, 0);
    
    /* This grabs this button to be the default button. Simply hitting
     * the "Enter" key will cause this button to activate. */
    gtk_widget_grab_default (button);
    gtk_widget_show (button);
    
    gtk_widget_show (window);
    
    gtk_main();
    
    return(0);
}
/* example-end */

Try playing with resizing the window. You'll notice how the scrollbars react. You may also wish to use the gtk_widget_set_usize() call to set the default size of the window or other widgets.

10.3 Paned Window Widgets

The paned window widgets are useful when you want to divide an area into two parts, with the relative size of the two parts controlled by the user. A groove is drawn between the two portions with a handle that the user can drag to change the ratio. The division can either be horizontal (HPaned) or vertical (VPaned).

To create a new paned window, call one of:

GtkWidget *gtk_hpaned_new (void);

GtkWidget *gtk_vpaned_new (void);

After creating the paned window widget, you need to add child widgets to its two halves. To do this, use the functions:

void gtk_paned_add1 (GtkPaned *paned, GtkWidget *child);

void gtk_paned_add2 (GtkPaned *paned, GtkWidget *child);

gtk_paned_add1() adds the child widget to the left or top half of the paned window. gtk_paned_add2() adds the child widget to the right or bottom half of the paned window.

As an example, we will create part of the user interface of an imaginary email program. A window is divided into two portions vertically, with the top portion being a list of email messages and the bottom portion the text of the email message. Most of the program is pretty straightforward. A couple of points to note: text can't be added to a Text widget until it is realized. This could be done by calling gtk_widget_realize(), but as a demonstration of an alternate technique, we connect a handler to the "realize" signal to add the text. Also, we need to add the GTK_SHRINK option to some of the items in the table containing the text window and its scrollbars, so that when the bottom portion is made smaller, the correct portions shrink instead of being pushed off the bottom of the window.

/* example-start paned paned.c */

#include <gtk/gtk.h>
   
/* Create the list of "messages" */
GtkWidget *
create_list (void)
{

    GtkWidget *scrolled_window;
    GtkWidget *list;
    GtkWidget *list_item;
   
    int i;
    char buffer[16];
   
    /* Create a new scrolled window, with scrollbars only if needed */
    scrolled_window = gtk_scrolled_window_new (NULL, NULL);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                    GTK_POLICY_AUTOMATIC, 
                                    GTK_POLICY_AUTOMATIC);
   
    /* Create a new list and put it in the scrolled window */
    list = gtk_list_new ();
    gtk_container_add (GTK_CONTAINER(scrolled_window), list);
    gtk_widget_show (list);
   
    /* Add some messages to the window */
    for (i=0; i<10; i++) {

        sprintf(buffer,"Message #%d",i);
        list_item = gtk_list_item_new_with_label (buffer);
        gtk_container_add (GTK_CONTAINER(list), list_item);
        gtk_widget_show (list_item);

    }
   
    return scrolled_window;
}
   
/* Add some text to our text widget - this is a callback that is invoked
when our window is realized. We could also force our window to be
realized with gtk_widget_realize, but it would have to be part of
a hierarchy first */

void
realize_text (GtkWidget *text, gpointer data)
{
    gtk_text_freeze (GTK_TEXT (text));
    gtk_text_insert (GTK_TEXT (text), NULL, &text->style->black, NULL,
    "From: pathfinder@nasa.gov\n"
    "To: mom@nasa.gov\n"
    "Subject: Made it!\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", -1);
   
    gtk_text_thaw (GTK_TEXT (text));
}
   
/* Create a scrolled text area that displays a "message" */
GtkWidget *
create_text (void)
{
    GtkWidget *table;
    GtkWidget *text;
    GtkWidget *hscrollbar;
    GtkWidget *vscrollbar;
   
    /* Create a table to hold the text widget and scrollbars */
    table = gtk_table_new (2, 2, FALSE);
   
    /* Put a text widget in the upper left hand corner. Note the use of
     * GTK_SHRINK in the y direction */
    text = gtk_text_new (NULL, NULL);
    gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1,
                      GTK_FILL | GTK_EXPAND,
                      GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
    gtk_widget_show (text);
   
    /* Put a HScrollbar in the lower left hand corner */
    hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
    gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
                      GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
    gtk_widget_show (hscrollbar);
   
    /* And a VScrollbar in the upper right */
    vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
    gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
                      GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
    gtk_widget_show (vscrollbar);
   
    /* Add a handler to put a message in the text widget when it is realized */
    gtk_signal_connect (GTK_OBJECT (text), "realize",
                        GTK_SIGNAL_FUNC (realize_text), NULL);
   
    return table;
}
   
int
main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *vpaned;
    GtkWidget *list;
    GtkWidget *text;

    gtk_init (&argc, &argv);
   
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Paned Windows");
    gtk_signal_connect (GTK_OBJECT (window), "destroy",
                        GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
    gtk_container_border_width (GTK_CONTAINER (window), 10);
   
    /* create a vpaned widget and add it to our toplevel window */
   
    vpaned = gtk_vpaned_new ();
    gtk_container_add (GTK_CONTAINER(window), vpaned);
    gtk_widget_show (vpaned);
   
    /* Now create the contents of the two halves of the window */
   
    list = create_list ();
    gtk_paned_add1 (GTK_PANED(vpaned), list);
    gtk_widget_show (list);
   
    text = create_text ();
    gtk_paned_add2 (GTK_PANED(vpaned), text);
    gtk_widget_show (text);
    gtk_widget_show (window);
    gtk_main ();
    return 0;
}
/* example-end */

10.4 Toolbar

Toolbars are usually used to group some number of widgets in order to simplify customization of their look and layout. Typically a toolbar consists of buttons with icons, labels and tooltips, but any other widget can also be put inside a toolbar. Finally, items can be arranged horizontally or vertically and buttons can be displayed with icons, labels or both.

Creating a toolbar is (as one may already suspect) done with the following function:

GtkWidget *gtk_toolbar_new( GtkOrientation orientation,
                            GtkToolbarStyle  style );

where orientation may be one of:

  GTK_ORIENTATION_HORIZONTAL    
  GTK_ORIENTATION_VERTICAL

and style one of:

  GTK_TOOLBAR_TEXT
  GTK_TOOLBAR_ICONS
  GTK_TOOLBAR_BOTH

The style applies to all the buttons created with the `item' functions (not to buttons inserted into toolbar as separate widgets).

After creating a toolbar one can append,prepend and insert items (that means simple buttons) into the toolbar. To describe an item we need a label text, a tooltip text, a private tooltip text, an icon for the button and a callback function for it. For example, to append an item you may use the following function:

GtkWidget *gtk_toolbar_append_item( GtkToolbar    *toolbar,
                                    const char    *text,
                                    const char    *tooltip_text,
                                    const char    *tooltip_private_text,
                                    GtkWidget     *icon,
                                    GtkSignalFunc  callback,
                                    gpointer       user_data );

If you want to use gtk_toolbar_insert_item, the only additional parameter which must be specified is the position in which the item should be inserted.

To simplify adding spaces between toolbar items, you may use the following function:

void gtk_toolbar_append_space( GtkToolbar *toolbar );

void gtk_toolbar_prepend_space( GtkToolbar *toolbar );

void gtk_toolbar_insert_space( GtkToolbar *toolbar,
                               gint        position );
 

While the size of the added space can be set globally for a whole toolbar with the function:

void gtk_toolbar_set_space_size( GtkToolbar *toolbar,
                                 gint        space_size) ;

If it's needed the orientation of a toolbar and its style can be changed `on the fly' using the following functions:

void gtk_toolbar_set_orientation( GtkToolbar     *toolbar,
                                  GtkOrientation  orientation );

void gtk_toolbar_set_style( GtkToolbar      *toolbar,
                            GtkToolbarStyle  style );

void gtk_toolbar_set_tooltips( GtkToolbar *toolbar,
                               gint        enable );

To show some other things that can be done with a toolbar, let's take the following program (we'll interrupt the listing with some additional explanations):

#include <gtk/gtk.h>

#include "gtk.xpm"

/* This function is connected to the Close button or
 * closing the window from the WM */
void delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
{
  gtk_main_quit ();
}

The above beginning seems for sure familiar to you if it's not your first GTK program. There is one additional thing though, we include a nice XPM picture to serve as an icon for all of the buttons.

GtkWidget* close_button; // this button will emit signal to close application
GtkWidget* tooltips_button; // to enable/disable tooltips
GtkWidget* text_button,
         * icon_button,
         * both_button; // radio buttons for toolbar style
GtkWidget* entry; // a text entry to show packing any widget into toolbar

In fact not all of the above widgets are needed here, but to make things clearer I put them all together.

/* that's easy... when one of the buttons is toggled, we just
 * check which one is active and set the style of the toolbar
 * accordingly
 * ATTENTION: our toolbar is passed as data to callback ! */
void radio_event (GtkWidget *widget, gpointer data)
{
  if (GTK_TOGGLE_BUTTON (text_button)->active) 
    gtk_toolbar_set_style(GTK_TOOLBAR ( data ), GTK_TOOLBAR_TEXT);
  else if (GTK_TOGGLE_BUTTON (icon_button)->active)
    gtk_toolbar_set_style(GTK_TOOLBAR ( data ), GTK_TOOLBAR_ICONS);
  else if (GTK_TOGGLE_BUTTON (both_button)->active)
    gtk_toolbar_set_style(GTK_TOOLBAR ( data ), GTK_TOOLBAR_BOTH);
}

/* even easier, just check given toggle button and enable/disable 
 * tooltips */
void toggle_event (GtkWidget *widget, gpointer data)
{
  gtk_toolbar_set_tooltips (GTK_TOOLBAR ( data ),
                            GTK_TOGGLE_BUTTON (widget)->active );
}

The above are just two callback functions that will be called when one of the buttons on a toolbar is pressed. You should already be familiar with things like this if you've already used toggle buttons (and radio buttons).

int main (int argc, char *argv[])
{
  /* Here is our main window (a dialog) and a handle for the handlebox */
  GtkWidget* dialog;
  GtkWidget* handlebox;

  /* Ok, we need a toolbar, an icon with a mask (one for all of 
     the buttons) and an icon widget to put this icon in (but 
     we'll create a separate widget for each button) */
  GtkWidget * toolbar;
  GdkPixmap * icon;
  GdkBitmap * mask;
  GtkWidget * iconw;

  /* this is called in all GTK application. */
  gtk_init (&argc, &argv);
  
  /* create a new window with a given title, and nice size */
  dialog = gtk_dialog_new ();
  gtk_window_set_title ( GTK_WINDOW ( dialog ) , "GTKToolbar Tutorial");
  gtk_widget_set_usize( GTK_WIDGET ( dialog ) , 600 , 300 );
  GTK_WINDOW ( dialog ) ->allow_shrink = TRUE;

  /* typically we quit if someone tries to close us */
  gtk_signal_connect ( GTK_OBJECT ( dialog ), "delete_event",
                       GTK_SIGNAL_FUNC ( delete_event ), NULL);

  /* we need to realize the window because we use pixmaps for 
   * items on the toolbar in the context of it */
  gtk_widget_realize ( dialog );

  /* to make it nice we'll put the toolbar into the handle box, 
   * so that it can be detached from the main window */
  handlebox = gtk_handle_box_new ();
  gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG(dialog)->vbox ),
                       handlebox, FALSE, FALSE, 5 );

The above should be similar to any other GTK application. Just initialization of GTK, creating the window etc.. There is only one thing that probably needs some explanation: a handle box. A handle box is just another box that can be used to pack widgets in to. The difference between it and typical boxes is that it can be detached from a parent window (or, in fact, the handle box remains in the parent, but it is reduced to a very small rectangle, while all of its contents are reparented to a new freely floating window). It is usually nice to have a detachable toolbar, so these two widgets occur together quite often.

  /* toolbar will be horizontal, with both icons and text, and
   * with 5pxl spaces between items and finally, 
   * we'll also put it into our handlebox */
  toolbar = gtk_toolbar_new ( GTK_ORIENTATION_HORIZONTAL,
                              GTK_TOOLBAR_BOTH );
  gtk_container_border_width ( GTK_CONTAINER ( toolbar ) , 5 );
  gtk_toolbar_set_space_size ( GTK_TOOLBAR ( toolbar ), 5 );
  gtk_container_add ( GTK_CONTAINER ( handlebox ) , toolbar );

  /* now we create icon with mask: we'll reuse it to create
   * icon widgets for toolbar items */
  icon = gdk_pixmap_create_from_xpm_d ( dialog->window, &mask,
      &dialog->style->white, gtk_xpm );

Well, what we do above is just a straight-forward initialization of the toolbar widget and creation of a GDK pixmap with its mask. If you want to know something more about using pixmaps, refer to GDK documentation or to the Pixmaps section earlier in this tutorial.

  /* our first item is <close> button */
  iconw = gtk_pixmap_new ( icon, mask ); // icon widget
  close_button = 
    gtk_toolbar_append_item ( GTK_TOOLBAR (toolbar), // our toolbar
                              "Close",               // button label
                              "Closes this app",     // tooltip for this button
                              "Private",             // tooltip private string
                              iconw,                 // icon widget
                              GTK_SIGNAL_FUNC (delete_event), // a signal
                              NULL );
  gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) ); // space after item

In the above code you see the simplest case: adding a button to toolbar. Just before appending a new item, we have to construct a pixmap widget to serve as an icon for this item; this step will have to be repeated for each new item. Just after the item we also add a space, so the following items will not touch each other. As you see gtk_toolbar_append_item returns a pointer to our newly created button widget, so that we can work with it in the normal way.

  /* now, let's make our radio buttons group... */
  iconw = gtk_pixmap_new ( icon, mask );
  icon_button = 
    gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
                               GTK_TOOLBAR_CHILD_RADIOBUTTON, // a type of element
                               NULL,                          // pointer to widget
                               "Icon",                        // label
                               "Only icons in toolbar",       // tooltip
                               "Private",                     // tooltip private string
                               iconw,                         // icon
                               GTK_SIGNAL_FUNC (radio_event), // signal
                               toolbar);                      // data for signal
  gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );

Here we begin creating a radio buttons group. To do this we use gtk_toolbar_append_element. In fact, using this function one can also add simple items or even spaces (type = GTK_TOOLBAR_CHILD_SPACE or GTK_TOOLBAR_CHILD_BUTTON). In the above case we start creating a radio group. In creating other radio buttons for this group a pointer to the previous button in the group is required, so that a list of buttons can be easily constructed (see the section on Radio Buttons earlier in this tutorial).

  /* following radio buttons refer to previous ones */
  iconw = gtk_pixmap_new ( icon, mask );
  text_button = 
    gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
                               GTK_TOOLBAR_CHILD_RADIOBUTTON,
                               icon_button,
                               "Text",
                               "Only texts in toolbar",
                               "Private",
                               iconw,
                               GTK_SIGNAL_FUNC (radio_event),
                               toolbar);
  gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );
                                          
  iconw = gtk_pixmap_new ( icon, mask );
  both_button = 
    gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
                               GTK_TOOLBAR_CHILD_RADIOBUTTON,
                               text_button,
                               "Both",
                               "Icons and text in toolbar",
                               "Private",
                               iconw,
                               GTK_SIGNAL_FUNC (radio_event),
                               toolbar);
  gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(both_button),TRUE);

In the end we have set the state of one of the buttons manually (otherwise they all stay in active state, preventing us from switching between them).

  /* here we have just a simple toggle button */
  iconw = gtk_pixmap_new ( icon, mask );
  tooltips_button = 
    gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
                               GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
                               NULL,
                               "Tooltips",
                               "Toolbar with or without tips",
                               "Private",
                               iconw,
                               GTK_SIGNAL_FUNC (toggle_event),
                               toolbar);
  gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(tooltips_button),TRUE);

A toggle button can be created in the obvious way (if one knows how to create radio buttons already).

  /* to pack a widget into toolbar, we only have to 
   * create it and append it with an appropriate tooltip */
  entry = gtk_entry_new ();
  gtk_toolbar_append_widget( GTK_TOOLBAR (toolbar), 
                             entry, 
                             "This is just an entry", 
                             "Private" );

  /* well, it isn't created within thetoolbar, so we must still show it */
  gtk_widget_show ( entry );

As you see, adding any kind of widget to a toolbar is simple. The one thing you have to remember is that this widget must be shown manually (contrary to other items which will be shown together with the toolbar).

  /* that's it ! let's show everything. */
  gtk_widget_show ( toolbar );
  gtk_widget_show (handlebox);
  gtk_widget_show ( dialog );

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

So, here we are at the end of toolbar tutorial. Of course, to appreciate it in full you need also this nice XPM icon, so here it is:

/* XPM */
static char * gtk_xpm[] = {
"32 39 5 1",
".      c none",
"+      c black",
"@      c #3070E0",
"#      c #F05050",
"$      c #35E035",
"................+...............",
"..............+++++.............",
"............+++++@@++...........",
"..........+++++@@@@@@++.........",
"........++++@@@@@@@@@@++........",
"......++++@@++++++++@@@++.......",
".....+++@@@+++++++++++@@@++.....",
"...+++@@@@+++@@@@@@++++@@@@+....",
"..+++@@@@+++@@@@@@@@+++@@@@@++..",
".++@@@@@@+++@@@@@@@@@@@@@@@@@@++",
".+#+@@@@@@++@@@@+++@@@@@@@@@@@@+",
".+##++@@@@+++@@@+++++@@@@@@@@$@.",
".+###++@@@@+++@@@+++@@@@@++$$$@.",
".+####+++@@@+++++++@@@@@+@$$$$@.",
".+#####+++@@@@+++@@@@++@$$$$$$+.",
".+######++++@@@@@@@++@$$$$$$$$+.",
".+#######+##+@@@@+++$$$$$$@@$$+.",
".+###+++##+##+@@++@$$$$$$++$$$+.",
".+###++++##+##+@@$$$$$$$@+@$$@+.",
".+###++++++#+++@$$@+@$$@++$$$@+.",
".+####+++++++#++$$@+@$$++$$$$+..",
".++####++++++#++$$@+@$++@$$$$+..",
".+#####+++++##++$$++@+++$$$$$+..",
".++####+++##+#++$$+++++@$$$$$+..",
".++####+++####++$$++++++@$$$@+..",
".+#####++#####++$$+++@++++@$@+..",
".+#####++#####++$$++@$$@+++$@@..",
".++####++#####++$$++$$$$$+@$@++.",
".++####++#####++$$++$$$$$$$$+++.",
".+++####+#####++$$++$$$$$$$@+++.",
"..+++#########+@$$+@$$$$$$+++...",
"...+++########+@$$$$$$$$@+++....",
".....+++######+@$$$$$$$+++......",
"......+++#####+@$$$$$@++........",
".......+++####+@$$$$+++.........",
".........++###+$$$@++...........",
"..........++##+$@+++............",
"...........+++++++..............",
".............++++..............."};

10.5 Aspect Frames

The aspect frame widget is like a frame widget, except that it also enforces the aspect ratio (that is, the ratio of the width to the height) of the child widget to have a certain value, adding extra space if necessary. This is useful, for instance, if you want to preview a larger image. The size of the preview should vary when the user resizes the window, but the aspect ratio needs to always match the original image.

To create a new aspect frame use:

GtkWidget *gtk_aspect_frame_new( const gchar *label,
                                 gfloat       xalign,
                                 gfloat       yalign,
                                 gfloat       ratio,
                                 gint         obey_child);

xalign and yalign specify alignment as with Alignment widgets. If obey_child is true, the aspect ratio of a child widget will match the aspect ratio of the ideal size it requests. Otherwise, it is given by ratio.

To change the options of an existing aspect frame, you can use:

void gtk_aspect_frame_set( GtkAspectFrame *aspect_frame,
                           gfloat          xalign,
                           gfloat          yalign,
                           gfloat          ratio,
                           gint            obey_child);

As an example, the following program uses an AspectFrame to present a drawing area whose aspect ratio will always be 2:1, no matter how the user resizes the top-level window.

/* example-start aspectframe aspectframe.c */

#include <gtk/gtk.h>
   
int
main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *aspect_frame;
    GtkWidget *drawing_area;
    gtk_init (&argc, &argv);
   
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame");
    gtk_signal_connect (GTK_OBJECT (window), "destroy",
                        GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
    gtk_container_border_width (GTK_CONTAINER (window), 10);
   
    /* Create an aspect_frame and add it to our toplevel window */
   
    aspect_frame = gtk_aspect_frame_new ("2x1", /* label */
                                         0.5, /* center x */
                                         0.5, /* center y */
                                         2, /* xsize/ysize = 2 */
                                         FALSE /* ignore child's aspect */);
   
    gtk_container_add (GTK_CONTAINER(window), aspect_frame);
    gtk_widget_show (aspect_frame);
   
    /* Now add a child widget to the aspect frame */
   
    drawing_area = gtk_drawing_area_new ();
   
    /* Ask for a 200x200 window, but the AspectFrame will give us a 200x100
     * window since we are forcing a 2x1 aspect ratio */
    gtk_widget_set_usize (drawing_area, 200, 200);
    gtk_container_add (GTK_CONTAINER(aspect_frame), drawing_area);
    gtk_widget_show (drawing_area);
   
    gtk_widget_show (window);
    gtk_main ();
    return 0;
}
/* example-end */


Next Previous Contents