Next Previous Contents

8. Miscallaneous Widgets

8.1 Labels

Labels are used a lot in GTK, and are relatively simple. Labels emit no signals as they do not have an associated X window. If you need to catch signals, or do clipping, use the EventBox widget.

To create a new label, use:

GtkWidget *gtk_label_new( char *str );

Where the sole argument is the string you wish the label to display.

To change the label's text after creation, use the function:

void gtk_label_set( GtkLabel *label,
                    char     *str );

Where the first argument is the label you created previously (cast using the GTK_LABEL() macro), and the second is the new string.

The space needed for the new string will be automatically adjusted if needed.

To retrieve the current string, use:

void gtk_label_get( GtkLabel  *label,
                    char     **str );

Where the first arguement is the label you've created, and the second, the return for the string.

8.2 The Tooltips Widget

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

Some widgets (such as the label) will not work with tooltips.

The first call you will use to create a new tooltip. You only need to do this once in a given function. The GtkTooltip object 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_tip( GtkTooltips *tooltips,
                           GtkWidget   *widget,
                           const gchar *tip_text,
                           const gchar *tip_private );

The first argument 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. The last argument is a text string that can be used as an identifier when using GtkTipsQuery to implement context sensitive help. For now, you can set it to NULL.

Here's a short example:

GtkTooltips *tooltips;
GtkWidget *button;
...
tooltips = gtk_tooltips_new ();
button = gtk_button_new_with_label ("button 1");
...
gtk_tooltips_set_tip (tooltips, button, "This is button 1", NULL);

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

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

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

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

8.3 Progress Bars

Progress bars are used to show the status of an operation. They are pretty easy to use, as you will see with the code below. But first lets start out with the call to create a new progress bar.

GtkWidget *gtk_progress_bar_new( void );

Now that the progress bar has been created we can use it.

void gtk_progress_bar_update( GtkProgressBar *pbar,
                              gfloat          percentage );

The first argument is the progress bar you wish to operate on, and the second argument is the amount 'completed', meaning the amount the progress bar has been filled from 0-100%. This is passed to the function as a real number ranging from 0 to 1.

Progress Bars are usually used with timeouts or other such functions (see section on Timeouts, I/O and Idle Functions) to give the illusion of multitasking. All will employ the gtk_progress_bar_update function in the same manner.

Here is an example of the progress bar, updated using timeouts. This code also shows you how to reset the Progress Bar.

/* example-start progressbar progressbar.c */

#include <gtk/gtk.h>

static int ptimer = 0;
int pstat = TRUE;

/* This function increments and updates the progress bar, it also resets
 the progress bar if pstat is FALSE */
gint progress (gpointer data)
{
    gfloat pvalue;
    
    /* get the current value of the progress bar */
    pvalue = GTK_PROGRESS_BAR (data)->percentage;
    
    if ((pvalue >= 1.0) || (pstat == FALSE)) {
        pvalue = 0.0;
        pstat = TRUE;
    }
    pvalue += 0.01;
    
    gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);
    
    return TRUE;
}

/* This function signals a reset of the progress bar */
void progress_r (void)
{  
    pstat = FALSE;  
}

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

int main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *label;
    GtkWidget *table;
    GtkWidget *pbar;
    
    gtk_init (&argc, &argv);
    
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    
    gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                        GTK_SIGNAL_FUNC (destroy), NULL);
    
    gtk_container_border_width (GTK_CONTAINER (window), 10);
    
    table = gtk_table_new(3,2,TRUE);
    gtk_container_add (GTK_CONTAINER (window), table);
    
    label = gtk_label_new ("Progress Bar Example");
    gtk_table_attach_defaults(GTK_TABLE(table), label, 0,2,0,1);
    gtk_widget_show(label);
    
    /* Create a new progress bar, pack it into the table, and show it */
    pbar = gtk_progress_bar_new ();
    gtk_table_attach_defaults(GTK_TABLE(table), pbar, 0,2,1,2);
    gtk_widget_show (pbar);
    
    /* Set the timeout to handle automatic updating of the progress bar */
    ptimer = gtk_timeout_add (100, progress, pbar);
    
    /* This button signals the progress bar to be reset */
    button = gtk_button_new_with_label ("Reset");
    gtk_signal_connect (GTK_OBJECT (button), "clicked",
                        GTK_SIGNAL_FUNC (progress_r), NULL);
    gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,2,3);
    gtk_widget_show(button);
    
    button = gtk_button_new_with_label ("Cancel");
    gtk_signal_connect (GTK_OBJECT (button), "clicked",
                        GTK_SIGNAL_FUNC (destroy), NULL);
    
    gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,2,3);
    gtk_widget_show (button);
    
    gtk_widget_show(table);
    gtk_widget_show(window);
    
    gtk_main ();
    
    return 0;
}
/* example-end */

In this small program there are four areas that concern the general operation of Progress Bars, we will look at them in the order they are called.

    pbar = gtk_progress_bar_new ();

This code creates a new progress bar, called pbar.

    ptimer = gtk_timeout_add (100, progress, pbar);

This code uses timeouts to enable a constant time interval, timeouts are not necessary in the use of Progress Bars.

    pvalue = GTK_PROGRESS_BAR (data)->percentage;

This code assigns the current value of the percentage bar to pvalue.

    gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);

Finally, this code updates the progress bar with the value of pvalue

And that is all there is to know about Progress Bars, enjoy.

8.4 Dialogs

