Previous Next Contents

8. Container Widgets

8.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 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 inserted to.

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 *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 both be either TRUE or FALSE (0 or 1).

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.


#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 destroy (GtkWidget *widget, 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), "destroy",
                        GTK_SIGNAL_FUNC (destroy), 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 (destroy), 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;
}

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

8.2 Scrolled Windows

Scrolled windows are used to create a scrollable area inside a real window. You may insert any types of widgets to these scrolled windows, and they will all be accessable regardless of the size by using the scrollbars.

The following function is used to create a new scolled 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 arguement is the scrolled window you wish to change. The second sets the policiy for the horizontal scrollbar, and the third, 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, wheras 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.

#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 seperator 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);
    
    /* 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, wheras 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);
}

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.


Previous Next Contents