The Dialog widget is very simple, and is actually just a window with a few things pre-packed into it for you. The structure for a Dialog is:

struct GtkDialog
{
      GtkWindow window;
    
      GtkWidget *vbox;
      GtkWidget *action_area;
};

So you see, it simply creates a window, and then packs a vbox into the top, then a seperator, and then an hbox for the "action_area".

The Dialog widget can be used for pop-up messages to the user, and other similar tasks. It is really basic, and there is only one function for the dialog box, which is:

GtkWidget *gtk_dialog_new( void );

So to create a new dialog box, use,

GtkWidget *window;
window = gtk_dialog_new ();

This will create the dialog box, and it is now up to you to use it. you could pack a button in the action_area by doing something like this:

button = ...
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button,
                    TRUE, TRUE, 0);
gtk_widget_show (button);

And you could add to the vbox area by packing, for instance, a label in it, try something like this:

label = gtk_label_new ("Dialogs are groovy");
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), label, TRUE,
                    TRUE, 0);
gtk_widget_show (label);

As an example in using the dialog box, you could put two buttons in the action_area, a Cancel button and an Ok button, and a label in the vbox area, asking the user a question or giving an error etc. Then you could attach a different signal to each of the buttons and perform the operation the user selects.

If the simple functionality provided by the default vertical and horizontal boxes in the two areas don't give you enough control for your application, then you can simply pack another layout widget into the boxes provided. For example, you could pack a table into the vertical box.

8.5 Pixmaps

Pixmaps are data structures that contain pictures. These pictures can be used in various places, but most visibly as icons on the X-Windows desktop, or as cursors. A bitmap is a 2-color pixmap.

To use pixmaps in GTK, we must first build a GdkPixmap structure using routines from the GDK layer. Pixmaps can either be created from in-memory data, or from data read from a file. We'll go through each of the calls to create a pixmap.

GdkPixmap *gdk_bitmap_create_from_data( GdkWindow *window,
                                        gchar     *data,
                                        gint       width,
                                        gint       height );

This routine is used to create a single-plane pixmap (2 colors) from data in memory. Each bit of the data represents whether that pixel is off or on. Width and height are in pixels. The GdkWindow pointer is to the current window, since a pixmap resources are meaningful only in the context of the screen where it is to be displayed.

GdkPixmap *gdk_pixmap_create_from_data( GdkWindow *window,
                                        gchar     *data,
                                        gint       width,
                                        gint       height,
                                        gint       depth,
                                        GdkColor  *fg,
                                        GdkColor  *bg );

This is used to create a pixmap of the given depth (number of colors) from the bitmap data specified. fg and bg are the foreground and background color to use.

GdkPixmap *gdk_pixmap_create_from_xpm( GdkWindow   *window,
                                       GdkBitmap  **mask,
                                       GdkColor    *transparent_color,
                                       const gchar *filename );

XPM format is a readable pixmap representation for the X Window System. It is widely used and many different utilities are available for creating image files in this format. The file specified by filename must contain an image in that format and it is loaded into the pixmap structure. The mask specifies which bits of the pixmap are opaque. All other bits are colored using the color specified by transparent_color. An example using this follows below.

GdkPixmap *gdk_pixmap_create_from_xpm_d( GdkWindow  *window,
                                         GdkBitmap **mask,
                                         GdkColor   *transparent_color,
                                         gchar     **data );

Small images can be incorporated into a program as data in the XPM format. A pixmap is created using this data, instead of reading it from a file. An example of such data is

/* XPM */
static const char * xpm_data[] = {
"16 16 3 1",
"       c None",
".      c #000000000000",
"X      c #FFFFFFFFFFFF",
"                ",
"   ......       ",
"   .XXX.X.      ",
"   .XXX.XX.     ",
"   .XXX.XXX.    ",
"   .XXX.....    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .........    ",
"                ",
"                "};

When we're done using a pixmap and not likely to reuse it again soon, it is a good idea to release the resource using gdk_pixmap_unref(). Pixmaps should be considered a precious resource.

Once we've created a pixmap, we can display it as a GTK widget. We must create a pixmap widget to contain the GDK pixmap. This is done using

GtkWidget *gtk_pixmap_new( GdkPixmap *pixmap,
                           GdkBitmap *mask );

The other pixmap widget calls are

guint gtk_pixmap_get_type( void );

void  gtk_pixmap_set( GtkPixmap  *pixmap,
                      GdkPixmap  *val,
                      GdkBitmap  *mask );

void  gtk_pixmap_get( GtkPixmap  *pixmap,
                      GdkPixmap **val,
                      GdkBitmap **mask);

gtk_pixmap_set is used to change the pixmap that the widget is currently managing. Val is the pixmap created using GDK.

The following is an example of using a pixmap in a button.

/* example-start pixmap pixmap.c */

#include <gtk/gtk.h>


/* XPM data of Open-File icon */
static const char * xpm_data[] = {
"16 16 3 1",
"       c None",
".      c #000000000000",
"X      c #FFFFFFFFFFFF",
"                ",
"   ......       ",
"   .XXX.X.      ",
"   .XXX.XX.     ",
"   .XXX.XXX.    ",
"   .XXX.....    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .........    ",
"                ",
"                "};


/* when invoked (via signal delete_event), terminates the application.
 */
void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
    gtk_main_quit();
}


/* is invoked when the button is clicked.  It just prints a message.
 */
void button_clicked( GtkWidget *widget, gpointer data ) {
    printf( "button clicked\n" );
}

int main( int argc, char *argv[] )
{
    /* GtkWidget is the storage type for widgets */
    GtkWidget *window, *pixmapwid, *button;
    GdkPixmap *pixmap;
    GdkBitmap *mask;
    GtkStyle *style;
    
    /* create the main window, and attach delete_event signal to terminating
       the application */
    gtk_init( &argc, &argv );
    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    gtk_signal_connect( GTK_OBJECT (window), "delete_event",
                        GTK_SIGNAL_FUNC (close_application), NULL );
    gtk_container_border_width( GTK_CONTAINER (window), 10 );
    gtk_widget_show( window );

    /* now for the pixmap from gdk */
    style = gtk_widget_get_style( window );
    pixmap = gdk_pixmap_create_from_xpm_d( window->window,  &mask,
                                           &style->bg[GTK_STATE_NORMAL],
                                           (gchar **)xpm_data );

    /* a pixmap widget to contain the pixmap */
    pixmapwid = gtk_pixmap_new( pixmap, mask );
    gtk_widget_show( pixmapwid );

    /* a button to contain the pixmap widget */
    button = gtk_button_new();
    gtk_container_add( GTK_CONTAINER(button), pixmapwid );
    gtk_container_add( GTK_CONTAINER(window), button );
    gtk_widget_show( button );

    gtk_signal_connect( GTK_OBJECT(button), "clicked",
                        GTK_SIGNAL_FUNC(button_clicked), NULL );

    /* show the window */
    gtk_main ();
          
    return 0;
}
/* example-end */

To load a file from an XPM data file called icon0.xpm in the current directory, we would have created the pixmap thus

    /* load a pixmap from a file */
    pixmap = gdk_pixmap_create_from_xpm( window->window, &mask,
                                         &style->bg[GTK_STATE_NORMAL],
                                         "./icon0.xpm" );
    pixmapwid = gtk_pixmap_new( pixmap, mask );
    gtk_widget_show( pixmapwid );
    gtk_container_add( GTK_CONTAINER(window), pixmapwid );

A disadvantage of using pixmaps is that the displayed object is always rectangular, regardless of the image. We would like to create desktops and applications with icons that have more natural shapes. For example, for a game interface, we would like to have round buttons to push. The way to do this is using shaped windows.

A shaped window is simply a pixmap where the background pixels are transparent. This way, when the background image is multi-colored, we don't overwrite it with a rectangular, non-matching border around our icon. The following example displays a full wheelbarrow image on the desktop.

/* example-start wheelbarrow wheelbarrow.c */

#include <gtk/gtk.h>

/* XPM */
static char * WheelbarrowFull_xpm[] = {
"48 48 64 1",
"       c None",
".      c #DF7DCF3CC71B",
"X      c #965875D669A6",
"o      c #71C671C671C6",
"O      c #A699A289A699",
"+      c #965892489658",
"@      c #8E38410330C2",
"#      c #D75C7DF769A6",
"$      c #F7DECF3CC71B",
"%      c #96588A288E38",
"&      c #A69992489E79",
"*      c #8E3886178E38",
"=      c #104008200820",
"-      c #596510401040",
";      c #C71B30C230C2",
":      c #C71B9A699658",
">      c #618561856185",
",      c #20811C712081",
"<      c #104000000000",
"1      c #861720812081",
"2      c #DF7D4D344103",
"3      c #79E769A671C6",
"4      c #861782078617",
"5      c #41033CF34103",
"6      c #000000000000",
"7      c #49241C711040",
"8      c #492445144924",
"9      c #082008200820",
"0      c #69A618611861",
"q      c #B6DA71C65144",
"w      c #410330C238E3",
"e      c #CF3CBAEAB6DA",
"r      c #71C6451430C2",
"t      c #EFBEDB6CD75C",
"y      c #28A208200820",
"u      c #186110401040",
"i      c #596528A21861",
"p      c #71C661855965",
"a      c #A69996589658",
"s      c #30C228A230C2",
"d      c #BEFBA289AEBA",
"f      c #596545145144",
"g      c #30C230C230C2",
"h      c #8E3882078617",
"j      c #208118612081",
"k      c #38E30C300820",
"l      c #30C2208128A2",
"z      c #38E328A238E3",
"x      c #514438E34924",
"c      c #618555555965",
"v      c #30C2208130C2",
"b      c #38E328A230C2",
"n      c #28A228A228A2",
"m      c #41032CB228A2",
"M      c #104010401040",
"N      c #492438E34103",
"B      c #28A2208128A2",
"V      c #A699596538E3",
"C      c #30C21C711040",
"Z      c #30C218611040",
"A      c #965865955965",
"S      c #618534D32081",
"D      c #38E31C711040",
"F      c #082000000820",
"                                                ",
"          .XoO                                  ",
"         +@#$%o&                                ",
"         *=-;#::o+                              ",
"           >,<12#:34                            ",
"             45671#:X3                          ",
"               +89<02qwo                        ",
"e*                >,67;ro                       ",
"ty>                 459@>+&&                    ",
"$2u+                  ><ipas8*                  ",
"%$;=*                *3:.Xa.dfg>                ",
"Oh$;ya             *3d.a8j,Xe.d3g8+             ",
" Oh$;ka          *3d$a8lz,,xxc:.e3g54           ",
"  Oh$;kO       *pd$%svbzz,sxxxxfX..&wn>         ",
"   Oh$@mO    *3dthwlsslszjzxxxxxxx3:td8M4       ",
"    Oh$@g& *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B*     ",
"     Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5&   ",
"      Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM*  ",
"       OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ",
"        2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ",
"        :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo",
"         +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g",
"          *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&en",
"           p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>",
"           OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ",
"            3206Bwxxszx%et.eaAp77m77mmmf3&eeeg* ",
"             @26MvzxNzvlbwfpdettttttttttt.c,n&  ",
"             *;16=lsNwwNwgsvslbwwvccc3pcfu<o    ",
"              p;<69BvwwsszslllbBlllllllu<5+     ",
"              OS0y6FBlvvvzvzss,u=Blllj=54       ",
"               c1-699Blvlllllu7k96MMMg4         ",
"               *10y8n6FjvllllB<166668           ",
"                S-kg+>666<M<996-y6n<8*          ",
"                p71=4 m69996kD8Z-66698&&        ",
"                &i0ycm6n4 ogk17,0<6666g         ",
"                 N-k-<>     >=01-kuu666>        ",
"                 ,6ky&      &46-10ul,66,        ",
"                 Ou0<>       o66y<ulw<66&       ",
"                  *kk5       >66By7=xu664       ",
"                   <<M4      466lj<Mxu66o       ",
"                   *>>       +66uv,zN666*       ",
"                              566,xxj669        ",
"                              4666FF666>        ",
"                               >966666M         ",
"                                oM6668+         ",
"                                  *4            ",
"                                                ",
"                                                "};


/* when invoked (via signal delete_event), terminates the application.
 */
void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
    gtk_main_quit();
}

int main (int argc, char *argv[])
{
    /* GtkWidget is the storage type for widgets */
    GtkWidget *window, *pixmap, *fixed;
    GdkPixmap *gdk_pixmap;
    GdkBitmap *mask;
    GtkStyle *style;
    GdkGC *gc;
    
    /* create the main window, and attach delete_event signal to terminate
       the application.  Note that the main window will not have a titlebar
       since we're making it a popup. */
    gtk_init (&argc, &argv);
    window = gtk_window_new( GTK_WINDOW_POPUP );
    gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                        GTK_SIGNAL_FUNC (close_application), NULL);
    gtk_widget_show (window);

    /* now for the pixmap and the pixmap widget */
    style = gtk_widget_get_default_style();
    gc = style->black_gc;
    gdk_pixmap = gdk_pixmap_create_from_xpm_d( window->window, &mask,
                                             &style->bg[GTK_STATE_NORMAL],
                                             WheelbarrowFull_xpm );
    pixmap = gtk_pixmap_new( gdk_pixmap, mask );
    gtk_widget_show( pixmap );

    /* To display the pixmap, we use a fixed widget to place the pixmap */
    fixed = gtk_fixed_new();
    gtk_widget_set_usize( fixed, 200, 200 );
    gtk_fixed_put( GTK_FIXED(fixed), pixmap, 0, 0 );
    gtk_container_add( GTK_CONTAINER(window), fixed );
    gtk_widget_show( fixed );

    /* This masks out everything except for the image itself */
    gtk_widget_shape_combine_mask( window, mask, 0, 0 );
    
    /* show the window */
    gtk_widget_set_uposition( window, 20, 400 );
    gtk_widget_show( window );
    gtk_main ();
          
    return 0;
}
/* example-end */

To make the wheelbarrow image sensitive, we could attach the button press event signal to make it do something. The following few lines would make the picture sensitive to a mouse button being pressed which makes the application terminate.

gtk_widget_set_events( window,
                       gtk_widget_get_events( window ) |
                       GDK_BUTTON_PRESS_MASK );

gtk_signal_connect( GTK_OBJECT(window), "button_press_event",
                    GTK_SIGNAL_FUNC(close_application), NULL );

8.6 Rulers

Ruler widgets are used to indicate the location of the mouse pointer in a given window. A window can have a vertical ruler spanning across the width and a horizontal ruler spanning down the height. A small triangular indicator on the ruler shows the exact location of the pointer relative to the ruler.

A ruler must first be created. Horizontal and vertical rulers are created using

GtkWidget *gtk_hruler_new( void );    /* horizontal ruler */
GtkWidget *gtk_vruler_new( void );    /* vertical ruler   */

Once a ruler is created, we can define the unit of measurement. Units of measure for rulers can be GTK_PIXELS, GTK_INCHES or GTK_CENTIMETERS. This is set using

void gtk_ruler_set_metric( GtkRuler      *ruler,
                           GtkMetricType  metric );

The default measure is GTK_PIXELS.

gtk_ruler_set_metric( GTK_RULER(ruler), GTK_PIXELS );

Other important characteristics of a ruler are how to mark the units of scale and where the position indicator is initially placed. These are set for a ruler using

void gtk_ruler_set_range( GtkRuler *ruler,
                          gfloat    lower,
                          gfloat    upper,
                          gfloat    position,
                          gfloat    max_size );

The lower and upper arguments define the extent of the ruler, and max_size is the largest possible number that will be displayed. Position defines the initial position of the pointer indicator within the ruler.

A vertical ruler can span an 800 pixel wide window thus

gtk_ruler_set_range( GTK_RULER(vruler), 0, 800, 0, 800);

The markings displayed on the ruler will be from 0 to 800, with a number for every 100 pixels. If instead we wanted the ruler to range from 7 to 16, we would code

gtk_ruler_set_range( GTK_RULER(vruler), 7, 16, 0, 20);

The indicator on the ruler is a small triangular mark that indicates the position of the pointer relative to the ruler. If the ruler is used to follow the mouse pointer, the motion_notify_event signal should be connected to the motion_notify_event method of the ruler. To follow all mouse movements within a window area, we would use

#define EVENT_METHOD(i, x) GTK_WIDGET_CLASS(GTK_OBJECT(i)->klass)->x

gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
         (GtkSignalFunc)EVENT_METHOD(ruler, motion_notify_event),
         GTK_OBJECT(ruler) );

The following example creates a drawing area with a horizontal ruler above it and a vertical ruler to the left of it. The size of the drawing area is 600 pixels wide by 400 pixels high. The horizontal ruler spans from 7 to 13 with a mark every 100 pixels, while the vertical ruler spans from 0 to 400 with a mark every 100 pixels. Placement of the drawing area and the rulers are done using a table.

/* example-start rulers rulers.c */

#include <gtk/gtk.h>

#define EVENT_METHOD(i, x) GTK_WIDGET_CLASS(GTK_OBJECT(i)->klass)->x

#define XSIZE  600
#define YSIZE  400

/* this routine gets control when the close button is clicked
 */
void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
    gtk_main_quit();
}


/* the main routine
 */
int main( int argc, char *argv[] ) {
    GtkWidget *window, *table, *area, *hrule, *vrule;

    /* initialize gtk and create the main window */
    gtk_init( &argc, &argv );

    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    gtk_signal_connect (GTK_OBJECT (window), "delete_event",
            GTK_SIGNAL_FUNC( close_application ), NULL);
    gtk_container_border_width (GTK_CONTAINER (window), 10);

    /* create a table for placing the ruler and the drawing area */
    table = gtk_table_new( 3, 2, FALSE );
    gtk_container_add( GTK_CONTAINER(window), table );

    area = gtk_drawing_area_new();
    gtk_drawing_area_size( (GtkDrawingArea *)area, XSIZE, YSIZE );
    gtk_table_attach( GTK_TABLE(table), area, 1, 2, 1, 2,
                      GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0 );
    gtk_widget_set_events( area, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK );

    /* The horizontal ruler goes on top.  As the mouse moves across the drawing area,
       a motion_notify_event is passed to the appropriate event handler for the ruler. */
    hrule = gtk_hruler_new();
    gtk_ruler_set_metric( GTK_RULER(hrule), GTK_PIXELS );
    gtk_ruler_set_range( GTK_RULER(hrule), 7, 13, 0, 20 );
    gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
                               (GtkSignalFunc)EVENT_METHOD(hrule, motion_notify_event),
                               GTK_OBJECT(hrule) );
    /*  GTK_WIDGET_CLASS(GTK_OBJECT(hrule)->klass)->motion_notify_event, */
    gtk_table_attach( GTK_TABLE(table), hrule, 1, 2, 0, 1,
                      GTK_EXPAND|GTK_SHRINK|GTK_FILL, GTK_FILL, 0, 0 );
    
    /* The vertical ruler goes on the left.  As the mouse moves across the drawing area,
       a motion_notify_event is passed to the appropriate event handler for the ruler. */
    vrule = gtk_vruler_new();
    gtk_ruler_set_metric( GTK_RULER(vrule), GTK_PIXELS );
    gtk_ruler_set_range( GTK_RULER(vrule), 0, YSIZE, 10, YSIZE );
    gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
                               (GtkSignalFunc)
                                  GTK_WIDGET_CLASS(GTK_OBJECT(vrule)->klass)->motion_notify_event,
                               GTK_OBJECT(vrule) );
    gtk_table_attach( GTK_TABLE(table), vrule, 0, 1, 1, 2,
                      GTK_FILL, GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0 );

    /* now show everything */
    gtk_widget_show( area );
    gtk_widget_show( hrule );
    gtk_widget_show( vrule );
    gtk_widget_show( table );
    gtk_widget_show( window );
    gtk_main();

    return 0;
}
/* example-end */

8.7 Statusbars

Statusbars are simple widgets used to display a text message. They keep a stack of the messages pushed onto them, so that popping the current message will re-display the previous text message.

In order to allow different parts of an application to use the same statusbar to display messages, the statusbar widget issues Context Identifiers which are used to identify different 'users'. The message on top of the stack is the one displayed, no matter what context it is in. Messages are stacked in last-in-first-out order, not context identifier order.

A statusbar is created with a call to:

GtkWidget *gtk_statusbar_new( void );

A new Context Identifier is requested using a call to the following function with a short textual description of the context:

guint gtk_statusbar_get_context_id( GtkStatusbar *statusbar,
                                    const gchar  *context_description );

There are three functions that can operate on statusbars:

guint gtk_statusbar_push( GtkStatusbar *statusbar,
                          guint         context_id,
                          gchar        *text );

void gtk_statusbar_pop( GtkStatusbar *statusbar)
                        guint         context_id );

void gtk_statusbar_remove( GtkStatusbar *statusbar,
                           guint         context_id,
                           guint         message_id ); 

The first, gtk_statusbar_push, is used to add a new message to the statusbar. It returns a Message Identifier, which can be passed later to the function gtk_statusbar_remove to remove the message with the given Message and Context Identifiers from the statusbar's stack.

The function gtk_statusbar_pop removes the message highest in the stack with the given Context Identifier.

The following example creates a statusbar and two buttons, one for pushing items onto the statusbar, and one for popping the last item back off.

/* example-start statusbar statusbar.c */

#include <gtk/gtk.h>
#include <glib.h>

GtkWidget *status_bar;

void push_item (GtkWidget *widget, gpointer data)
{
  static int count = 1;
  char buff[20];

  g_snprintf(buff, 20, "Item %d", count++);
  gtk_statusbar_push( GTK_STATUSBAR(status_bar), (guint) &data, buff);

  return;
}

void pop_item (GtkWidget *widget, gpointer data)
{
  gtk_statusbar_pop( GTK_STATUSBAR(status_bar), (guint) &data );
  return;
}

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

    GtkWidget *window;
    GtkWidget *vbox;
    GtkWidget *button;

    int context_id;

    gtk_init (&argc, &argv);

    /* create a new window */
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_usize( GTK_WIDGET (window), 200, 100);
    gtk_window_set_title(GTK_WINDOW (window), "GTK Statusbar Example");
    gtk_signal_connect(GTK_OBJECT (window), "delete_event",
                       (GtkSignalFunc) gtk_exit, NULL);
 
    vbox = gtk_vbox_new(FALSE, 1);
    gtk_container_add(GTK_CONTAINER(window), vbox);
    gtk_widget_show(vbox);
          
    status_bar = gtk_statusbar_new();      
    gtk_box_pack_start (GTK_BOX (vbox), status_bar, TRUE, TRUE, 0);
    gtk_widget_show (status_bar);

    context_id = gtk_statusbar_get_context_id( GTK_STATUSBAR(status_bar), "Statusbar example");

    button = gtk_button_new_with_label("push item");
    gtk_signal_connect(GTK_OBJECT(button), "clicked",
        GTK_SIGNAL_FUNC (push_item), &context_id);
    gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 2);
    gtk_widget_show(button);              

    button = gtk_button_new_with_label("pop last item");
    gtk_signal_connect(GTK_OBJECT(button), "clicked",
        GTK_SIGNAL_FUNC (pop_item), &context_id);
    gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 2);
    gtk_widget_show(button);              

    /* 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;
}
/* example-end */

8.8 Text Entries

The Entry widget allows text to be typed and displayed in a single line text box. The text may be set with function calls that allow new text to replace, prepend or append the current contents of the Entry widget.

There are two functions for creating Entry widgets:

GtkWidget *gtk_entry_new( void );

GtkWidget *gtk_entry_new_with_max_length( guint16 max );

The first just creates a new Entry widget, whilst the second creates a new Entry and sets a limit on the length of the text within the Entry.

There are several functions for altering the text which is currently within the Entry widget.

void gtk_entry_set_text( GtkEntry    *entry,
                         const gchar *text );

void gtk_entry_append_text( GtkEntry    *entry,
                            const gchar *text );

void gtk_entry_prepend_text( GtkEntry    *entry,
                             const gchar *text );

The function gtk_entry_set_text sets the contents of the Entry widget, replacing the current contents. The functions gtk_entry_append_text and gtk_entry_prepend_text allow the current contents to be appended and prepended to.

The next function allows the current insertion point to be set.

void gtk_entry_set_position( GtkEntry *entry,
                             gint      position );

The contents of the Entry can be retrieved by using a call to the following function. This is useful in the callback functions described below.

gchar *gtk_entry_get_text( GtkEntry *entry );

If we don't want the contents of the Entry to be changed by someone typing into it, we can change it's editable state.

void gtk_entry_set_editable( GtkEntry *entry,
                             gboolean  editable );

This function allows us to toggle the edittable state of the Entry widget by passing in a TRUE or FALSE value for the editable argument.

If we are using the Entry where we don't want the text entered to be visible, for example when a password is being entered, we can use the following function, which also takes a boolean flag.

void gtk_entry_set_visibility( GtkEntry *entry,
                               gboolean  visible );

A region of the text may be set as selected by using the following function. This would most often be used after setting some default text in an Entry, making it easy for the user to remove it.

void gtk_entry_select_region( GtkEntry *entry,
                              gint      start,
                              gint      end );

If we want to catch when the user has entered text, we can connect to the activate or changed signal. Activate is raised when the user hits the enter key within the Entry widget. Changed is raised when the text changes at all, e.g. for every character entered or removed.

The following code is an example of using an Entry widget.

/* example-start entry entry.c */

#include <gtk/gtk.h>

void enter_callback(GtkWidget *widget, GtkWidget *entry)
{
  gchar *entry_text;
  entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
  printf("Entry contents: %s\n", entry_text);
}

void entry_toggle_editable (GtkWidget *checkbutton,
                                   GtkWidget *entry)
{
  gtk_entry_set_editable(GTK_ENTRY(entry),
                         GTK_TOGGLE_BUTTON(checkbutton)->active);
}

void entry_toggle_visibility (GtkWidget *checkbutton,
                                   GtkWidget *entry)
{
  gtk_entry_set_visibility(GTK_ENTRY(entry),
                         GTK_TOGGLE_BUTTON(checkbutton)->active);
}

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

    GtkWidget *window;
    GtkWidget *vbox, *hbox;
    GtkWidget *entry;
    GtkWidget *button;
    GtkWidget *check;

    gtk_init (&argc, &argv);

    /* create a new window */
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_usize( GTK_WIDGET (window), 200, 100);
    gtk_window_set_title(GTK_WINDOW (window), "GTK Entry");
    gtk_signal_connect(GTK_OBJECT (window), "delete_event",
                       (GtkSignalFunc) gtk_exit, NULL);

    vbox = gtk_vbox_new (FALSE, 0);
    gtk_container_add (GTK_CONTAINER (window), vbox);
    gtk_widget_show (vbox);

    entry = gtk_entry_new_with_max_length (50);
    gtk_signal_connect(GTK_OBJECT(entry), "activate",
                       GTK_SIGNAL_FUNC(enter_callback),
                       entry);
    gtk_entry_set_text (GTK_ENTRY (entry), "hello");
    gtk_entry_append_text (GTK_ENTRY (entry), " world");
    gtk_entry_select_region (GTK_ENTRY (entry),
                             0, GTK_ENTRY(entry)->text_length);
    gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 0);
    gtk_widget_show (entry);

    hbox = gtk_hbox_new (FALSE, 0);
    gtk_container_add (GTK_CONTAINER (vbox), hbox);
    gtk_widget_show (hbox);
                                  
    check = gtk_check_button_new_with_label("Editable");
    gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
    gtk_signal_connect (GTK_OBJECT(check), "toggled",
                        GTK_SIGNAL_FUNC(entry_toggle_editable), entry);
    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
    gtk_widget_show (check);
    
    check = gtk_check_button_new_with_label("Visible");
    gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
    gtk_signal_connect (GTK_OBJECT(check), "toggled",
                        GTK_SIGNAL_FUNC(entry_toggle_visibility), entry);
    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
    gtk_widget_show (check);
                                   
    button = gtk_button_new_with_label ("Close");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               GTK_SIGNAL_FUNC(gtk_exit),
                               GTK_OBJECT (window));
    gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
    GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
    gtk_widget_grab_default (button);
    gtk_widget_show (button);
    
    gtk_widget_show(window);

    gtk_main();
    return(0);
}
/* example-end */

8.9 Color Selection

The color selection widget is, not surprisingly, a widget for interactive selection of colors. This composite widget lets the user select a color by manipulating RGB (Red, Green, Blue) and HSV (Hue, Saturation, Value) triples. This is done either by adjusting single values with sliders or entries, or by picking the desired color from a hue-saturation wheel/value bar. Optionally, the opacity of the color can also be set.

The color selection widget currently emits only one signal, "color_changed", which is emitted whenever the current color in the widget changes, either when the user changes it or if it's set explicitly through gtk_color_selection_set_color().

Lets have a look at what the color selection widget has to offer us. The widget comes in two flavours; gtk_color_selection and gtk_color_selection_dialog:

GtkWidget *gtk_color_selection_new( void );

You'll probably not be using this constructor directly. It creates an orphan GtkColorSelection widget which you'll have to parent yourself. The GtkColorSelection widget inherits from the GtkVBox widget.

 
GtkWidget *gtk_color_selection_dialog_new( const gchar *title );

This is the most common color selection constructor. It creates a GtkColorSelectionDialog, which inherits from a GtkDialog. It consists of a GtkFrame containing a GtkColorSelection widget, a GtkHSeparator and a GtkHBox with three buttons, "Ok", "Cancel" and "Help". You can reach these buttons by accessing the "ok_button", "cancel_button" and "help_button" widgets in the GtkColorSelectionDialog structure, (i.e. GTK_COLOR_SELECTION_DIALOG(colorseldialog)->ok_button).

void gtk_color_selection_set_update_policy( GtkColorSelection *colorsel, 
                                            GtkUpdateType      policy );

This function sets the update policy. The default policy is GTK_UPDATE_CONTINOUS which means that the current color is updated continously when the user drags the sliders or presses the mouse and drags in the hue-saturation wheel or value bar. If you experience performance problems, you may want to set the policy to GTK_UPDATE_DISCONTINOUS or GTK_UPDATE_DELAYED.

void gtk_color_selection_set_opacity( GtkColorSelection *colorsel,
                                      gint               use_opacity );

The color selection widget supports adjusting the opacity of a color (also known as the alpha channel). This is disabled by default. Calling this function with use_opacity set to TRUE enables opacity. Likewise, use_opacity set to FALSE will disable opacity.

void gtk_color_selection_set_color( GtkColorSelection *colorsel,
                                    gdouble           *color );

You can set the current color explicitly by calling this function with a pointer to an array of colors (gdouble). The length of the array depends on whether opacity is enabled or not. Position 0 contains the red component, 1 is green, 2 is blue and opacity is at position 3 (only if opacity is enabled, see gtk_color_selection_set_opacity()). All values are between 0.0 and 1.0.

void gtk_color_selection_get_color( GtkColorSelection *colorsel,
                                    gdouble           *color );

When you need to query the current color, typically when you've received a "color_changed" signal, you use this function. Color is a pointer to the array of colors to fill in. See the gtk_color_selection_set_color() function for the description of this array.

Here's a simple example demonstrating the use of the GtkColorSelectionDialog. The program displays a window containing a drawing area. Clicking on it opens a color selection dialog, and changing the color in the color selection dialog changes the background color.

/* example-start colorsel colorsel.c */

#include <glib.h>
#include <gdk/gdk.h>
#include <gtk/gtk.h>

GtkWidget *colorseldlg = NULL;
GtkWidget *drawingarea = NULL;

/* Color changed handler */

void color_changed_cb (GtkWidget *widget, GtkColorSelection *colorsel)
{
  gdouble color[3];
  GdkColor gdk_color;
  GdkColormap *colormap;

  /* Get drawingarea colormap */

  colormap = gdk_window_get_colormap (drawingarea->window);

  /* Get current color */

  gtk_color_selection_get_color (colorsel,color);

  /* Fit to a unsigned 16 bit integer (0..65535) and insert into the GdkColor structure */

  gdk_color.red = (guint16)(color[0]*65535.0);
  gdk_color.green = (guint16)(color[1]*65535.0);
  gdk_color.blue = (guint16)(color[2]*65535.0);

  /* Allocate color */

  gdk_color_alloc (colormap, &gdk_color);

  /* Set window background color */

  gdk_window_set_background (drawingarea->window, &gdk_color);

  /* Clear window */

  gdk_window_clear (drawingarea->window);
}

/* Drawingarea event handler */

gint area_event (GtkWidget *widget, GdkEvent *event, gpointer client_data)
{
  gint handled = FALSE;
  GtkWidget *colorsel;

  /* Check if we've received a button pressed event */

  if (event->type == GDK_BUTTON_PRESS && colorseldlg == NULL)
    {
      /* Yes, we have an event and there's no colorseldlg yet! */

      handled = TRUE;

      /* Create color selection dialog */

      colorseldlg = gtk_color_selection_dialog_new("Select background color");

      /* Get the GtkColorSelection widget */

      colorsel = GTK_COLOR_SELECTION_DIALOG(colorseldlg)->colorsel;

      /* Connect to the "color_changed" signal, set the client-data to the colorsel widget */

      gtk_signal_connect(GTK_OBJECT(colorsel), "color_changed",
        (GtkSignalFunc)color_changed_cb, (gpointer)colorsel);

      /* Show the dialog */

      gtk_widget_show(colorseldlg);
    }

  return handled;
}

/* Close down and exit handler */

void destroy_window (GtkWidget *widget, gpointer client_data)
{
  gtk_main_quit ();
}

/* Main */

gint main (gint argc, gchar *argv[])
{
  GtkWidget *window;

  /* Initialize the toolkit, remove gtk-related commandline stuff */

  gtk_init (&argc,&argv);

  /* Create toplevel window, set title and policies */

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW(window), "Color selection test");
  gtk_window_set_policy (GTK_WINDOW(window), TRUE, TRUE, TRUE);

  /* Attach to the "delete" and "destroy" events so we can exit */

  gtk_signal_connect (GTK_OBJECT(window), "delete_event",
    (GtkSignalFunc)destroy_window, (gpointer)window);

  gtk_signal_connect (GTK_OBJECT(window), "destroy",
    (GtkSignalFunc)destroy_window, (gpointer)window);
  
  /* Create drawingarea, set size and catch button events */

  drawingarea = gtk_drawing_area_new ();

  gtk_drawing_area_size (GTK_DRAWING_AREA(drawingarea), 200, 200);

  gtk_widget_set_events (drawingarea, GDK_BUTTON_PRESS_MASK);

  gtk_signal_connect (GTK_OBJECT(drawingarea), "event", 
    (GtkSignalFunc)area_event, (gpointer)drawingarea);
  
  /* Add drawingarea to window, then show them both */

  gtk_container_add (GTK_CONTAINER(window), drawingarea);

  gtk_widget_show (drawingarea);
  gtk_widget_show (window);
  
  /* Enter the gtk main loop (this never returns) */

  gtk_main ();

  /* Satisfy grumpy compilers */

  return 0;
}
/* example-end */

8.10 File Selections

The file selection widget is a quick and simple way to display a File dialog box. It comes complete with Ok, Cancel, and Help buttons, a great way to cut down on programming time.

To create a new file selection box use:

GtkWidget *gtk_file_selection_new( gchar *title );

To set the filename, for example to bring up a specific directory, or give a default filename, use this function:

void gtk_file_selection_set_filename( GtkFileSelection *filesel,
                                      gchar            *filename );

To grab the text that the user has entered or clicked on, use this function:

gchar *gtk_file_selection_get_filename( GtkFileSelection *filesel );

There are also pointers to the widgets contained within the file selection widget. These are:

Most likely you will want to use the ok_button, cancel_button, and help_button pointers in signaling their use.

Included here is an example stolen from testgtk.c, modified to run on it's own. As you will see, there is nothing much to creating a file selection widget. While in this example the Help button appears on the screen, it does nothing as there is not a signal attached to it.

/* example-start filesel filesel.c */

#include <gtk/gtk.h>

/* Get the selected filename and print it to the console */
void file_ok_sel (GtkWidget *w, GtkFileSelection *fs)
{
    g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
}

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

int main (int argc, char *argv[])
{
    GtkWidget *filew;
    
    gtk_init (&argc, &argv);
    
    /* Create a new file selection widget */
    filew = gtk_file_selection_new ("File selection");
    
    gtk_signal_connect (GTK_OBJECT (filew), "destroy",
                        (GtkSignalFunc) destroy, &filew);
    /* Connect the ok_button to file_ok_sel function */
    gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
                        "clicked", (GtkSignalFunc) file_ok_sel, filew );
    
    /* Connect the cancel_button to destroy the widget */
    gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
                               "clicked", (GtkSignalFunc) gtk_widget_destroy,
                               GTK_OBJECT (filew));
    
    /* Lets set the filename, as if this were a save dialog, and we are giving
     a default filename */
    gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew), 
                                     "penguin.png");
    
    gtk_widget_show(filew);
    gtk_main ();
    return 0;
}
/* example-end */


Next Previous Contents