Sunteți pe pagina 1din 98

GTK+ tutorial

From: http://zetcode.com/gui/gtk2/
This is a GTK+ programming tutorial. In this tutorial, we will learn the basics of GUI programming
in GTK+ and C language. The GTK+ tutorial is suitable for beginners and intermediate
programmers. This tutorial covers GTK+ 2.

Sumário
GTK+ tutorial.......................................................................................................................................1
GTK+...............................................................................................................................................2
Introduction to GTK+...........................................................................................................................2
GTK+...............................................................................................................................................2
Compiling GTK+ applications.........................................................................................................3
Version.............................................................................................................................................4
Sources.............................................................................................................................................4
First programs in GTK+.......................................................................................................................4
Simple example................................................................................................................................4
Centering the window......................................................................................................................6
The application icon.........................................................................................................................7
Tooltip..............................................................................................................................................8
Mnemonic......................................................................................................................................10
Menus and toolbars in GTK+.............................................................................................................11
Simple menu example....................................................................................................................12
Submenu........................................................................................................................................14
Image menus, mnemonics & accelerators.....................................................................................16
CheckMenuItem.............................................................................................................................18
Popup menu...................................................................................................................................20
A toolbar........................................................................................................................................22
Undo redo......................................................................................................................................24
GTK+ layout management.................................................................................................................26
GtkFixed........................................................................................................................................26
GtkAlignment................................................................................................................................28
GtkVBox........................................................................................................................................29
GtkTable.........................................................................................................................................31
Corner buttons................................................................................................................................32
Windows........................................................................................................................................34
GTK+ events and signals....................................................................................................................37
Button click....................................................................................................................................38
Moving a window..........................................................................................................................39
The enter signal..............................................................................................................................40
Disconnecting a callback...............................................................................................................42
Drag and drop example..................................................................................................................43
A timer example.............................................................................................................................45
GTK+ dialogs.....................................................................................................................................47
Message dialogs.............................................................................................................................47
GtkAboutDialog.............................................................................................................................50
GtkFontSelectionDialog................................................................................................................52
GtkColorSelectionDialog...............................................................................................................54
GTK+ Widgets....................................................................................................................................56
GtkButton.......................................................................................................................................56
GtkCheckButton............................................................................................................................57
GtkFrame.......................................................................................................................................59
GtkHScale......................................................................................................................................60
GtkLabel........................................................................................................................................62
Label with markup.........................................................................................................................63
GTK+ Widgets II................................................................................................................................64
GktComboBoxText........................................................................................................................64
GtkHSeparator...............................................................................................................................66
GtkEntry.........................................................................................................................................68
GtkImage.......................................................................................................................................69
GtkStatusbar...................................................................................................................................70
GtkIconView..................................................................................................................................72
GtkTreeView widget...........................................................................................................................75
List view........................................................................................................................................75
Dynamic List view.........................................................................................................................78
Tree view.......................................................................................................................................82
GtkTextView widget...........................................................................................................................85
Simple example..............................................................................................................................85
Lines and columns.........................................................................................................................87
Search & highlight.........................................................................................................................89
Custom GTK+ widget........................................................................................................................92
CPU widget....................................................................................................................................92

GTK+
GTK+ is a library for creating graphical user interfaces. The library is created in C programming
language. The GTK+ is also called the GIMP Toolkit. Originally, the library was created while
developing the GIMP image manipulation program.

Introduction to GTK+
This is an introductory GTK+ programming tutorial. The tutorial is written in the C programming
language. It has been created and tested on Linux. The GTK+ programming tutorial is suited for
novice and intermediate programmers. This tutorials covers GTK+ 2.

GTK+
GTK+ is a library for creating graphical user interfaces. The library is created in C programming
language. The GTK+ library is also called the GIMP toolkit. Originally, the library was created
while developing the GIMP image manipulation program. Since then, the GTK+ became one of the
most popular toolkits under Linux and BSD Unix. Today, most of the GUI software in the open
source world is created in Qt or in GTK+. The GTK+ is an object-oriented application
programming interface. The object-oriented system is created with the Glib Object system, which is
a base for the GTK+ library. GObject also enables to create language bindings for various other
programming languages. Language bindings exist for C++, Python, Perl, Java, C#, and other
programming languages.
The GTK+ itself depends on the following libraries:
• Glib
• Pango
• ATK
• GDK
• GdkPixbuf
• Cairo
The Glib is a general purpose utility library. It provides various data types, string utilities, enables
error reporting, message logging, working with threads, and other useful programming features.
Pango is a library which enables internationalisation. ATK is the accessibility toolkit; it provides
tools which help physically challenged people work with computers. GDK is a wrapper around the
low-level drawing and windowing functions provided by the underlying graphics system. On Linux,
GDK lies between the X Server and the GTK+ library. It handles basic rendering such as drawing
primitives, raster graphics, cursors, fonts, as well as window events, and drag-and-drop
functionality. The GdkPixbuf library is a toolkit for image loading and pixel buffer manipulation.
Cairo is a library for creating 2D vector graphics. It has been included in GTK+ since version 2.8.
Gnome and XFce desktop environments have been created using the GTK+ library. SWT and
wxWidgets are well known programming frameworks that use GTK+. Prominent software
applications that use GTK+ include Firefox or Inkscape.

Compiling GTK+ applications


To compile GTK+ applications, we have a handy tool called pkg-config. The pgk-config
returns metadata about installed libraries. If we want to use a specific library, it will provide us
necessary dependent libraries and include files that we need. The pkg-config program retrieves
information about packages from special metadata files.
$ gcc -o simple simple.c `pkg-config --libs --cflags gtk+-2.0`

This line compiles a basic program. The source code consists of one file—simple.c.
$ pkg-config --cflags gtk+-2.0 | xargs -n3
-pthread -I/usr/include/gtk-2.0 -I/usr/lib/x86_64-linux-gnu/gtk-2.0/include
-I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/gdk-pixbuf-2.0
-I/usr/include/pango-1.0 -I/usr/include/gio-unix-2.0/ -I/usr/include/freetype2
-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
-I/usr/include/pixman-1
-I/usr/include/libpng12 -I/usr/include/harfbuzz

The --cflags parameter prints pre-processor and compile flags required to compile GTK+
programs, including flags for all their dependencies.
$ pkg-config --libs gtk+-2.0 | xargs -n5
-lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgio-2.0 -lpangoft2-1.0
-lpangocairo-1.0 -lgdk_pixbuf-2.0 -lcairo -lpango-1.0 -lfontconfig
-lgobject-2.0 -lglib-2.0 -lfreetype

The --libs parameter lists the necessary libraries.


Version
The following program prints the version of the GTK+ and the Glib libraries.
version.c
#include <gtk/gtk.h>

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

gtk_init(&argc, &argv);

g_printf("GTK+ version: %d.%d.%d\n", gtk_major_version,


gtk_minor_version, gtk_micro_version);
g_printf("Glib version: %d.%d.%d\n", glib_major_version,
glib_minor_version, glib_micro_version);

return 0;
}

The program uses buit-in constants.


$ ./version
GTK+ version: 2.24.23
Glib version: 2.40.2

This is the output of the version program.

Sources
• gtk.org
• gtkforums.com
• Gtk+ 2 reference
This was an introduction to the GTK+ library.

First programs in GTK+


In this part of the GTK+ programming tutorial, we create our first programs in GTK+. We center a
window on the screen, show an icon in the titlebar, display a small tooltip, and create a mnemonic
for a button widget.

Simple example
Our first example shows a basic window.
simple.c
#include <gtk/gtk.h>

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

GtkWidget *window;

gtk_init(&argc, &argv);

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

g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_main();

return 0;
}

This example shows a basic window on the screen.


GtkWidget *window;

GtkWidget is the base class that all widgets in GTK+ derive from. It manages the widget
lifecycle, states, and style.
gtk_init(&argc, &argv);

The gtk_init() function initializes GTK+ and parses some standard command line options.
This function must be called before using any other GTK+ functions.
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

The gtk_window_new() function creates a new GtkWindow, which is a toplevel window that
can contain other widgets. The window type is GTK_WINDOW_TOPLEVEL; toplevel windows have
a titlebar and a border. They are managed by the window manager.
gtk_widget_show(window);

The get_widget_show() flags a widget to be displayed. Any widget that is not shown will not
appear on the screen.
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);

The g_signal_connect() function connects a callback function to a signal for a particular


object. The window does not react to the destroy signal by default. We must explicitly terminate
the application by connecting the destroy signal to the built-in gtk_main_quit() function,
which terminates the application.
gtk_main();

This code enters the GTK+ main loop. From this point, the application sits and waits for events to
happen.
$ gcc -o simple simple.c `pkg-config --libs --cflags gtk+-2.0`

This is how we compile the example.


Figure: Simple

Centering the window


If we do not position the window ourselves, the window manager will position it for us. In the next
example, we will center the window.
center.c
#include <gtk/gtk.h>

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

GtkWidget *window;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Center");
gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_widget_show(window);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_main();

return 0;
}

In the example, we center the window, set a title, and size the window.
gtk_window_set_title(GTK_WINDOW(window), "Center");

The gtk_window_set_title() function sets a window title. If we do not set a title ourselves,
the GTK+ will use a name of a source file as a title.
gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);

This gtk_window_set_default_size() sets the size of the window to 230x150. Note that
we are talking about the client area, excluding the decorations provided by the window manager.
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
Passing the GTK_WIN_POS_CENTER constant to the gtk_window_set_position()
function centers the program on the screen.

The application icon


In the next example, we show the application icon. Most window managers display the icon in the
left corner of the titlebar and also on the taskbar.
icon.c
#include <gtk/gtk.h>

GdkPixbuf *create_pixbuf(const gchar * filename) {

GdkPixbuf *pixbuf;
GError *error = NULL;
pixbuf = gdk_pixbuf_new_from_file(filename, &error);

if (!pixbuf) {

fprintf(stderr, "%s\n", error->message);


g_error_free(error);
}

return pixbuf;
}

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

GtkWidget *window;
GdkPixbuf *icon;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Icon");
gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

icon = create_pixbuf("web.png");
gtk_window_set_icon(GTK_WINDOW(window), icon);

gtk_widget_show(window);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

g_object_unref(icon);

gtk_main();

return 0;
}

The code example shows an application icon.


pixbuf = gdk_pixbuf_new_from_file(filename, &error);

The gdk_pixbuf_new_from_file() function creates a new pixbuf by loading an image from


a file. The file format is detected automatically. If NULL is returned, then an error will be set.
if (!pixbuf) {

fprintf(stderr, "%s\n", error->message);


g_error_free(error);
}

An error message is printed if the icon could not be loaded.


icon = create_pixbuf("web.png");
gtk_window_set_icon(GTK_WINDOW(window), icon);

The gtk_window_set_icon() displays the icon for the window. The create_pixbuf()
creates a GdkPixbuf from a PNG file.
g_object_unref(icon);

The g_object_unref() decreases the reference count of the pixbuf object. When its reference
count drops to 0, the object is finalized (i.e. its memory is freed).

Figure: Icon

Tooltip
A tooltip is a small rectangular window, which gives a brief information about an object. It is
usually a GUI component; it is part of the help system of the application.
tooltip.c
#include <gtk/gtk.h>

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

GtkWidget *window;
GtkWidget *button;
GtkWidget *halign;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Tooltip");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);

button = gtk_button_new_with_label("Button");
gtk_widget_set_tooltip_text(button, "Button widget");

halign = gtk_alignment_new(0, 0, 0, 0);


gtk_container_add(GTK_CONTAINER(halign), button);
gtk_container_add(GTK_CONTAINER(window), halign);
gtk_widget_show_all(window);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_main();

return 0;
}

The example shows a basic tooltip on a button widget.


gtk_container_set_border_width(GTK_CONTAINER(window), 15);

The gtk_container_set_border_width() sets some border space around the edges of the
window.
gtk_widget_set_tooltip_text(button, "Button widget");

The gtk_widget_set_tooltip_text() sets a basic tooltip for the given widget.


halign = gtk_alignment_new(0, 0, 0, 0);
gtk_container_add(GTK_CONTAINER(halign), button);

The GtkAlignment is a basic container which can be used to align its child to the sides of the
window. In our case, the button is placed to the upper-left corner of the window. The first
parameters of the function are the xalign and yalign. A value of 0 for xalign indicates left alignment;
a value of 0 for yalign indicates top alignment. The third and fourth parameters are scaling values.
Passing 0 to both parameters indicates that the widget does not expand in both directions.
gtk_container_add(GTK_CONTAINER(window), halign);

The GtkAlignment is set to be the main container of the window.


gtk_widget_show_all(window);

When we are dealing with multiple widgets, it is easier to call gtk_widget_show_all() on


the container than individually showing all widgets. In our case, both the window and the button are
shown in one shot.

Figure: Tooltip
Mnemonic
Mnemonics are shortcut keys that activate a widget that supports mnemonics. They can be used with
labels, buttons, or menu items. The mnemonic is created by adding the _ character to the widget's
label. It causes the next character to be the mnemonic. The character is combined with the
mouseless modifier, usually Alt. The chosen character is underlined, but it may be emphasized in a
platform specific manner. On some platforms, the character is only underlined after pressing the
mouseless modifier.
mnemonic.c
#include <gtk/gtk.h>

void print_msg(GtkWidget *widget, gpointer window) {

g_printf("Button clicked\n");
}

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

GtkWidget *window;
GtkWidget *button;
GtkWidget *halign;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Mnemonic");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);

button = gtk_button_new_with_mnemonic("_Button");

g_signal_connect(button, "clicked",
G_CALLBACK(print_msg), NULL);

halign = gtk_alignment_new(0, 0, 0, 0);


gtk_container_add(GTK_CONTAINER(halign), button);
gtk_container_add(GTK_CONTAINER(window), halign);

gtk_widget_show_all(window);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_main();

return 0;
}

We set a mnemonic for a button widget. It can be activated with the Alt+B keyboard shortcut.
button = gtk_button_new_with_mnemonic("_Button");

The gtk_button_new_with_mnemonic() function creates a new GtkButton containing a


label. If characters in label are preceded by an underscore, they are underlined.
g_signal_connect(button, "clicked",
G_CALLBACK(print_msg), NULL);
When we fire the button, a message is printed to the console. With the g_signal_connect()
function, we connect a clicked signal to the print_msg function.

At this moment, there are three ways to activate the button: a left mouse button click, the Alt+B
shortcut, and the Space key (provided the button has the focus).

Figure: Mnemonic
In this chapter we have created some simple GTK+ programs.

Menus and toolbars in GTK+


In this part of the GTK+ programming tutorial, we work with menus and toolbars. A menubar is a
common part of a GUI application. It is a group of commands located in various menus.
GtkMenuBar is a widget that creates a menubar. It contains one to many GtkMenuItems. A
menu item is an object which a user can select. GtkMenu implements a drop down menu
consisting of a list of GtkMenuItem objects, which can be navigated and activated by the user to
perform application functions. A GtkMenu is attached to the menu items of the menubar or menu
items of another menu.
Figure: Menus
The image shows the structure of a menubar and its menus.

Simple menu example


In our first example, we create a menubar with one File menu.
simplemenu.c
#include <gtk/gtk.h>

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

GtkWidget *window;
GtkWidget *vbox;

GtkWidget *menubar;
GtkWidget *fileMenu;
GtkWidget *fileMi;
GtkWidget *quitMi;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "Simple menu");

vbox = gtk_vbox_new(FALSE, 0);


gtk_container_add(GTK_CONTAINER(window), vbox);

menubar = gtk_menu_bar_new();
fileMenu = gtk_menu_new();

fileMi = gtk_menu_item_new_with_label("File");
quitMi = gtk_menu_item_new_with_label("Quit");

gtk_menu_item_set_submenu(GTK_MENU_ITEM(fileMi), fileMenu);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), quitMi);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fileMi);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

g_signal_connect(G_OBJECT(quitMi), "activate",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

The menu in the examle has one menu item. By selecting the item, the application quits.
menubar = gtk_menu_bar_new();

The gtk_menu_bar_new() creates a new GtkMenuBar.


filemenu = gtk_menu_new();

The gtk_menu_new() function creates a new GtkMenu.


gtk_menu_item_set_submenu(GTK_MENU_ITEM(fileMi), fileMenu);

The fileMenu is set to the File menu item with the gtk_menu_item_set_submenu()
function. Menus are containers which hold menu items. They are themselves plugged to a particular
menu item.
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), quitMi);

The quitMi is added to the File menu with the gtk_menu_shell_append() function.
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fileMi);

The File menu item is added to the menubar with the gtk_menu_shell_append() function.
Both GtkMenu and GtkMenuBar are derived from the GtkMenuShell.
g_signal_connect(G_OBJECT(quitMi), "activate",
G_CALLBACK(gtk_main_quit), NULL);

By selecting the quit menu item, we terminate the application.


Figure: Simple menu

Submenu
The next example demonstrates how to create a submenu. A submenu is a menu inside another
menu.
submenu.c
#include <gtk/gtk.h>

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

GtkWidget *window;
GtkWidget *vbox;

GtkWidget *menubar;
GtkWidget *fileMenu;
GtkWidget *imprMenu;
GtkWidget *sep;
GtkWidget *fileMi;
GtkWidget *imprMi;
GtkWidget *feedMi;
GtkWidget *bookMi;
GtkWidget *mailMi;
GtkWidget *quitMi;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "Submenu");

vbox = gtk_vbox_new(FALSE, 0);


gtk_container_add(GTK_CONTAINER(window), vbox);

menubar = gtk_menu_bar_new();

fileMenu = gtk_menu_new();
fileMi = gtk_menu_item_new_with_label("File");

imprMenu = gtk_menu_new();
imprMi = gtk_menu_item_new_with_label("Import");
feedMi = gtk_menu_item_new_with_label("Import news feed...");
bookMi = gtk_menu_item_new_with_label("Import bookmarks...");
mailMi = gtk_menu_item_new_with_label("Import mail...");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(imprMi), imprMenu);
gtk_menu_shell_append(GTK_MENU_SHELL(imprMenu), feedMi);
gtk_menu_shell_append(GTK_MENU_SHELL(imprMenu), bookMi);
gtk_menu_shell_append(GTK_MENU_SHELL(imprMenu), mailMi);
sep = gtk_separator_menu_item_new();
quitMi = gtk_menu_item_new_with_label("Quit");

gtk_menu_item_set_submenu(GTK_MENU_ITEM(fileMi), fileMenu);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), imprMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), sep);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), quitMi);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fileMi);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

g_signal_connect(G_OBJECT(quitMi), "activate",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

The example creates a menu inside another menu. The submenu has three menu items. We also add
a horizontal separator.
imprMenu = gtk_menu_new();
imprMi = gtk_menu_item_new_with_label("Import");
feedMi = gtk_menu_item_new_with_label("Import news feed...");
bookMi = gtk_menu_item_new_with_label("Import bookmarks...");
mailMi = gtk_menu_item_new_with_label("Import mail...");

This is a submenu with its menu items.


gtk_menu_item_set_submenu(GTK_MENU_ITEM(imprMi), imprMenu);

The imprMenu submenu is added to its own menu item.


gtk_menu_shell_append(GTK_MENU_SHELL(imprMenu), feedMi);
gtk_menu_shell_append(GTK_MENU_SHELL(imprMenu), bookMi);
gtk_menu_shell_append(GTK_MENU_SHELL(imprMenu), mailMi);

The three menu items are added to the submenu with the gtk_menu_shell_append()
function.
sep = gtk_separator_menu_item_new();

A horizontal menu separator is created with the gtk_separator_menu_item_new()


function.
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), imprMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), sep);

The imprMi and the separator are added to the File menu with the
gtk_menu_shell_append() function.
Figure: Submenu

Image menus, mnemonics & accelerators


GtkImageMenuItem is a menu item which has an icon next to the text label. Since the user can
disable displaying of menu icons, we still need to fill in the text label. Accelerators are keyboard
shortcuts for activating a menu item. Mnemonics are keyboard shortcuts for GUI elements. They are
represented as underlined characters. Note that in some environments, we first need to press the
mouseless modifier (usually Alt) to show the underlined characters.

We might also have our environment configured not to show menu images. To turn the menu
images on, we launch the gconf-editor and go to
/desktop/gnome/interface/menus_have_icons and check the option.

imagemenu.c
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

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

GtkWidget *window;
GtkWidget *vbox;

GtkWidget *menubar;
GtkWidget *fileMenu;
GtkWidget *fileMi;
GtkWidget *newMi;
GtkWidget *openMi;
GtkWidget *quitMi;

GtkWidget *sep;

GtkAccelGroup *accel_group = NULL;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "Images");

vbox = gtk_vbox_new(FALSE, 0);


gtk_container_add(GTK_CONTAINER(window), vbox);

menubar = gtk_menu_bar_new();
fileMenu = gtk_menu_new();

accel_group = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);

fileMi = gtk_menu_item_new_with_mnemonic("_File");
newMi = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
openMi = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);
sep = gtk_separator_menu_item_new();
quitMi = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);

gtk_widget_add_accelerator(quitMi, "activate", accel_group,


GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

gtk_menu_item_set_submenu(GTK_MENU_ITEM(fileMi), fileMenu);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), newMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), openMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), sep);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), quitMi);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fileMi);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

g_signal_connect(G_OBJECT(quitMi), "activate",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

The example has three menu items with icons. The menu items can be selected with mnemonics.
The Quit menu item has a keyboard accelerator.
accel_group = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
...
quitMi = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);

gtk_widget_add_accelerator(quitMi, "activate", accel_group,


GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

An accelerator group is a group of keyboard accelerators, typically attached to a toplevel window.


Here we create the Ctrl+Q keyboard accelerator.
fileMi = gtk_menu_item_new_with_mnemonic("_File");

The gtk_menu_item_new_with_mnemonic() creates a menu item which can have a


mnemonic. Underscores in label indicate the mnemonic for the menu item. The character is
combined with the mouseless modifier, usually Alt. In our case, we have created the Alt+F
mnemonic.
newMi = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
openMi = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);
The gtk_image_menu_item_new_from_stock() creates a GtkImageMenuItem
containing the image and text from a stock item.

Figure: Menu items with icons

CheckMenuItem
A GtkCheckMenuItem is a menu item with a check box.

checkmenuitem.c
#include <gtk/gtk.h>

void toggle_statusbar(GtkWidget *widget, gpointer statusbar) {

if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {

gtk_widget_show(statusbar);
} else {

gtk_widget_hide(statusbar);
}
}

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

GtkWidget *window;
GtkWidget *vbox;

GtkWidget *menubar;
GtkWidget *viewmenu;
GtkWidget *view;
GtkWidget *tog_stat;
GtkWidget *statusbar;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "GtkCheckMenuItem");

vbox = gtk_vbox_new(FALSE, 0);


gtk_container_add(GTK_CONTAINER(window), vbox);

menubar = gtk_menu_bar_new();
viewmenu = gtk_menu_new();
view = gtk_menu_item_new_with_label("View");
tog_stat = gtk_check_menu_item_new_with_label("View statusbar");
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(tog_stat), TRUE);

gtk_menu_item_set_submenu(GTK_MENU_ITEM(view), viewmenu);
gtk_menu_shell_append(GTK_MENU_SHELL(viewmenu), tog_stat);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), view);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);

statusbar = gtk_statusbar_new();
gtk_box_pack_end(GTK_BOX(vbox), statusbar, FALSE, TRUE, 0);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

g_signal_connect(G_OBJECT(tog_stat), "activate",
G_CALLBACK(toggle_statusbar), statusbar);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

The example contains a GtkCheckMenuItem in a View menu. If the check box is activated, the
statusbar widget is shown.
tog_stat = gtk_check_menu_item_new_with_label("View statusbar");

The gtk_check_menu_item_new_with_label() function creates a new


CheckMenuItem.
statusbar = gtk_statusbar_new();

The gtk_statusbar_new() function creates a new GtkStatusbar widget. It is used to


report messages of minor importance to the user.
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {

gtk_widget_show(statusbar);
} else {

gtk_widget_hide(statusbar);
}

If the check box in the menu item is activated, we show the statusbar widget; otherwise the
statusbar is hidden.
Figure: GtkCheckMenuItem

Popup menu
In the next example, we create a popup menu. A popup menu is also called a context menu. This
type of menu is usually shown when we right click on a GUI object.
popupmenu.c
#include <gtk/gtk.h>

int show_popup(GtkWidget *widget, GdkEvent *event) {

const gint RIGHT_CLICK = 3;

if (event->type == GDK_BUTTON_PRESS) {

GdkEventButton *bevent = (GdkEventButton *) event;

if (bevent->button == RIGHT_CLICK) {

gtk_menu_popup(GTK_MENU(widget), NULL, NULL, NULL, NULL,


bevent->button, bevent->time);
}

return TRUE;
}

return FALSE;
}

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

GtkWidget *window;
GtkWidget *ebox;
GtkWidget *pmenu;
GtkWidget *hideMi;
GtkWidget *quitMi;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "Popup menu");

ebox = gtk_event_box_new();
gtk_container_add(GTK_CONTAINER(window), ebox);
pmenu = gtk_menu_new();

hideMi = gtk_menu_item_new_with_label("Minimize");
gtk_widget_show(hideMi);
gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), hideMi);

quitMi = gtk_menu_item_new_with_label("Quit");
gtk_widget_show(quitMi);
gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), quitMi);

g_signal_connect_swapped(G_OBJECT(hideMi), "activate",
G_CALLBACK(gtk_window_iconify), GTK_WINDOW(window));

g_signal_connect(G_OBJECT(quitMi), "activate",
G_CALLBACK(gtk_main_quit), NULL);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

g_signal_connect_swapped(G_OBJECT(ebox), "button-press-event",
G_CALLBACK(show_popup), pmenu);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

In the code example, we create a popup menu with two menu items. The first minimizes the
window, and the second terminates the application.
ebox = gtk_event_box_new();
gtk_container_add(GTK_CONTAINER(window), ebox);

In order to process button press events, we create a GtkEventBox.


pmenu = gtk_menu_new();

A popup menu is a GtkMenu.


hideMi = gtk_menu_item_new_with_label("Minimize");
gtk_widget_show(hideMi);
gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), hideMi);

The first menu item is added to the popup menu.


g_signal_connect_swapped(G_OBJECT(hideMi), "activate",
G_CALLBACK(gtk_window_iconify), GTK_WINDOW(window));

Selecting the first menu item minimizes the window. We connect the activate signal of the Hide
menu item to the gtk_window_iconify() function. The term iconify is a synonym for
minimize.
g_signal_connect_swapped(G_OBJECT(ebox), "button_press_event",
G_CALLBACK(show_popup), pmenu);

When a mouse button is pressed, a button-press-event signal is emitted. We connect the


signal to the show_popup() function and pass it the popup menu.
if (event->type == GDK_BUTTON_PRESS) {

Inside the event handler, we check for the button press event type.
if (bevent->button == RIGHT_CLICK) {

gtk_menu_popup(GTK_MENU(widget), NULL, NULL, NULL, NULL,


bevent->button, bevent->time);
}

When the button triggering the signal is a right mouse button, we show the popup menu with the
gtk_menu_popup() function.

Figure: Popup menu

A toolbar
Menus group commands that we can use in application. Toolbars provide a quick access to the most
frequently used commands. GtkToolbar is a toolbar widget in GTK+. A toolbar can contain
instances of a subclass of a GtkToolItem, e.g. a GtkToolButton or a
GtkSeparatorToolItem.

toolbar.c
#include <gtk/gtk.h>

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

GtkWidget *window;
GtkWidget *vbox;

GtkWidget *toolbar;
GtkToolItem *newTb;
GtkToolItem *openTb;
GtkToolItem *saveTb;
GtkToolItem *sep;
GtkToolItem *exitTb;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "toolbar");

vbox = gtk_vbox_new(FALSE, 0);


gtk_container_add(GTK_CONTAINER(window), vbox);

toolbar = gtk_toolbar_new();
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);

newTb = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), newTb, -1);

openTb = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), openTb, -1);

saveTb = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), saveTb, -1);

sep = gtk_separator_tool_item_new();
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1);

exitTb = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exitTb, -1);

gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 5);

g_signal_connect(G_OBJECT(exitTb), "clicked",
G_CALLBACK(gtk_main_quit), NULL);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

The code example creates simple toolbar example.


toolbar = gtk_toolbar_new();

The gtk_toolbar_new() function creates a new GtkToolBar widget.


gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS)

The gtk_toolbar_set_style() function alters the view of toolbar to display either icons
only, text only, or both. Passing the GTK_TOOLBAR_ICONS constant makes the toolbar show only
icons.
newTb = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);

The gtk_tool_button_new_from_stock() function creates a new GtkToolButton


containing the image and text from a stock item.
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), new, -1);

The gtk_toolbar_insert() function inserts a GtkToolItem into the toolbar at the


specified position. If the position is negative, the item is appended to the end of the toolbar.
sep = gtk_separator_tool_item_new();
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1);
The gtk_separator_tool_item_new() function creates a new
GtkSeparatorToolItem. It is inserted into the toolbar with the gtk_toolbar_insert()
function.

Figure: Toolbar

Undo redo
The following example demonstrates how to inactivate toolbar buttons on the toolbar. It is a
common practice in GUI programming. For example the Save button; if we save all changes of our
document to the disk, the Save button is inactivated in most text editors. This way the application
indicates to the user that all changes are already saved.
undoredo.c
#include <gtk/gtk.h>

void undo_redo(GtkWidget *widget, gpointer item) {

static gint count = 2;


const gchar *name = gtk_widget_get_name(widget);

if (g_strcmp0(name, "undo") ) {
count++;
} else {
count--;
}

if (count < 0) {
gtk_widget_set_sensitive(widget, FALSE);
gtk_widget_set_sensitive(item, TRUE);
}

if (count > 5) {
gtk_widget_set_sensitive(widget, FALSE);
gtk_widget_set_sensitive(item, TRUE);
}
}

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

GtkWidget *window;
GtkWidget *vbox;

GtkWidget *toolbar;
GtkToolItem *undo;
GtkToolItem *redo;
GtkToolItem *sep;
GtkToolItem *exit;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "Undo redo");

vbox = gtk_vbox_new(FALSE, 0);


gtk_container_add(GTK_CONTAINER(window), vbox);

toolbar = gtk_toolbar_new();
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);

gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);

undo = gtk_tool_button_new_from_stock(GTK_STOCK_UNDO);
gtk_widget_set_name(GTK_WIDGET(undo), "undo");
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), undo, -1);

redo = gtk_tool_button_new_from_stock(GTK_STOCK_REDO);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), redo, -1);

sep = gtk_separator_tool_item_new();
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1);

exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exit, -1);

gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);

g_signal_connect(G_OBJECT(undo), "clicked",
G_CALLBACK(undo_redo), redo);

g_signal_connect(G_OBJECT(redo), "clicked",
G_CALLBACK(undo_redo), undo);

g_signal_connect(G_OBJECT(exit), "clicked",
G_CALLBACK(gtk_main_quit), NULL);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

Our example creates Undo and Redo buttons from the GTK+ stock resources. After several clicks
each of the buttons is inactivated. The buttons are grayed out.
if (count < 0) {
gtk_widget_set_sensitive(widget, FALSE);
gtk_widget_set_sensitive(item, TRUE);
}

if (count > 5) {
gtk_widget_set_sensitive(widget, FALSE);
gtk_widget_set_sensitive(item, TRUE);
}

The gtk_widget_set_sensitive() function is used to activate or inactivate the toolbar


buttons.

Figure: Undo redo


In this chapter we have covered about menus and toolbars in GTK+.

GTK+ layout management


In this chapter we show how to lay out our widgets in windows or dialogs.
When we design the UI of our application, we decide what widgets we will use and how we will
organise those widgets. To organise our widgets, we use specialised non-visible widgets called
layout containers. In this chapter, we will mention GtkAlignment, GtkFixed, GtkVBox, and
GtkTable.

GtkFixed
The GtkFixed container places child widgets at fixed positions and with fixed sizes. This
container performs no automatic layout management. Therefore, it does not work with translations,
font changes, or themes. In most applications, we do not use the GtkFixed container. There might
be some specialised areas where the container can be used (for instance, positioning charts or
images).
fixed.c
#include <gtk/gtk.h>

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

GtkWidget *window;
GtkWidget *fixed;

GtkWidget *btn1;
GtkWidget *btn2;
GtkWidget *btn3;
gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkFixed");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

fixed = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window), fixed);

btn1 = gtk_button_new_with_label("Button");
gtk_fixed_put(GTK_FIXED(fixed), btn1, 150, 50);
gtk_widget_set_size_request(btn1, 80, 30);

btn2 = gtk_button_new_with_label("Button");
gtk_fixed_put(GTK_FIXED(fixed), btn2, 15, 15);
gtk_widget_set_size_request(btn2, 80, 30);

btn3 = gtk_button_new_with_label("Button");
gtk_fixed_put(GTK_FIXED(fixed), btn3, 100, 100);
gtk_widget_set_size_request(btn3, 80, 30);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

In our example, we create three buttons and place them at fixed coordinates. When we resize the
window of the application, the buttons keep their size and positions.
fixed = gtk_fixed_new();

The get_fixed_new() function creates a GtkFixed container.


gtk_fixed_put(GTK_FIXED(fixed), btn1, 150, 50);

The first button is placed using the gtk_fixed_put() function at coordinates x=150 and y=50.
gtk_widget_set_size_request(btn1, 80, 30);

The gtk_widget_set_size_request() sets a minimum size for the widget. It is the


smallest size a widget can accept while still functioning well and drawing itself correctly.
Figure: GtkFixed container

GtkAlignment
GtkAlignment controls the alignment of a widget. In addition, it can manage its scaling.

bottomleft.c
#include <gtk/gtk.h>

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

GtkWidget *window;
GtkWidget *align;

GtkWidget *lbl;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkAlignment");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_container_set_border_width(GTK_CONTAINER(window), 5);

align = gtk_alignment_new(0, 1, 0, 0);


lbl = gtk_label_new("bottom-left");

gtk_container_add(GTK_CONTAINER(align), lbl);
gtk_container_add(GTK_CONTAINER(window), align);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

In the example, a label is positioned in the bottom-left corner of the window.


align = gtk_alignment_new(0, 1, 0, 0);

The gtk_alignment_new() function creates the GtkAlignment container. The parameters


take values from 0 to 1. The first parameter is the horizontal alignment, where 0 is left and 1 is
right. The second parameter is the vertical alignment, where 0 is top and 1 is bottom. The third
parameter is a horizontal scale, which is the amount that the child widget expands horizontally to
fill up unused space. A value of 0 indicates that the child widget should never expand. The last
parameter is the vertical scale.
lbl = gtk_label_new("bottom-left");

A label widget is created with the gtk_label_new() function.


gtk_container_add(GTK_CONTAINER(align), lbl);

The label is added to the GtkAlignment container.


gtk_container_add(GTK_CONTAINER(window), align);

Finally, the alignment container is placed into the window.

Figure: GtkAlignment

GtkVBox
GtkVBox is a vertical box container. It places its child widgets into a single column. GtkHBox is a
very similar container; it places its child widgets into a single row.
vbox.c
#include <gtk/gtk.h>

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

GtkWidget *window;
GtkWidget *vbox;

GtkWidget *settings;
GtkWidget *accounts;
GtkWidget *loans;
GtkWidget *cash;
GtkWidget *debts;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 230, 250);
gtk_window_set_title(GTK_WINDOW(window), "GtkVBox");
gtk_container_set_border_width(GTK_CONTAINER(window), 5);
vbox = gtk_vbox_new(TRUE, 1);
gtk_container_add(GTK_CONTAINER(window), vbox);

settings = gtk_button_new_with_label("Settings");
accounts = gtk_button_new_with_label("Accounts");
loans = gtk_button_new_with_label("Loans");
cash = gtk_button_new_with_label("Cash");
debts = gtk_button_new_with_label("Debts");

gtk_box_pack_start(GTK_BOX(vbox), settings, TRUE, TRUE, 0);


gtk_box_pack_start(GTK_BOX(vbox), accounts, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), loans, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), cash, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), debts, TRUE, TRUE, 0);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));

gtk_widget_show_all(window);

gtk_main();

return 0;
}

This example packs five buttons into one column. If we resize the window of the application, the
child widgets are resized as well.
vbox = gtk_vbox_new(TRUE, 1);

The gtk_vbox_new() function creates a GtkVBox container. We set the homogeneous


parameter to TRUE. This means that all our buttons will be of the same size. The spacing between
widgets is set to 1 pixel.
gtk_box_pack_start(GTK_BOX(vbox), settings, TRUE, TRUE, 0);

The gtk_box_pack_start() function adds a widget to the box. The first two parameters are
the box container and the child widget. The next three parameters are expand, fill, and
padding. Note that the fill parameter has no effect if the expand parameter is set to FALSE.
Similarly, the expand parameter has no effect if we have created the container with homogeneous
parameter set to TRUE. In our case, the Settings button is given extra space when the window is
enlarged and the widget fills the additional area.
Figure: GtkVBox container

GtkTable
The GtkTable widget arranges widgets in rows and columns.

table.c
#include <gtk/gtk.h>

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

GtkWidget *window;
GtkWidget *table;
GtkWidget *button;

gchar *values[16] = { "7", "8", "9", "/",


"4", "5", "6", "*",
"1", "2", "3", "-",
"0", ".", "=", "+"
};

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 250, 180);
gtk_window_set_title(GTK_WINDOW(window), "GtkTable");

gtk_container_set_border_width(GTK_CONTAINER(window), 5);

table = gtk_table_new(4, 4, TRUE);


gtk_table_set_row_spacings(GTK_TABLE(table), 2);
gtk_table_set_col_spacings(GTK_TABLE(table), 2);

int i = 0;
int j = 0;
int pos = 0;

for (i=0; i < 4; i++) {


for (j=0; j < 4; j++) {
button = gtk_button_new_with_label(values[pos]);
gtk_table_attach_defaults(GTK_TABLE(table), button, j, j+1, i, i+1);
pos++;
}
}

gtk_container_add(GTK_CONTAINER(window), table);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

In this example, we create a set of buttons that we see in calculators.


table = gtk_table_new(4, 4, TRUE);

We create a new GtkTable widget with 4 rows and 4 columns. When we pass TRUE to the third
parameter, all table cells are resized to the size of the cell containing the largest widget.
gtk_table_set_row_spacings(GTK_TABLE(table), 2);
gtk_table_set_col_spacings(GTK_TABLE(table), 2);

We set some space between rows and columns.


for (i=0; i < 4; i++) {
for (j=0; j < 4; j++) {
button = gtk_button_new_with_label(values[pos]);
gtk_table_attach_defaults(GTK_TABLE(table), button, j, j+1, i, i+1 );
pos++;
}
}

This code creates 16 buttons and places them into the container. The
gtk_table_attach_defaults() adds children to a table container with identical padding
and expansion options.

Figure: GtkTable

Corner buttons
The next example places two buttons in the bottom-right corner of the window.
cornerbuttons.c
#include <gtk/gtk.h>
int main(int argc, char *argv[]) {

GtkWidget *window;
GtkWidget *okBtn;
GtkWidget *clsBtn;

GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *halign;
GtkWidget *valign;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 350, 200);
gtk_window_set_title(GTK_WINDOW(window), "Corner buttons");
gtk_container_set_border_width(GTK_CONTAINER(window), 10);

vbox = gtk_vbox_new(FALSE, 5);

valign = gtk_alignment_new(0, 1, 0, 0);


gtk_container_add(GTK_CONTAINER(vbox), valign);
gtk_container_add(GTK_CONTAINER(window), vbox);

hbox = gtk_hbox_new(TRUE, 3);

okBtn = gtk_button_new_with_label("OK");
gtk_widget_set_size_request(okBtn, 70, 30);
gtk_container_add(GTK_CONTAINER(hbox), okBtn);
clsBtn = gtk_button_new_with_label("Close");
gtk_container_add(GTK_CONTAINER(hbox), clsBtn);

halign = gtk_alignment_new(1, 0, 0, 0);


gtk_container_add(GTK_CONTAINER(halign), hbox);

gtk_box_pack_start(GTK_BOX(vbox), halign, FALSE, FALSE, 0);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));

gtk_widget_show_all(window);

gtk_main();

return 0;
}

In the example, we use one horizontal box, one vertical box, and two alignment containers.
valign = gtk_alignment_new(0, 1, 0, 0);

This alignment container puts its child widget to the bottom.


gtk_container_add(GTK_CONTAINER(vbox), valign);

Here we place the alignment widget into the vertical box.


hbox = gtk_hbox_new(TRUE, 3);

okBtn = gtk_button_new_with_label("OK");
gtk_widget_set_size_request(okBtn, 70, 30);
gtk_container_add(GTK_CONTAINER(hbox), okBtn);
clsBtn = gtk_button_new_with_label("Close");
gtk_container_add(GTK_CONTAINER(hbox), clsBtn);

We create a horizontal box and put two buttons inside it. The
gtk_widget_set_size_request() sets the minimum size of a widget. Since we have set
the homogeneous parameter of a GtkHBox to TRUE, the other button is adjusted to the new size
as well.
halign = gtk_alignment_new(1, 0, 0, 0);
gtk_container_add(GTK_CONTAINER(halign), hbox);

gtk_box_pack_start(GTK_BOX(vbox), halign, FALSE, FALSE, 0);

This creates an alignment container that places its child widget to the right. We add the horizontal
box into the alignment container and pack the alignment container into the vertical box. The
alignment container can take only one child widget; therefore, we must also use boxes.

Figure: Corner buttons

Windows
Next we will create a more advanced example. We show a window that can be found in JDeveloper.

Figure: Windows dialog in JDeveloper


The dialog shows all opened windows, or more precisely tabs in JDeveloper application.
windows.c
#include <gtk/gtk.h>

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


GtkWidget *window;
GtkWidget *table;
GtkWidget *title;
GtkWidget *wins;

GtkWidget *halign;
GtkWidget *halign2;
GtkWidget *valign;

GtkWidget *actBtn;
GtkWidget *clsBtn;
GtkWidget *hlpBtn;
GtkWidget *okBtn;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_widget_set_size_request (window, 300, 250);

gtk_window_set_title(GTK_WINDOW(window), "Windows");

gtk_container_set_border_width(GTK_CONTAINER(window), 15);

table = gtk_table_new(6, 4, FALSE);


gtk_table_set_col_spacings(GTK_TABLE(table), 3);
gtk_table_set_row_spacing(GTK_TABLE(table), 0, 3);

title = gtk_label_new("Windows");
halign = gtk_alignment_new(0, 0, 0, 0);
gtk_container_add(GTK_CONTAINER(halign), title);
gtk_table_attach(GTK_TABLE(table), halign, 0, 1, 0, 1,
GTK_FILL, GTK_FILL, 0, 0);

wins = gtk_text_view_new();
gtk_text_view_set_editable(GTK_TEXT_VIEW(wins), FALSE);
gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(wins), FALSE);
gtk_table_attach(GTK_TABLE(table), wins, 0, 2, 1, 3,
GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 1, 1);

actBtn = gtk_button_new_with_label("Activate");
gtk_widget_set_size_request(actBtn, 50, 30);
gtk_table_attach(GTK_TABLE(table), actBtn, 3, 4, 1, 2,
GTK_FILL, GTK_SHRINK, 1, 1);

valign = gtk_alignment_new(0, 0, 0, 0);


clsBtn = gtk_button_new_with_label("Close");

gtk_widget_set_size_request(clsBtn, 70, 30);


gtk_container_add(GTK_CONTAINER(valign), clsBtn);
gtk_table_set_row_spacing(GTK_TABLE(table), 1, 3);
gtk_table_attach(GTK_TABLE(table), valign, 3, 4, 2, 3,
GTK_FILL, GTK_FILL | GTK_EXPAND, 1, 1);

halign2 = gtk_alignment_new(0, 1, 0, 0);


hlpBtn = gtk_button_new_with_label("Help");
gtk_container_add(GTK_CONTAINER(halign2), hlpBtn);
gtk_widget_set_size_request(hlpBtn, 70, 30);
gtk_table_set_row_spacing(GTK_TABLE(table), 3, 5);
gtk_table_attach(GTK_TABLE(table), halign2, 0, 1, 4, 5,
GTK_FILL, GTK_FILL, 0, 0);

okBtn = gtk_button_new_with_label("OK");
gtk_widget_set_size_request(okBtn, 70, 30);
gtk_table_attach(GTK_TABLE(table), okBtn, 3, 4, 4, 5,
GTK_FILL, GTK_FILL, 0, 0);

gtk_container_add(GTK_CONTAINER(window), table);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));

gtk_widget_show_all(window);
gtk_main();

return 0;
}

The example uses a table container and three alignment containers.


table = gtk_table_new(6, 4, FALSE);

A GtkTable container is created. It has six rows and four columns.


gtk_table_set_col_spacings(GTK_TABLE(table), 3);

The gtk_table_set_col_spacings() sets the space between every column in the table to
3.
gtk_table_set_row_spacing(GTK_TABLE(table), 0, 3);

The gtk_table_row_spacing() sets space between the first and the second row.
title = gtk_label_new("Windows");
halign = gtk_alignment_new(0, 0, 0, 0);
gtk_container_add(GTK_CONTAINER(halign), title);
gtk_table_attach(GTK_TABLE(table), halign, 0, 1, 0, 1,
GTK_FILL, GTK_FILL, 0, 0);

This code creates a left-aligned label. The label is placed in the first row and the first column of the
GtkTable container.
wins = gtk_text_view_new();
gtk_text_view_set_editable(GTK_TEXT_VIEW(wins), FALSE);
gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(wins), FALSE);
gtk_table_attach(GTK_TABLE(table), wins, 0, 2, 1, 3,
GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 1, 1);

The GtkText widget spans two rows and two columns. We make the widget non-editable using
the gtk_text_view_set_editable() method and hide its cursor using the
gtk_text_view_set_cursor_visible() method.
valign = gtk_alignment_new(0, 0, 0, 0);
clsBtn = gtk_button_new_with_label("Close");

gtk_widget_set_size_request(clsBtn, 70, 30);


gtk_container_add(GTK_CONTAINER(valign), clsBtn);
gtk_table_set_row_spacing(GTK_TABLE(table), 1, 3);
gtk_table_attach(GTK_TABLE(table), valign, 3, 4, 2, 3,
GTK_FILL, GTK_FILL | GTK_EXPAND, 1, 1);
We put the Close button next to the text view widget into the fourth column. We add the button into
the alignment widget so that we can align it to the top.
halign2 = gtk_alignment_new(0, 1, 0, 0);
hlpBtn = gtk_button_new_with_label("Help");
gtk_container_add(GTK_CONTAINER(halign2), hlpBtn);
gtk_widget_set_size_request(hlpBtn, 70, 30);
gtk_table_set_row_spacing(GTK_TABLE(table), 3, 5);
gtk_table_attach(GTK_TABLE(table), halign2, 0, 1, 4, 5,
GTK_FILL, GTK_FILL, 0, 0);

The Help button is left-aligned. It is placed below the text widget. We put some space between the
text widget and the button.
okBtn = gtk_button_new_with_label("OK");
gtk_widget_set_size_request(okBtn, 70, 30);
gtk_table_attach(GTK_TABLE(table), okBtn, 3, 4, 4, 5,
GTK_FILL, GTK_FILL, 0, 0);

The OK button goes to the second column, below the Activate and Close buttons.

Figure: Windows
This chapter was dedicated to layout management.

GTK+ events and signals


In this part of the GTK+ programming tutorial, we talk about the event system.
GTK+ is an event driven system. All GUI applications are event driven. The applications start a
main loop, which continuously checks for newly generated events. If there is no event, the
application waits and does nothing. In GTK+ an event is a message from the X server. When the
event reaches a widget, it may react to this event by emitting a signal. The GTK+ programmer can
connect a specific callback to the signal. The callback is a handler function that reacts to the signal.
Button click
When a button is fired, it sends a clicked signal. A button can be fired by a mouse pointer or with
the Space key (provided the button has focus).

buttonclick.c
#include <gtk/gtk.h>

void button_clicked(GtkWidget *widget, gpointer data) {

g_print("clicked\n");
}

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

GtkWidget *window;
GtkWidget *halign;
GtkWidget *btn;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkButton");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

halign = gtk_alignment_new(0, 0, 0, 0);


btn = gtk_button_new_with_label("Click");
gtk_widget_set_size_request(btn, 70, 30);

gtk_container_add(GTK_CONTAINER(halign), btn);
gtk_container_add(GTK_CONTAINER(window), halign);

g_signal_connect(G_OBJECT(btn), "clicked",
G_CALLBACK(button_clicked), NULL);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

In the application, we have two signals: the clicked signal and the destroy signal.
g_signal_connect(G_OBJECT(btn), "clicked",
G_CALLBACK(button_clicked), NULL);

We use the g_signal_connect() function to connect the clicked signal to the


button_clicked() callback.
void button_clicked(GtkWidget *widget, gpointer data) {

g_print("clicked\n");
}
The callback prints the "clicked" string to the console. The first parameter of the callback function
is the object which emitted the signal. In our case it is the Click button. The second parameter is
optional. We may send some data to the callback. In our case, we did not send any data; we
provided a NULL value to the fourth parameter of the g_signal_connect() function.
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

If we press on the x button located in the upper right corner of the titlebar, or we press Atl+F4, a
destroy signal is emitted. The gtk_main_quit() function is called, which terminates the
application.

Moving a window
The next example shows how we react to window move events.
moveevent.c
#include <gtk/gtk.h>

void configure_callback(GtkWindow *window,


GdkEvent *event, gpointer data) {

int x, y;
GString *buf;

x = event->configure.x;
y = event->configure.y;

buf = g_string_new(NULL);
g_string_printf(buf, "%d, %d", x, y);

gtk_window_set_title(window, buf->str);

g_string_free(buf, TRUE);
}

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

GtkWidget *window;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_widget_add_events(GTK_WIDGET(window), GDK_CONFIGURE);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));

g_signal_connect(G_OBJECT(window), "configure-event",
G_CALLBACK(configure_callback), NULL);

gtk_widget_show(window);
gtk_main();

return 0;
}
In the example, we show the current position of the upper-left corner of our window in the titlebar.
gtk_widget_add_events(GTK_WIDGET(window), GDK_CONFIGURE);

The event mask of the widget determines what kind of events will a particular widget receive. Some
event are preconfigured, other events have to be added to the event mask. The
gtk_widget_add_events() adds a GDK_CONFIGURE event type to the mask. The
GDK_CONFIGURE event type accounts for all size, position, and the stacking order of the window
changes.
g_signal_connect(G_OBJECT(window), "configure-event",
G_CALLBACK(configure_callback), NULL);

The configure-event is emitted when the size, position, or stacking of the widget's window
has changed.
void configure_callback(GtkWindow *window,
GdkEvent *event, gpointer data) {

int x, y;
GString *buf;

x = event->configure.x;
y = event->configure.y;

buf = g_string_new(NULL);
g_string_printf(buf, "%d, %d", x, y);

gtk_window_set_title(window, buf->str);

g_string_free(buf, TRUE);
}

The callback function has three parameters: the object that emitted the signal, the GdkEvent, and
the optional data. We determine the x, y coordinates, build a string, and set it to the window title.

Figure: Move event

The enter signal


The following example shows how we can react to an enter signal. The enter signal is emitted
when we enter the area of a widget with a mouse pointer.
entersignal.c
#include <gtk/gtk.h>

void enter_button(GtkWidget *widget, gpointer data) {

GdkColor col = {0, 27000, 30000, 35000};

gtk_widget_modify_bg(widget, GTK_STATE_PRELIGHT, &col);


}

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

GtkWidget *window;
GtkWidget *halign;
GtkWidget *btn;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_title(GTK_WINDOW(window), "Enter signal");

halign = gtk_alignment_new(0, 0, 0, 0);

btn = gtk_button_new_with_label("Button");
gtk_widget_set_size_request(btn, 70, 30);

gtk_container_add(GTK_CONTAINER(halign), btn);
gtk_container_add(GTK_CONTAINER(window), halign);

g_signal_connect(G_OBJECT(btn), "enter",
G_CALLBACK(enter_button), NULL);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

In the example, the background colour of the button widget is changes when we hover a mouse
pointer over it.
g_signal_connect(G_OBJECT(btn), "enter",
G_CALLBACK(enter_button), NULL);

We call the enter_button() user function when the enter signal occurs.
void enter_button(GtkWidget *widget, gpointer data) {

GdkColor col = {0, 27000, 30000, 35000};

gtk_widget_modify_bg(widget, GTK_STATE_PRELIGHT, &col);


}

Inside the callback, we change the background of the button by calling the
gtk_widget_modify_bg() function.
Disconnecting a callback
We can disconnect a callback from the signal. The next code example demonstrates such a case.
disconnect.c
#include <gtk/gtk.h>

gint handler_id;

void button_clicked(GtkWidget *widget, gpointer data) {

g_print("clicked\n");
}

void toogle_signal(GtkWidget *widget, gpointer window) {

if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
handler_id = g_signal_connect(G_OBJECT(window), "clicked",
G_CALLBACK(button_clicked), NULL);
} else {
g_signal_handler_disconnect(window, handler_id);
}
}

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

GtkWidget *window;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *btn;
GtkWidget *cb;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_title(GTK_WINDOW(window), "Disconnect");

hbox = gtk_hbox_new(FALSE, 15);

btn = gtk_button_new_with_label("Click");
gtk_widget_set_size_request(btn, 70, 30);
gtk_box_pack_start(GTK_BOX(hbox), btn, FALSE, FALSE, 0);

cb = gtk_check_button_new_with_label("Connect");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb), TRUE);
gtk_box_pack_start(GTK_BOX(hbox), cb, FALSE, FALSE, 0);

vbox = gtk_vbox_new(FALSE, 5);


gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);

handler_id = g_signal_connect(G_OBJECT(btn), "clicked",


G_CALLBACK(button_clicked), NULL);

g_signal_connect(G_OBJECT(cb), "clicked",
G_CALLBACK(toogle_signal), (gpointer) btn);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);

gtk_main();

return 0;
}

In the code example, we have a button and a check box. The check box connects or disconnects a
callback from the clicked signal of the button.
handler_id = g_signal_connect(G_OBJECT(btn), "clicked",
G_CALLBACK(button_clicked), NULL);

The g_signal_connect() returns the handler id which uniquely identifies the callback.
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
handler_id = g_signal_connect(G_OBJECT(window), "clicked",
G_CALLBACK(button_clicked), NULL);
} else {
g_signal_handler_disconnect(window, handler_id);
}

This code determines the state of the check box. Depending on the state, it connects the callback
with the g_signal_connect() function or disconnects with the
g_signal_handler_disconnect() function.

Figure: Disconnect

Drag and drop example


In the next example, we show borderless window and learn how we can drag and move such a
window.
dragdrop.c
#include <gtk/gtk.h>

gboolean on_button_press(GtkWidget* widget,


GdkEventButton *event, GdkWindowEdge edge) {

if (event->type == GDK_BUTTON_PRESS) {

if (event->button == 1) {
gtk_window_begin_move_drag(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
event->button,
event->x_root,
event->y_root,
event->time);
}
}

return TRUE;
}

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

GtkWidget *window;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
gtk_window_set_title(GTK_WINDOW(window), "Drag & drop");
gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);

g_signal_connect(G_OBJECT(window), "button-press-event",
G_CALLBACK(on_button_press), NULL);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));

gtk_widget_show(window);

gtk_main();

return 0;
}

The example demonstrates a drag and drop operation of a borderless window.


gtk_window_set_decorated(GTK_WINDOW(window), FALSE);

We remove the window decorations with the gtk_window_set_decorated() function. This


means that the window will not have borders and titlebar.
g_signal_connect(G_OBJECT(window), "button-press-event",
G_CALLBACK(on_button_press), NULL);

We connect the window to the button-press-event signal.


gboolean on_button_press(GtkWidget* widget,
GdkEventButton *event, GdkWindowEdge edge) {

if (event->type == GDK_BUTTON_PRESS) {

if (event->button == 1) {
gtk_window_begin_move_drag(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
event->button,
event->x_root,
event->y_root,
event->time);
}
}

return TRUE;
}

Inside the on_button_press() function, we perform the drag and drop operation. We check if
the left mouse button was pressed. Then we call the gtk_window_begin_move_drag()
function, which starts moving the window.

A timer example
The following example demonstrates a timer example. Timers are used when we have some
repeating tasks. It could be a clock, a count down, visual effects, or animations.
timer.c
#include <cairo.h>
#include <gtk/gtk.h>

gchar buf[256];

gboolean on_expose_event(GtkWidget *widget,


GdkEventExpose *event,
gpointer data) {

cairo_t *cr;

cr = gdk_cairo_create(widget->window);

cairo_move_to(cr, 30, 30);


cairo_set_font_size(cr, 15);
cairo_show_text(cr, buf);

cairo_destroy(cr);

return FALSE;
}

gboolean time_handler(GtkWidget *widget) {

if (widget->window == NULL) return FALSE;

GDateTime *now = g_date_time_new_now_local();


gchar *my_time = g_date_time_format(now, "%H:%M:%S");

g_sprintf(buf, "%s", my_time);

g_free(my_time);
g_date_time_unref(now);

gtk_widget_queue_draw(widget);

return TRUE;
}

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

GtkWidget *window;
GtkWidget *darea;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(window), darea);

g_signal_connect(darea, "expose-event",
G_CALLBACK(on_expose_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);

gtk_window_set_title(GTK_WINDOW(window), "Timer");
g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) window);
gtk_widget_show_all(window);
time_handler(window);

gtk_main();

return 0;
}

The example displays the current local time on the window. The Cairo 2D library is also used.
g_signal_connect(darea, "expose-event",
G_CALLBACK(on_expose_event), NULL);

We draw the time inside the on_expose_event() callback. The callback is connected to the
expose-event signal, which is is emitted when the window is going to be redrawn.
g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) window);

This function registers the timer. The time_handler() function is called repeatedly at regular
intervals; in our case in every second. The timer function is called until it returns FALSE.
time_handler(window);

This calls the timer function immediately. Otherwise, there would be one sec delay.
cairo_t *cr;

cr = gdk_cairo_create(widget->window);

cairo_move_to(cr, 30, 30);


cairo_set_font_size(cr, 15);
cairo_show_text(cr, buf);

cairo_destroy(cr);

This code draws the current time on the window. For more information about the Cairo 2D library,
see the ZetCode's Cairo graphics tutorial.
if (widget->window == NULL) return FALSE;

When the window is destroyed, it may happen that the timer function is called. This line prevents
working on an already destroyed widget.
GDateTime *now = g_date_time_new_now_local();
gchar *my_time = g_date_time_format(now, "%H:%M:%S");

g_sprintf(buf, "%s", my_time);


These lines determine the current local time. The time is stored in the global buf variable.
gtk_widget_queue_draw(widget);

The gtk_widget_queue_draw() function invalidates the window area, which then emits the
expose-event signal.

This chapter was about events in GTK+.

GTK+ dialogs
In this part of the GTK+ programming tutorial, we work with dialogs.
Dialog windows or dialogs are an indispensable part of most modern GUI applications. A dialog is
defined as a conversation between two or more persons. In a computer application a dialog is a
window which is used to "talk" to the application. A dialog is used to input data, modify data,
change the application settings etc.

Message dialogs
Message dialogs are convenient dialogs that provide messages to the user of the application. The
message consists of textual and image data.
messagedialogs.c
#include <gtk/gtk.h>

void show_info(GtkWidget *widget, gpointer window) {

GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"Download Completed");
gtk_window_set_title(GTK_WINDOW(dialog), "Information");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}

void show_error(GtkWidget *widget, gpointer window) {

GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
"Error loading file");
gtk_window_set_title(GTK_WINDOW(dialog), "Error");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}

void show_question(GtkWidget *widget, gpointer window) {

GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
"Are you sure to quit?");
gtk_window_set_title(GTK_WINDOW(dialog), "Question");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}

void show_warning(GtkWidget *widget, gpointer window) {

GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_OK,
"Unallowed operation");
gtk_window_set_title(GTK_WINDOW(dialog), "Warning");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}

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

GtkWidget *window;
GtkWidget *table;

GtkWidget *info;
GtkWidget *warn;
GtkWidget *que;
GtkWidget *err;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 220, 150);
gtk_window_set_title(GTK_WINDOW(window), "Message dialogs");

table = gtk_table_new(2, 2, TRUE);


gtk_table_set_row_spacings(GTK_TABLE(table), 2);
gtk_table_set_col_spacings(GTK_TABLE(table), 2);

info = gtk_button_new_with_label("Info");
warn = gtk_button_new_with_label("Warning");
que = gtk_button_new_with_label("Question");
err = gtk_button_new_with_label("Error");

gtk_table_attach(GTK_TABLE(table), info, 0, 1, 0, 1,
GTK_FILL, GTK_FILL, 3, 3);
gtk_table_attach(GTK_TABLE(table), warn, 1, 2, 0, 1,
GTK_FILL, GTK_FILL, 3, 3);
gtk_table_attach(GTK_TABLE(table), que, 0, 1, 1, 2,
GTK_FILL, GTK_FILL, 3, 3);
gtk_table_attach(GTK_TABLE(table), err, 1, 2, 1, 2,
GTK_FILL, GTK_FILL, 3, 3);

gtk_container_add(GTK_CONTAINER(window), table);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);

g_signal_connect(G_OBJECT(info), "clicked",
G_CALLBACK(show_info), (gpointer) window);

g_signal_connect(G_OBJECT(warn), "clicked",
G_CALLBACK(show_warning), (gpointer) window);

g_signal_connect(G_OBJECT(que), "clicked",
G_CALLBACK(show_question), (gpointer) window);

g_signal_connect(G_OBJECT(err), "clicked",
G_CALLBACK(show_error), (gpointer) window);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));

gtk_widget_show_all(window);

gtk_main();

return 0;
}

In our example, we show four kinds of message dialogs: Information, Warning, Question, and Error
message dialogs.
void show_question(GtkWidget *widget, gpointer window) {

GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
"Are you sure to quit?");
gtk_window_set_title(GTK_WINDOW(dialog), "Question");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}

In the show_question() function, we pop up the message dialog. The message dialog is created
using the gtk_message_dialog_new() call. The parameters of the function specify what kind
of message dialog we create. The GTK_MESSAGE_QUESTION constant creates a question type
dialog. The GTK_BUTTONS_YES_NO constant will add Yes and No buttons in the dialog. The last
parameter is the text that we display in the dialog. The gtk_dialog_run() function shows the
dialog and blocks the main loop until the dialog responds or is destroyed. The dialog must be
destroyed with the gtk_widget_destroy() function.
Figure: Message dialogs

GtkAboutDialog
GtkAboutDialog is a dialog whose purpose is to display information about the application. It
can display the logo, the name of the application, the version, the copyright, the website, and the
licence information. It is also possible to give credits to the authors, documenters, translators, and
artists.
aboutdialog.c
#include <gtk/gtk.h>

void show_about(GtkWidget *widget, gpointer data) {

GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file("battery.png", NULL);

GtkWidget *dialog = gtk_about_dialog_new();


gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(dialog), "Battery");
gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), "0.9");
gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog),"(c) Jan Bodnar");
gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog),
"Battery is a simple tool for battery checking.");
gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog),
"http://www.batteryhq.net");
gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(dialog), pixbuf);
g_object_unref(pixbuf), pixbuf = NULL;
gtk_dialog_run(GTK_DIALOG (dialog));
gtk_widget_destroy(dialog);
}

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

GtkWidget *window;
GtkWidget *about;
GdkPixbuf *battery;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 220, 150);
gtk_window_set_title(GTK_WINDOW(window), "Battery");

gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);

g_signal_connect(G_OBJECT(window), "button-press-event",
G_CALLBACK(show_about), (gpointer) window);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));

gtk_widget_show_all(window);

gtk_main();

return 0;
}

The code example uses a GtkAboutDialog with some of its features. A click on the client area
of the window pops up the dialog.
GtkWidget *dialog = gtk_about_dialog_new();

The GtkAboutDialog is created with the gtk_about_dialog_new() function.


gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(dialog), "Battery");
gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), "0.9");
gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog),
"(c) Jan Bodnar");

These function calls set the name, the version, and the copyright of the application.
gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog),
"Battery is a simple tool for battery checking.");
gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog),
"http://www.batteryhq.net");

These lines set descriptive comments and the website of the application.
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file("battery.png", NULL);
...
gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(dialog), pixbuf);
g_object_unref(pixbuf), pixbuf = NULL;

This code creates a logo of the application.


Figure: GtkAboutDialog

GtkFontSelectionDialog
GtkFontSelectionDialog is a dialog for selecting fonts. It is typically used in applications
that do some text editing or formatting.
fontdialog.c
#include <gtk/gtk.h>

void select_font(GtkWidget *widget, gpointer label) {

GtkResponseType result;

GtkWidget *dialog = gtk_font_selection_dialog_new("Select Font");

result = gtk_dialog_run(GTK_DIALOG(dialog));

if (result == GTK_RESPONSE_OK || result == GTK_RESPONSE_APPLY) {

PangoFontDescription *font_desc;
gchar *fontname = gtk_font_selection_dialog_get_font_name(
GTK_FONT_SELECTION_DIALOG(dialog));

font_desc = pango_font_description_from_string(fontname);

gtk_widget_modify_font(GTK_WIDGET(label), font_desc);

g_free(fontname);
}

gtk_widget_destroy(dialog);
}

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

GtkWidget *window;
GtkWidget *label;
GtkWidget *vbox;

GtkWidget *toolbar;
GtkToolItem *font;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 280, 200);
gtk_window_set_title(GTK_WINDOW(window), "Font Selection Dialog");

vbox = gtk_vbox_new(FALSE, 0);


gtk_container_add(GTK_CONTAINER(window), vbox);

toolbar = gtk_toolbar_new();
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);

gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);

font = gtk_tool_button_new_from_stock(GTK_STOCK_SELECT_FONT);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), font, -1);

gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 5);

label = gtk_label_new("ZetCode");
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, FALSE, 5);

g_signal_connect(G_OBJECT(font), "clicked",
G_CALLBACK(select_font), label);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

In the code example, we put a simple label into the center of the window. We show a font selecting
dialog by clicking on the toolbar button.
GtkWidget *dialog = gtk_font_selection_dialog_new("Select Font");
result = gtk_dialog_run(GTK_DIALOG(dialog));

We create the GtkFontSelectionDialog with the


gtk_font_selection_dialog_new() function and run it with the gtk_dialog_run()
function.
if (result == GTK_RESPONSE_OK || result == GTK_RESPONSE_APPLY) {

PangoFontDescription *font_desc;
gchar *fontname = gtk_font_selection_dialog_get_font_name(
GTK_FONT_SELECTION_DIALOG(dialog));

font_desc = pango_font_description_from_string(fontname);

gtk_widget_modify_font(GTK_WIDGET(label), font_desc);

g_free(fontname);
}

If the user clicks on the OK or Apply button, we proceed. We get the selected font name using the
gtk_font_selection_dialog_get_font_name() function. Then we change the label's
font to the selected font name.
Figure:
GtkFontSelectionDialog

GtkColorSelectionDialog
GtkColorSelectionDialog is a dialog for selecting a colour.

colordialog.c
#include <gtk/gtk.h>

void select_font(GtkWidget *widget, gpointer label) {

GtkResponseType result;
GtkColorSelection *colorsel;

GtkWidget *dialog = gtk_color_selection_dialog_new("Font Color");

result = gtk_dialog_run(GTK_DIALOG(dialog));

if (result == GTK_RESPONSE_OK) {

GdkColor color;

colorsel = GTK_COLOR_SELECTION(
GTK_COLOR_SELECTION_DIALOG(dialog)->colorsel);
gtk_color_selection_get_current_color(colorsel,
&color);

gtk_widget_modify_fg(GTK_WIDGET(label),
GTK_STATE_NORMAL,
&color);
}

gtk_widget_destroy(dialog);
}

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


GtkWidget *window;
GtkWidget *widget;
GtkWidget *label;
GtkWidget *vbox;

GtkWidget *toolbar;
GtkToolItem *font;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 280, 200);
gtk_window_set_title(GTK_WINDOW(window), "Color Selection Dialog");

vbox = gtk_vbox_new(FALSE, 0);


gtk_container_add(GTK_CONTAINER(window), vbox);

toolbar = gtk_toolbar_new();
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);

gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);

font = gtk_tool_button_new_from_stock(GTK_STOCK_SELECT_COLOR);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), font, -1);

gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 5);

label = gtk_label_new("ZetCode");
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, FALSE, 5);

g_signal_connect(G_OBJECT(font), "clicked",
G_CALLBACK(select_font), label);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

The example is very similar to the previous one. This time we change the colour of the label.
GtkWidget *dialog = gtk_color_selection_dialog_new("Font Color");
result = gtk_dialog_run(GTK_DIALOG(dialog));

We create and show the GtkColorSelectionDialog.


if (result == GTK_RESPONSE_OK) {

GdkColor color;

colorsel = GTK_COLOR_SELECTION(
GTK_COLOR_SELECTION_DIALOG(dialog)->colorsel);
gtk_color_selection_get_current_color(colorsel,
&color);

gtk_widget_modify_fg(GTK_WIDGET(label),
GTK_STATE_NORMAL,
&color);
}

If we press the OK button, we get the colour and modify the label's colour. The colour value is
returned with the gtk_color_selection_get_current_color() function.

Figure:
GtkColorSelectionDialog
This chapter was about dialogs in GTK+.

GTK+ Widgets
In this part of the GTK+ programming tutorial, we will introduce some GTK+ widgets.
Widgets are basic building blocks of a GUI application. Over the years, several widgets became a
standard in programming toolkits; for example a button, a check box, or a scroll bar. The GTK+
toolkit's philosophy is to keep the number of widgets at a minimum level. More specialised widgets
are created as custom GTK+ widgets.

GtkButton
GtkButton is a simple widget that is used to trigger an action.

button.c
#include <gtk/gtk.h>

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

GtkWidget *window;
GtkWidget *halign;
GtkWidget *btn;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkButton");
gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

halign = gtk_alignment_new(0, 0, 0, 0);


gtk_container_add(GTK_CONTAINER(window), halign);

btn = gtk_button_new_with_label("Quit");
gtk_widget_set_size_request(btn, 70, 30);

gtk_container_add(GTK_CONTAINER(halign), btn);

g_signal_connect(G_OBJECT(btn), "clicked",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

The example shows a button that is positioned in the upper-left corner of the window. The
application quits when we click on the button.
btn = gtk_button_new_with_label("Quit");

The gtk_button_new_with_label() creates a new GtkButton with a label.


g_signal_connect(G_OBJECT(btn), "clicked",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));

The button's clicked signal is connected to the gtk_main_quit() function, which terminates
the application.

Figure: GtkButton

GtkCheckButton
GtkCheckButton is a widget that has two states: on and off. The on state is visualized by a
check mark.
checkbutton.c
#include <gtk/gtk.h>
void toggle_title(GtkWidget *widget, gpointer window) {

if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
gtk_window_set_title(window, "GtkCheckButton");
} else {
gtk_window_set_title(window, "");
}
}

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

GtkWidget *window;
GtkWidget *halign;
GtkWidget *cb;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
gtk_window_set_title(GTK_WINDOW(window), "GtkCheckButton");

halign = gtk_alignment_new(0, 0, 0, 0);


gtk_container_add(GTK_CONTAINER(window), halign);

cb = gtk_check_button_new_with_label("Show title");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb), TRUE);

GTK_WIDGET_UNSET_FLAGS(cb, GTK_CAN_FOCUS);
gtk_container_add(GTK_CONTAINER(halign), cb);

g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);

g_signal_connect(cb, "clicked",
G_CALLBACK(toggle_title), (gpointer) window);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

The example displays a window title depending on the state of the GtkCheckButton.
cb = gtk_check_button_new_with_label("Show title");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb), TRUE);

The GtkCheckButton is created and is marked by default; the title is initially shown.
GTK_WIDGET_UNSET_FLAGS(cb, GTK_CAN_FOCUS);

This code line disables the focus.


if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
gtk_window_set_title(window, "GtkCheckButton");
} else {
gtk_window_set_title(window, "");
}
We show the title of the window, depending on the state of the GtkCheckButton. To set a title of
the window, we use the gtk_window_set_title().

Figure: GtkCheckButton

GtkFrame
GtkFrame is a bin with a decorative frame and optional label.

frames.c
#include <gtk/gtk.h>

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

GtkWidget *window;
GtkWidget *table;

GtkWidget *frame1;
GtkWidget *frame2;
GtkWidget *frame3;
GtkWidget *frame4;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 250, 250);
gtk_window_set_title(GTK_WINDOW(window), "GtkFrame");

gtk_container_set_border_width(GTK_CONTAINER(window), 10);

table = gtk_table_new(2, 2, TRUE);


gtk_table_set_row_spacings(GTK_TABLE(table), 10);
gtk_table_set_col_spacings(GTK_TABLE(table), 10);
gtk_container_add(GTK_CONTAINER(window), table);

frame1 = gtk_frame_new("Shadow In");


gtk_frame_set_shadow_type(GTK_FRAME(frame1), GTK_SHADOW_IN);
frame2 = gtk_frame_new("Shadow Out");
gtk_frame_set_shadow_type(GTK_FRAME(frame2), GTK_SHADOW_OUT);
frame3 = gtk_frame_new("Shadow Etched In");
gtk_frame_set_shadow_type(GTK_FRAME(frame3), GTK_SHADOW_ETCHED_IN);
frame4 = gtk_frame_new("Shadow Etched Out");
gtk_frame_set_shadow_type(GTK_FRAME(frame4), GTK_SHADOW_ETCHED_OUT);

gtk_table_attach_defaults(GTK_TABLE(table), frame1, 0, 1, 0, 1);


gtk_table_attach_defaults(GTK_TABLE(table), frame2, 0, 1, 1, 2);
gtk_table_attach_defaults(GTK_TABLE(table), frame3, 1, 2, 0, 1);
gtk_table_attach_defaults(GTK_TABLE(table), frame4, 1, 2, 1, 2);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));

gtk_widget_show_all(window);

gtk_main();

return 0;
}

The example shows four different frame types. The frames are attached into a table container.
frame1 = gtk_frame_new("Shadow In");

The gtk_frame_new() function creates a GtkFrame with an optional label.


gtk_frame_set_shadow_type(GTK_FRAME(frame1), GTK_SHADOW_IN);

The gtk_frame_set_shadow_type() function sets a shadow type for a frame.

Figure: GtkFrame

GtkHScale
GtkHScale is a horizontal slider widget for selecting a value from a range of values.

hscale.c
void value_changed(GtkRange *range, gpointer win) {

gdouble val = gtk_range_get_value(range);


gchar *str = g_strdup_printf("%.f", val);
gtk_label_set_text(GTK_LABEL(win), str);

g_free(str);
}

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

GtkWidget *window;
GtkWidget *halign;
GtkWidget *hbox;
GtkWidget *hscale;
GtkWidget *label;
gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 250);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_window_set_title(GTK_WINDOW(window), "GtkHScale");

hbox = gtk_hbox_new(FALSE, 20);

hscale = gtk_hscale_new_with_range(0, 100, 1);


gtk_scale_set_draw_value(GTK_SCALE(hscale), FALSE);
gtk_widget_set_size_request(hscale, 150, -1);
label = gtk_label_new("...");
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 1);

gtk_box_pack_start(GTK_BOX(hbox), hscale, FALSE, FALSE, 0);


gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);

halign = gtk_alignment_new(0, 0, 0, 0);


gtk_container_add(GTK_CONTAINER(halign), hbox);
gtk_container_add(GTK_CONTAINER(window), halign);

g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);

g_signal_connect(hscale, "value-changed",
G_CALLBACK(value_changed), label);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

In the example, we have a horizontal scale widget and a label widget. The currently chosen value is
displayed in the label.
gdouble val = gtk_range_get_value(range);

The gtk_range_get_value() function retrieves the currently selected value from the scale
widget.
gchar *str = g_strdup_printf("%.f", val);
gtk_label_set_text(GTK_LABEL(win), str);

We build a string value with the g_strdup_printf() function and set it to the label with the
gtk_label_set_text() function.
hscale = gtk_hscale_new_with_range(0, 100, 1);

The gtk_hscale_new_with_range() function creates a new horizontal scale widget with


the given range. The first parameter is the minimum value, the second parameter is the maximum
value, and the last parameter is the step.
gtk_scale_set_draw_value(GTK_SCALE(hscale), FALSE);
The gtk_scale_set_draw_value() specifies whether the current value is displayed as a
string next to the slider. We turn the value off. Instead, we programmatically set it to the label
widget.

Figure: GtkHScale

GtkLabel
The GtkLabel widget displays text.

label.c
#include <gtk/gtk.h>

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

GtkWidget *window;
GtkWidget *label;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_title(GTK_WINDOW(window), "No sleep");
gtk_container_set_border_width(GTK_CONTAINER(window), 15);

label = gtk_label_new("I've always been too lame\n\


To see what's before me\n\
And I know nothing sweeter than\n\
Champaign from last New Years\n\
Sweet music in my ears\n\
And a night full of no fears\n\
\n\
But if I had one wish fulfilled tonight\n\
I'd ask for the sun to never rise\n\
If God passed a mic to me to speak\n\
I'd say \"Stay in bed, world,\n\
Sleep in peace");

gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
gtk_container_add(GTK_CONTAINER(window), label);

g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);
gtk_main();

return 0;
}

The example shows two verses of a song.


label = gtk_label_new("I've always been too lame\n\
To see what's before me\n\
...

We create a GtkLabel widget. We can create multiline text label by using a new line character.
Note the escape character. We use a rather long string and we do not want to put all the text into one
line. In such cases, we can use an escape character.
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);

The gtk_label_set_justify() function aligns the text in the label. With the
GTK_JUSTIFY_CENTER type, the text is centered.

Figure: GtkLabel

Label with markup


GtkLabel can also display markup language. The markup is the Pango text markup language.

markup.c
#include <gtk/gtk.h>

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

GtkWidget *window;
GtkWidget *label;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 100);
gtk_window_set_title(GTK_WINDOW(window), "Markup label");

gchar *str = "<b>ZetCode</b>, knowledge only matters";


label = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(label), str);

gtk_container_add(GTK_CONTAINER(window), label);
gtk_widget_show(label);

g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show(window);

gtk_main();

return 0;
}

The example shows a portion of text in bold.


gchar *str = "<b>ZetCode</b>, knowledge only matters";

This is the string that is displayed. It contains a simple markup.


label = gtk_label_new(NULL);

We create an empty label.


gtk_label_set_markup(GTK_LABEL(label), str);

The gtk_label_set_markup() parses the string which is marked up and applies its attributes
to the label.

Figure: markup label


In this part of the GTK+ tutorial, we have covered GTK+ widgets.

GTK+ Widgets II
In this part of the GTK+ programming tutorial, we continue covering various GTK+ widgets.

GktComboBoxText
GktComboBoxText is a widget that allows the user to choose from a list of options. The options
are strings.
combobox.c
#include <gtk/gtk.h>

void combo_selected(GtkWidget *widget, gpointer window) {


gchar *text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(widget));
gtk_label_set_text(GTK_LABEL(window), text);
g_free(text);
}

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

GtkWidget *window;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *combo;
GtkWidget *label;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkComboBox");
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);

hbox = gtk_hbox_new(FALSE, 0);


vbox = gtk_vbox_new(FALSE, 15);

combo = gtk_combo_box_new_text();
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Ubuntu");
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Arch");
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Fedora");
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Mint");
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Gentoo");
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Debian");

gtk_box_pack_start(GTK_BOX(vbox), combo, FALSE, FALSE, 0);

label = gtk_label_new("...");
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);

gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);


gtk_container_add(GTK_CONTAINER(window), hbox);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

g_signal_connect(G_OBJECT(combo), "changed",
G_CALLBACK(combo_selected), (gpointer) label);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

The example shows a combo box and a label. The combo box has a list of six options. These are the
names of Linux distros. The label widget shows the selected option from the combo box.
combo = gtk_combo_box_text_new();

The gtk_combo_box_text_new() function creates a simple text-only combo box.


gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Ubuntu");
The gtk_combo_box_text_append_text() function appends a string to the list of strings
stored in the combo box.
label = gtk_label_new("-");

A new label widget is created.


gchar *text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(widget));
gtk_label_set_text(GTK_LABEL(window), text);
g_free(text);

We get the selected text and set the label text to it. The
gtk_combo_box_get_active_text() function returns the currently active string in the
combo box. We set the string to the label with the gtk_label_set_text() function.

Figure: GktComboBoxText

GtkHSeparator
The GtkHSeparator is a horizontal separator. It is a kind of an ornament widget. There is also a
sister GtkVSeparator widget.

separator.c
#include <gtk/gtk.h>

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

GtkWidget *window;
GtkWidget *label1;
GtkWidget *label2;
GtkWidget *hseparator;
GtkWidget *vbox;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_title(GTK_WINDOW(window), "GtkHSeparator");
gtk_window_set_resizable(GTK_WINDOW(window), FALSE);

gtk_container_set_border_width(GTK_CONTAINER(window), 10);

label1 = gtk_label_new("Zinc is a moderately reactive, blue gray metal \


that tarnishes in moist air and burns in air with a bright bluish-green flame,\
giving off fumes of zinc oxide. It reacts with acids, alkalis and other non-
metals.\
If not completely pure, zinc reacts with dilute acids to release hydrogen.");

gtk_label_set_line_wrap(GTK_LABEL(label1), TRUE);

label2 = gtk_label_new("Copper is an essential trace nutrient to all high \


plants and animals. In animals, including humans, it is found primarily in \
the bloodstream, as a co-factor in various enzymes, and in copper-based
pigments. \
However, in sufficient amounts, copper can be poisonous and even fatal to
organisms.");

gtk_label_set_line_wrap(GTK_LABEL(label2), TRUE);

vbox = gtk_vbox_new(FALSE, 10);


gtk_container_add(GTK_CONTAINER(window), vbox);

hseparator = gtk_hseparator_new();

gtk_box_pack_start(GTK_BOX(vbox), label1, FALSE, TRUE, 0);


gtk_box_pack_start(GTK_BOX(vbox), hseparator, FALSE, TRUE, 10);
gtk_box_pack_start(GTK_BOX(vbox), label2, FALSE, TRUE, 0);

g_signal_connect_swapped(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));

gtk_widget_show_all(window);

gtk_main();

return 0;
}

The code example shows definitions of two chemical elements; they are separated by a horizontal
separator. This makes the example more visually appealing.
label1 = gtk_label_new("Zinc is a moderately reactive, blue gray metal \
that tarnishes in moist air and burns in air with a bright bluish-green flame,\
giving off fumes of zinc oxide. It reacts with acids, alkalis and other non-
metals.\
If not completely pure, zinc reacts with dilute acids to release hydrogen.");

We create the first label, the definition of the Zinc element.


gtk_label_set_line_wrap(GTK_LABEL(label2), TRUE);

The gtk_label_set_line_wrap() function break lines if text exceeds the widget's size.
hseparator = gtk_hseparator_new();

The gtk_hseparator_new() creates a new GtkHSeparator.


gtk_box_pack_start(GTK_BOX(vbox), label1, FALSE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hseparator, FALSE, TRUE, 10);
gtk_box_pack_start(GTK_BOX(vbox), label2, FALSE, TRUE, 0);

We place the separator between the labels.


Figure: GtkHSeparator

GtkEntry
GtkEntry is a single line text entry field. This widget is used to enter textual data.

entry.c
#include <gtk/gtk.h>

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

GtkWidget *window;
GtkWidget *table;

GtkWidget *label1;
GtkWidget *label2;
GtkWidget *label3;

GtkWidget *entry1;
GtkWidget *entry2;
GtkWidget *entry3;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_title(GTK_WINDOW(window), "GtkEntry");
gtk_container_set_border_width(GTK_CONTAINER(window), 10);

table = gtk_table_new(3, 2, FALSE);


gtk_container_add(GTK_CONTAINER(window), table);

label1 = gtk_label_new("Name");
label2 = gtk_label_new("Age");
label3 = gtk_label_new("Occupation");

gtk_table_attach(GTK_TABLE(table), label1, 0, 1, 0, 1,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 5, 5);
gtk_table_attach(GTK_TABLE(table), label2, 0, 1, 1, 2,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 5, 5);
gtk_table_attach(GTK_TABLE(table), label3, 0, 1, 2, 3,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 5, 5);

entry1 = gtk_entry_new();
entry2 = gtk_entry_new();
entry3 = gtk_entry_new();

gtk_table_attach(GTK_TABLE(table), entry1, 1, 2, 0, 1,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 5, 5);
gtk_table_attach(GTK_TABLE(table), entry2, 1, 2, 1, 2,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 5, 5);
gtk_table_attach(GTK_TABLE(table), entry3, 1, 2, 2, 3,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 5, 5);

gtk_widget_show_all(window);

g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_main();

return 0;
}

In our example we show three text entries and three labels.


table = gtk_table_new(3, 2, FALSE);
gtk_container_add(GTK_CONTAINER(window), table);

To organise our widgets, we use the table container widget.


entry1 = gtk_entry_new();

The gtk_entry_new() function creates a new GtkEntry.


gtk_table_attach(GTK_TABLE(table), entry1, 1, 2, 0, 1,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 5, 5);
gtk_table_attach(GTK_TABLE(table), entry2, 1, 2, 1, 2,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 5, 5);
gtk_table_attach(GTK_TABLE(table), entry3, 1, 2, 2, 3,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 5, 5);

We attach the widgets to the table widget.

Figure: GtkEntry

GtkImage
GtkImage is a widget used to display an image.

image.c
#include <gtk/gtk.h>

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


GtkWidget *window;
GtkWidget *image;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_title(GTK_WINDOW(window), "Red Rock");

image = gtk_image_new_from_file("redrock.jpg");

gtk_container_add(GTK_CONTAINER(window), image);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

In our example we show an image of a castle.


image = gtk_image_new_from_file("redrock.png");

The gtk_image_new_from_file() creates a new GtkImage from the specified filename .


If the file is not found or cannot be loaded, the resulting GtkImage displays a "broken image"
icon.
gtk_container_add(GTK_CONTAINER(window), image);

The image is added to the window container.

GtkStatusbar
GtkStatusbar displays status information. It is placed at the bottom of the application window.

statusbar.c
#include <gtk/gtk.h>

void button_pressed(GtkWidget *widget, gpointer window) {

gchar *str;
str = g_strdup_printf("%s button clicked",
gtk_button_get_label(GTK_BUTTON(widget)));

gtk_statusbar_push(GTK_STATUSBAR(window),
gtk_statusbar_get_context_id(GTK_STATUSBAR(window), str), str);
g_free(str);
}

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

GtkWidget *window;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *halign;
GtkWidget *balign;
GtkWidget *button1;
GtkWidget *button2;
GtkWidget *statusbar;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "GtkStatusbar");

vbox = gtk_vbox_new(FALSE, 0);

hbox = gtk_hbox_new(FALSE, 0);


gtk_container_add(GTK_CONTAINER(window), vbox);

halign = gtk_alignment_new(0, 0, 0, 0);


gtk_container_add(GTK_CONTAINER(halign), hbox);
gtk_box_pack_start(GTK_BOX(vbox), halign, TRUE, TRUE, 5);

button1 = gtk_button_new_with_label("OK");
gtk_widget_set_size_request(button1, 70, 30 );
button2 = gtk_button_new_with_label("Apply");
gtk_widget_set_size_request(button2, 70, 30 );

gtk_box_pack_start(GTK_BOX(hbox), button1, FALSE, FALSE, 5);


gtk_box_pack_start(GTK_BOX(hbox), button2, FALSE, FALSE, 0);

balign = gtk_alignment_new(0, 1, 1, 0);


statusbar = gtk_statusbar_new();
gtk_container_add(GTK_CONTAINER(balign), statusbar);
gtk_box_pack_start(GTK_BOX(vbox), balign, FALSE, FALSE, 0);

g_signal_connect(G_OBJECT(button1), "clicked",
G_CALLBACK(button_pressed), G_OBJECT(statusbar));

g_signal_connect(G_OBJECT(button2), "clicked",
G_CALLBACK(button_pressed), G_OBJECT(statusbar));

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

In the code example, there are two buttons and a statusbar. If we click on the button, a message is
displayed in the statusbar. It says which button was pressed.
gchar *str;
str = g_strdup_printf("Button %s clicked",
gtk_button_get_label(GTK_BUTTON(widget)));

The message is built with the g_strdup_printf() function. We get the label of the button with
the gtk_button_get_label() function.
gtk_statusbar_push(GTK_STATUSBAR(window),
gtk_statusbar_get_context_id(GTK_STATUSBAR(window), str), str);
We show the message in the statusbar. The gtk_statusbar_push() function pushes a new
message onto a statusbar’s stack. The function requires a context id, which is returned by the
gtk_statusbar_get_context_id() function.
statusbar = gtk_statusbar_new();

The gtk_statusbar_new() function creates a new GtkStatusbar widget.

Figure: GtkStatusbar

GtkIconView
GtkIconView is a widget which displays a list of icons in a grid. It uses a GtkListStore to
store its data.
iconview.c
#include <gtk/gtk.h>
#include <assert.h>

enum {

COL_DISPLAY_NAME,
COL_PIXBUF,
NUM_COLS
};

GtkTreeModel *init_model(void) {

GtkListStore *list_store;
GdkPixbuf *p1, *p2, *p3, *p4;
GtkTreeIter iter;
GError *err = NULL;

p1 = gdk_pixbuf_new_from_file("ubuntu.png", &err);
p2 = gdk_pixbuf_new_from_file("gnumeric.png", &err);
p3 = gdk_pixbuf_new_from_file("blender.png", &err);
p4 = gdk_pixbuf_new_from_file("inkscape.png", &err);

assert(err==NULL);

list_store = gtk_list_store_new(NUM_COLS,
G_TYPE_STRING, GDK_TYPE_PIXBUF);

gtk_list_store_append(list_store, &iter);
gtk_list_store_set(list_store, &iter, COL_DISPLAY_NAME,
"Ubuntu", COL_PIXBUF, p1, -1);
gtk_list_store_append(list_store, &iter);
gtk_list_store_set(list_store, &iter, COL_DISPLAY_NAME,
"Gnumeric", COL_PIXBUF, p2, -1);
gtk_list_store_append(list_store, &iter);
gtk_list_store_set(list_store, &iter, COL_DISPLAY_NAME,
"Blender", COL_PIXBUF, p3, -1);
gtk_list_store_append(list_store, &iter);
gtk_list_store_set(list_store, &iter, COL_DISPLAY_NAME,
"Inkscape", COL_PIXBUF, p4, -1);

g_object_unref(p1);
g_object_unref(p2);
g_object_unref(p3);
g_object_unref(p4);

return GTK_TREE_MODEL(list_store);
}

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

GtkWidget *window;
GtkWidget *icon_view;
GtkWidget *sw;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

gtk_window_set_title(GTK_WINDOW(window), "IconView");
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_window_set_default_size(GTK_WINDOW(window), 350, 300);

sw = gtk_scrolled_window_new(NULL, NULL);
gtk_container_add(GTK_CONTAINER(window), sw);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
GTK_SHADOW_IN);

icon_view = gtk_icon_view_new_with_model(init_model());
gtk_container_add(GTK_CONTAINER(sw), icon_view);

gtk_icon_view_set_text_column(GTK_ICON_VIEW(icon_view),
COL_DISPLAY_NAME);
gtk_icon_view_set_pixbuf_column(GTK_ICON_VIEW(icon_view), COL_PIXBUF);
gtk_icon_view_set_selection_mode(GTK_ICON_VIEW(icon_view),
GTK_SELECTION_MULTIPLE);

g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

The example displays 4 icons. The icons represent four prominent open source projects.
p1 = gdk_pixbuf_new_from_file("ubuntu.png", &err);
p2 = gdk_pixbuf_new_from_file("gnumeric.png", &err);
p3 = gdk_pixbuf_new_from_file("blender.png", &err);
p4 = gdk_pixbuf_new_from_file("inkscape.png", &err);

We load four images from the disk using the gdk_pixbuf_new_from_file() function.
list_store = gtk_list_store_new(NUM_COLS,
G_TYPE_STRING, GDK_TYPE_PIXBUF);

The gtk_list_store_new() function creates a GtkListStore, which is a list model for


the GtkTreeView and GtkIconView widgets. We store textual and pixbuf data.
gtk_list_store_append(list_store, &iter);
gtk_list_store_set(list_store, &iter, COL_DISPLAY_NAME,
"ubuntu", COL_PIXBUF, p1, -1);

This code adds a new row into the model.


icon_view = gtk_icon_view_new_with_model(init_model());

The gtk_icon_view_new_with_model() creates a new GtkIconView widget with a


GtkTreeModel.
gtk_container_add(GTK_CONTAINER(sw), icon_view);

The GtkIconView is a container widget. We add it into the GtkScrolledWindow.


gtk_icon_view_set_text_column(GTK_ICON_VIEW(icon_view),
COL_DISPLAY_NAME);

The gtk_icon_view_set_text_column() function sets which column is a string column.


gtk_icon_view_set_pixbuf_column(GTK_ICON_VIEW(icon_view),
COL_PIXBUF);

The gtk_icon_view_set_pixbuf_column() function sets which is the column with


pixbufs.
gtk_icon_view_set_selection_mode(GTK_ICON_VIEW(icon_view),
GTK_SELECTION_MULTIPLE);

The gtk_icon_view_set_selection_mode() sets the selection mode of the


GtkIconView. Choosing the GTK_SELECTION_MULTIPLE mode, it is possible to choose
multiple icons.

Figure: IconView
In this part of the GTK+ tutorial, we have continued covering GTK+ widgets.
GtkTreeView widget
In this part of the GTK+ programming tutorial, we work with the GtkTreeView widget.

GtkTreeView widget is a complex widget which can be used to display lists and trees. The
widget can have one or multiple columns. The GtkTreeView widget has a MVC (Model View
Controller) design architecture. This means that the data is separated from the view.
There are several other objects that are used with the GtkTreeView widget. The
GtkCellRenderer determines how the data is going to be displayed in the
GtkTreeViewColumn. The GtkListStore and the GtkTreeStore represent the model.
They handle data that are displayed in the GtkTreeView widget. GtkTreeIter is a structure
used to refer to a row in the GtkTreeView. GtkTreeSelection is an object that handles
selections.

List view
The first example will show a simple list view. We will display textual data.
listview.c
#include <gtk/gtk.h>

enum {

LIST_ITEM = 0,
N_COLUMNS
};

void init_list(GtkWidget *list) {

GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkListStore *store;

renderer = gtk_cell_renderer_text_new ();


column = gtk_tree_view_column_new_with_attributes("List Items",
renderer, "text", LIST_ITEM, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);

store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING);

gtk_tree_view_set_model(GTK_TREE_VIEW(list),
GTK_TREE_MODEL(store));

g_object_unref(store);
}

void add_to_list(GtkWidget *list, const gchar *str) {

GtkListStore *store;
GtkTreeIter iter;

store = GTK_LIST_STORE(gtk_tree_view_get_model
(GTK_TREE_VIEW(list)));

gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, LIST_ITEM, str, -1);
}

void on_changed(GtkWidget *widget, gpointer label) {

GtkTreeIter iter;
GtkTreeModel *model;
gchar *value;

if (gtk_tree_selection_get_selected(
GTK_TREE_SELECTION(widget), &model, &iter)) {

gtk_tree_model_get(model, &iter, LIST_ITEM, &value, -1);


gtk_label_set_text(GTK_LABEL(label), value);
g_free(value);
}
}

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

GtkWidget *window;
GtkWidget *list;

GtkWidget *vbox;
GtkWidget *label;
GtkTreeSelection *selection;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
list = gtk_tree_view_new();

gtk_window_set_title(GTK_WINDOW(window), "List view");


gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_window_set_default_size(GTK_WINDOW(window), 270, 250);

gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);

vbox = gtk_vbox_new(FALSE, 0);

gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 5);

label = gtk_label_new("");
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);

gtk_container_add(GTK_CONTAINER(window), vbox);

init_list(list);
add_to_list(list, "Aliens");
add_to_list(list, "Leon");
add_to_list(list, "The Verdict");
add_to_list(list, "North Face");
add_to_list(list, "Der Untergang");

selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));

g_signal_connect(selection, "changed",
G_CALLBACK(on_changed), label);

g_signal_connect(G_OBJECT (window), "destroy",


G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);
gtk_main();

return 0;
}

In our code example, we show five items in the GtkTreeView. We have only one column and the
header of the column is hidden. We place a GtkVBox into the window. This box has two widgets: a
GtkTreeView and a GtkLabel.
list = gtk_tree_view_new();

The gtk_tree_view_new() function creates a new GtkTreeView widget.


gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);

We hide the column header with the gtk_tree_view_set_headers_visible() function.


label = gtk_label_new("");
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);

The GtkLabel is created and placed below the GtkTreeView.


init_list(list);

This function initializes the list.


renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes("List Items",
renderer, "text", LIST_ITEM, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);

Inside that function, we create and append one single column.


store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING);

gtk_tree_view_set_model(GTK_TREE_VIEW(list),
GTK_TREE_MODEL(store));

We create a GtkListStore (a model) and set it to the list.


g_object_unref(store);

The TreeView increases the reference of the store object. We decrease the reference from 2 to 1
with the g_object_unref() function. The model is then destroyed automatically with the view.
add_to_list(list, "Aliens");

This user function adds an option to the list.


store = GTK_LIST_STORE(gtk_tree_view_get_model
(GTK_TREE_VIEW(list)));

gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, LIST_ITEM, str, -1);

Inside the add_to_list() function, we get the model using the


gtk_tree_view_get_model() function call. We append a new row and set a value to the
row, which is referenced by an GtkTreeIter object.
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));

The GtkTreeSelection does not need to be created explicitly; it is automatically created with
the GtkTreeView widget. The reference to the widget is obtained using the
gtk_tree_view_get_selection() function call.
g_signal_connect(selection, "changed",
G_CALLBACK(on_changed), label);

The changed signal of the GtkTreeSelection is connected to the on_changed() handler.


if (gtk_tree_selection_get_selected(
GTK_TREE_SELECTION(widget), &model, &iter)) {

The gtk_tree_selection_get_selected() function sets the iter to the currently


selected node.
gtk_tree_model_get(model, &iter, LIST_ITEM, &value, -1);

Inside the handler function, we get the value of the cell in the row referenced by the iter object.
gtk_label_set_text(GTK_LABEL(label), value);

The retrieved value is set to the label with the gtk_label_set_text() function.

Figure: List view

Dynamic List view


The second example adds additional functionality to the previous one. We will be able to add and
remove items from the list view.
dynamiclistview.c
#include <gtk/gtk.h>

enum {

LIST_ITEM = 0,
N_COLUMNS
};
GtkWidget *list;

void append_item(GtkWidget *widget, gpointer entry) {

GtkListStore *store;
GtkTreeIter iter;

const gchar *str = gtk_entry_get_text(entry);

store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));

gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, LIST_ITEM, str, -1);

gtk_entry_set_text(entry, "");
}

void remove_item(GtkWidget *widget, gpointer selection) {

GtkListStore *store;
GtkTreeModel *model;
GtkTreeIter iter;

store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));

if (gtk_tree_model_get_iter_first(model, &iter) == FALSE) {


return;
}

if (gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection),
&model, &iter)) {
gtk_list_store_remove(store, &iter);
}
}

void remove_all(GtkWidget *widget, gpointer selection) {

GtkListStore *store;
GtkTreeModel *model;
GtkTreeIter iter;

store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));

if (gtk_tree_model_get_iter_first(model, &iter) == FALSE) {


return;
}

gtk_list_store_clear(store);
}

void init_list(GtkWidget *list) {

GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkListStore *store;

renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes("List Item",
renderer, "text", LIST_ITEM, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);

store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING);


gtk_tree_view_set_model(GTK_TREE_VIEW(list), GTK_TREE_MODEL(store));

g_object_unref(store);
}

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

GtkWidget *window;
GtkWidget *sw;

GtkWidget *remove;
GtkWidget *add;
GtkWidget *removeAll;
GtkWidget *entry;

GtkWidget *vbox;
GtkWidget *hbox;

GtkTreeSelection *selection;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

gtk_window_set_title(GTK_WINDOW(window), "List view");


gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_container_set_border_width(GTK_CONTAINER (window), 10);
gtk_widget_set_size_request(window, 370, 270);

sw = gtk_scrolled_window_new(NULL, NULL);
list = gtk_tree_view_new();
gtk_container_add(GTK_CONTAINER(sw), list);

gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
GTK_SHADOW_ETCHED_IN);

gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);

vbox = gtk_vbox_new(FALSE, 0);

gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 5);

hbox = gtk_hbox_new(FALSE, 5);

add = gtk_button_new_with_label("Add");
remove = gtk_button_new_with_label("Remove");
removeAll = gtk_button_new_with_label("Remove All");
entry = gtk_entry_new();
gtk_widget_set_size_request(entry, 120, -1);

gtk_box_pack_start(GTK_BOX(hbox), add, FALSE, TRUE, 3);


gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, TRUE, 3);
gtk_box_pack_start(GTK_BOX(hbox), remove, FALSE, TRUE, 3);
gtk_box_pack_start(GTK_BOX(hbox), removeAll, FALSE, TRUE, 3);

gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 3);

gtk_container_add(GTK_CONTAINER(window), vbox);

init_list(list);
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));

g_signal_connect(G_OBJECT(add), "clicked",
G_CALLBACK(append_item), entry);

g_signal_connect(G_OBJECT(remove), "clicked",
G_CALLBACK(remove_item), selection);

g_signal_connect(G_OBJECT(removeAll), "clicked",
G_CALLBACK(remove_all), selection);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

In the example, we have three buttons and one text entry. The buttons add a new item, remove the
selected item, and remove all items.
sw = gtk_scrolled_window_new(NULL, NULL);
list = gtk_tree_view_new();
gtk_container_add(GTK_CONTAINER(sw), list);

The GtkTreeView is placed inside a scrolled window.


if (gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection),
&model, &iter)) {
gtk_list_store_remove(store, &iter);
}

The gtk_list_store_remove() function removes an item from the list.


gtk_list_store_clear(store);

The gtk_list_store_clear() removes all items from the list.


if (gtk_tree_model_get_iter_first(model, &iter) == FALSE) {
return;
}

This code checks if there is some item left in the list. Obviously, we can remove items only if there
is at least one left in the list.
Figure: Dynamic List view

Tree view
The following example uses the GtkTreeView widget to display hierarchical data. In the previous
two examples, we used the list view; now we are going to use tree view.
treeview.c
#include <gtk/gtk.h>

enum {
COLUMN = 0,
NUM_COLS
};

void on_changed(GtkWidget *widget, gpointer statusbar) {

GtkTreeIter iter;
GtkTreeModel *model;
gchar *value;

if (gtk_tree_selection_get_selected(
GTK_TREE_SELECTION(widget), &model, &iter)) {

gtk_tree_model_get(model, &iter, COLUMN, &value, -1);


gtk_statusbar_push(GTK_STATUSBAR(statusbar),
gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar),
value), value);
g_free(value);
}
}

GtkTreeModel *create_and_fill_model(void) {

GtkTreeStore *treestore;
GtkTreeIter toplevel, child;

treestore = gtk_tree_store_new(NUM_COLS,
G_TYPE_STRING);

gtk_tree_store_append(treestore, &toplevel, NULL);


gtk_tree_store_set(treestore, &toplevel,
COLUMN, "Scripting languages",
-1);

gtk_tree_store_append(treestore, &child, &toplevel);


gtk_tree_store_set(treestore, &child,
COLUMN, "Python",
-1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child,
COLUMN, "Perl",
-1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child,
COLUMN, "PHP",
-1);

gtk_tree_store_append(treestore, &toplevel, NULL);


gtk_tree_store_set(treestore, &toplevel,
COLUMN, "Compiled languages",
-1);

gtk_tree_store_append(treestore, &child, &toplevel);


gtk_tree_store_set(treestore, &child,
COLUMN, "C",
-1);

gtk_tree_store_append(treestore, &child, &toplevel);


gtk_tree_store_set(treestore, &child,
COLUMN, "C++",
-1);

gtk_tree_store_append(treestore, &child, &toplevel);


gtk_tree_store_set(treestore, &child,
COLUMN, "Java",
-1);

return GTK_TREE_MODEL(treestore);
}

GtkWidget *create_view_and_model(void) {

GtkTreeViewColumn *col;
GtkCellRenderer *renderer;
GtkWidget *view;
GtkTreeModel *model;

view = gtk_tree_view_new();

col = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(col, "Programming languages");
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);

renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_add_attribute(col, renderer,
"text", COLUMN);

model = create_and_fill_model();
gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
g_object_unref(model);

return view;
}
int main(int argc, char *argv[]) {

GtkWidget *window;
GtkWidget *view;
GtkTreeSelection *selection;
GtkWidget *vbox;
GtkWidget *statusbar;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_title(GTK_WINDOW(window), "Tree view");
gtk_widget_set_size_request(window, 350, 300);

vbox = gtk_vbox_new(FALSE, 2);


gtk_container_add(GTK_CONTAINER(window), vbox);

view = create_view_and_model();
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));

gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 1);

statusbar = gtk_statusbar_new();
gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, TRUE, 1);

g_signal_connect(selection, "changed",
G_CALLBACK(on_changed), statusbar);

g_signal_connect (G_OBJECT (window), "destroy",


G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

In the example, we divide programming languages into two groups: scripting languages and
compiled languages. The language categories serve as toplevel nodes for their list of items. The
currently selected item is shown in the statusbar. The steps to create a tree view are very similar to
creating a list view.
treestore = gtk_tree_store_new(NUM_COLS,
G_TYPE_STRING);

The gtk_tree_store_new() function creates a GtkTreeStore, which is a tree-like data


structure that is used with the GtkTreeView.
gtk_tree_store_append(treestore, &toplevel, NULL);
gtk_tree_store_set(treestore, &toplevel,
COLUMN, "Scripting languages",
-1);

These two lines create a toplevel node.


gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child,
COLUMN, "Python",
-1);

Here we add a child item into the toplevel node.

Figure: Tree View


In this chapter we covered the GtkTreeView widget.

GtkTextView widget
In this part of the GTK+ programming tutorial, we work with the GtkTextView widget.

GtkTextView widget is used for displaying and editing multiline text. GtkTextView widget
has also the MVC design. The GtkTextView represents the view component and the
GtkTextBuffer represents the model component. GtkTextBuffer is used to manipulate
textual data. GtkTextTag is an attribute that can be applied to the text. GtkTextIter
represents a position between two characters in the text. All manipulation with the text is done using
text iterators.

Simple example
In our first example, we show some of the GtkTextView's functionality. We show how to apply
various text tags to the text data.
simpletextview.c
#include <gtk/gtk.h>

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

GtkWidget *window;
GtkWidget *view;
GtkWidget *vbox;

GtkTextBuffer *buffer;
GtkTextIter start, end;
GtkTextIter iter;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "GtkTextView");

vbox = gtk_vbox_new(FALSE, 0);


view = gtk_text_view_new();
gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);

buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));

gtk_text_buffer_create_tag(buffer, "gap",
"pixels_above_lines", 30, NULL);

gtk_text_buffer_create_tag(buffer, "lmarg",
"left_margin", 5, NULL);
gtk_text_buffer_create_tag(buffer, "blue_fg",
"foreground", "blue", NULL);
gtk_text_buffer_create_tag(buffer, "gray_bg",
"background", "gray", NULL);
gtk_text_buffer_create_tag(buffer, "italic",
"style", PANGO_STYLE_ITALIC, NULL);
gtk_text_buffer_create_tag(buffer, "bold",
"weight", PANGO_WEIGHT_BOLD, NULL);

gtk_text_buffer_get_iter_at_offset(buffer, &iter, 0);

gtk_text_buffer_insert(buffer, &iter, "Plain text\n", -1);


gtk_text_buffer_insert_with_tags_by_name(buffer, &iter,
"Colored Text\n", -1, "blue_fg", "lmarg", NULL);
gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
"Text with colored background\n", -1, "lmarg", "gray_bg", NULL);

gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,


"Text in italics\n", -1, "italic", "lmarg", NULL);

gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,


"Bold text\n", -1, "bold", "lmarg", NULL);

gtk_container_add(GTK_CONTAINER(window), vbox);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

The example shows some text with different GtkTextTags applied.


view = gtk_text_view_new();

The gtk_text_view_new() function creates a new GtkTextView widget.


gtk_text_buffer_create_tag(buffer, "blue_fg",
"foreground", "blue", NULL);
The gtk_text_buffer_create_tag() function creates a tag and adds it to the tag table for
the buffer. The second parameter is the tag name. The tag changes the colour of the text to blue.
gtk_text_buffer_insert_with_tags_by_name(buffer, &iter,
"Colored Text\n", -1, "blue_fg", "lmarg", NULL);

The gtk_text_buffer_insert_with_tags_by_name() function inserts text with


blue_fg and lmarg text tags. The tags are recognized by their names.

Figure: GtkTextView

Lines and columns


The following example displays the current line and column of the text cursor.
linescols.c
#include <gtk/gtk.h>

update_statusbar(GtkTextBuffer *buffer,
GtkStatusbar *statusbar) {
gchar *msg;
gint row, col;
GtkTextIter iter;

gtk_statusbar_pop(statusbar, 0);

gtk_text_buffer_get_iter_at_mark(buffer,
&iter, gtk_text_buffer_get_insert(buffer));

row = gtk_text_iter_get_line(&iter);
col = gtk_text_iter_get_line_offset(&iter);

msg = g_strdup_printf("Col: %d Ln: %d", col+1, row+1);

gtk_statusbar_push(statusbar, 0, msg);

g_free(msg);
}

void mark_set_callback(GtkTextBuffer *buffer,


const GtkTextIter *new_location, GtkTextMark *mark, gpointer data) {

update_statusbar(buffer, GTK_STATUSBAR(data));
}

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


GtkWidget *window;
GtkWidget *vbox;

GtkWidget *toolbar;
GtkWidget *view;
GtkWidget *statusbar;
GtkToolItem *exit;
GtkTextBuffer *buffer;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 350, 300);
gtk_window_set_title(GTK_WINDOW(window), "Lines & columns");

vbox = gtk_vbox_new(FALSE, 0);


gtk_container_add(GTK_CONTAINER(window), vbox);

toolbar = gtk_toolbar_new();
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);

exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exit, -1);

gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 5);

view = gtk_text_view_new();
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);
gtk_widget_grab_focus(view);

buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));

statusbar = gtk_statusbar_new();
gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0);

g_signal_connect(G_OBJECT(exit), "clicked",
G_CALLBACK(gtk_main_quit), NULL);

g_signal_connect(buffer, "changed",
G_CALLBACK(update_statusbar), statusbar);

g_signal_connect_object(buffer, "mark_set",
G_CALLBACK(mark_set_callback), statusbar, 0);

g_signal_connect_swapped(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

update_statusbar(buffer, GTK_STATUSBAR(statusbar));

gtk_main();

return 0;
}

In this code example, we show the current position of the text cursor in the statusbar.
g_signal_connect(buffer, "changed",
G_CALLBACK(update_statusbar), statusbar);
When we change the text, we call the update_statusbar() handler.
g_signal_connect_object(buffer, "mark_set",
G_CALLBACK(mark_set_callback), statusbar, 0);

The mark_set signal is emitted when the cursor moves.


gtk_statusbar_pop(statusbar, 0);

This code line clears the message with context ID 0 from the statusbar.
gtk_text_buffer_get_iter_at_mark(buffer,
&iter, gtk_text_buffer_get_insert(buffer));

row = gtk_text_iter_get_line(&iter);
col = gtk_text_iter_get_line_offset(&iter);

These lines determine the current line and column.


msg = g_strdup_printf("Col %d Ln %d", col+1, row+1);

The g_strdup_printf() is used to build the text to be displayed on the statusbar.


gtk_statusbar_push(statusbar, 0, msg);

We show the text on the statusbar with the gtk_statusbar_push() function.

Figure: Lines and


columns

Search & highlight


In the next example, we do some searching in the GtkTextBuffer; we highlight some text
patterns in the text buffer.
search.c
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

gboolean key_pressed(GtkWidget *window,


GdkEventKey* event, GtkTextBuffer *buffer) {

GtkTextIter start_sel, end_sel;


GtkTextIter start_find, end_find;
GtkTextIter start_match, end_match;
gboolean selected;
gchar *text;

if ((event->type == GDK_KEY_PRESS) &&


(event->state & GDK_CONTROL_MASK)) {

switch (event->keyval) {

case GDK_m :
selected = gtk_text_buffer_get_selection_bounds(buffer,
&start_sel, &end_sel);
if (selected) {
gtk_text_buffer_get_start_iter(buffer, &start_find);
gtk_text_buffer_get_end_iter(buffer, &end_find);

gtk_text_buffer_remove_tag_by_name(buffer, "gray_bg",
&start_find, &end_find);
text = (gchar *) gtk_text_buffer_get_text(buffer, &start_sel,
&end_sel, FALSE);

while (gtk_text_iter_forward_search(&start_find, text,


GTK_TEXT_SEARCH_TEXT_ONLY |
GTK_TEXT_SEARCH_VISIBLE_ONLY,
&start_match, &end_match, NULL)) {

gtk_text_buffer_apply_tag_by_name(buffer, "gray_bg",
&start_match, &end_match);
gint offset = gtk_text_iter_get_offset(&end_match);
gtk_text_buffer_get_iter_at_offset(buffer,
&start_find, offset);
}

g_free(text);
}

break;

case GDK_r:
gtk_text_buffer_get_start_iter(buffer, &start_find);
gtk_text_buffer_get_end_iter(buffer, &end_find);

gtk_text_buffer_remove_tag_by_name(buffer, "gray_bg",
&start_find, &end_find);
break;
}
}

return FALSE;
}

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

GtkWidget *window;
GtkWidget *view;
GtkWidget *vbox;

GtkTextBuffer *buffer;
GtkTextIter start, end;
GtkTextIter iter;
gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 350, 300);
gtk_window_set_title(GTK_WINDOW(window), "Search & highlight");
GTK_WINDOW(window)->allow_shrink = TRUE;

vbox = gtk_vbox_new(FALSE, 0);


view = gtk_text_view_new();
gtk_widget_add_events(view, GDK_BUTTON_PRESS_MASK);
gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);

buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
gtk_text_buffer_create_tag(buffer, "gray_bg",
"background", "lightgray", NULL);
gtk_container_add(GTK_CONTAINER(window), vbox);

g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

g_signal_connect(G_OBJECT(window), "key-press-event",
G_CALLBACK(key_pressed), buffer);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

In our code example we use keyboard shortcuts. The Ctrl+M shortcut highlights the all
occurrences of the currently selected text. The Ctrl+R removes the highlights from the text.
gtk_text_buffer_create_tag(buffer, "gray_bg",
"background", "lightgray", NULL);

This is the GtkTextTag that we use in our example. The tag makes the background of the text
gray.
selected = gtk_text_buffer_get_selection_bounds(buffer,
&start_sel, &end_sel);

With the gtk_text_buffer_get_selection_bounds() function, we get the start and end


positions of the selected text.
gtk_text_buffer_get_start_iter(buffer, &start_find);
gtk_text_buffer_get_end_iter(buffer, &end_find);

We get the first and the last position in the text buffer.
gtk_text_buffer_remove_tag_by_name(buffer, "gray_bg",
&start_find, &end_find);

With the gtk_text_buffer_remove_tag_by_name() funcion, we remove any previous


text tag.
text = (gchar *) gtk_text_buffer_get_text(buffer, &start_sel,
&end_sel, FALSE);
We obtain the selected text. It is the text we are going to search for.
while (gtk_text_iter_forward_search(&start_find, text,
GTK_TEXT_SEARCH_TEXT_ONLY |
GTK_TEXT_SEARCH_VISIBLE_ONLY,
&start_match, &end_match, NULL)) {

gtk_text_buffer_apply_tag_by_name(buffer, "gray_bg",
&start_match, &end_match);
gint offset = gtk_text_iter_get_offset(&end_match);
gtk_text_buffer_get_iter_at_offset(buffer,
&start_find, offset);
}

This code searches for all occurences of the selected text. If we find any match, we apply the text
tag. After the match, the ending point of the word becomes the starting point for the next search.

Figure: Search & Highlight


In this chapter we covered the GtkTextView widget.

Custom GTK+ widget


In this part of the GTK+ programming tutorial, we create a custom GTK+ widget. We use the Cairo
graphics library.

CPU widget
In the next example we create a custom CPU widget.
mycpu.h
#ifndef __MY_CPU_H__
#define __MY_CPU_H__

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

G_BEGIN_DECLS
/* Standart GObject macros */
#define MY_TYPE_CPU (my_cpu_get_type())
#define MY_CPU(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), MY_TYPE_CPU, MyCpu))
#define MY_CPU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), MY_TYPE_CPU,
MyCpuClass))
#define MY_IS_CPU(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), MY_TYPE_CPU))
#define MY_IS_CPU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), MY_TYPE_CPU))
#define MY_CPU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), MY_TYPE_CPU,
MyCpuClass))

/* Type definition */
typedef struct _MyCpu MyCpu;
typedef struct _MyCpuClass MyCpuClass;
typedef struct _MyCpuPrivate MyCpuPrivate;

struct _MyCpu {

GtkWidget parent;

/*< Private >*/


MyCpuPrivate *priv;
};

struct _MyCpuClass {

GtkWidgetClass parent_class;
};

/* Public API */
GType my_cpu_get_type(void) G_GNUC_CONST;
GtkWidget *my_cpu_new(void);

gdouble my_cpu_get_percent(MyCpu *cpu);


void my_cpu_set_percent(MyCpu *cpu, gdouble sel);

G_END_DECLS

#endif /* __MY_CPU_H__ */

In the mycpu.h file, we define types, macros, and functions for the custom widget.

mycpu.c
/* mycpu.c */

#include "mycpu.h"

/* Properties enum */
enum {

P_0, /* Padding */
P_PERCENT
};

/* Private data structure */


struct _MyCpuPrivate {

gdouble percent;
GdkWindow *window;
};

const gint WIDTH = 80;


const gint HEIGHT = 100;

/* Internal API */
static void my_cpu_set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec);
static void my_cpu_get_property(GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec);
static void my_cpu_size_request(GtkWidget *widget,
GtkRequisition *requisition);
static void my_cpu_size_allocate(GtkWidget *widget,
GtkAllocation *allocation);
static void my_cpu_realize(GtkWidget *widget);
static gboolean my_cpu_expose(GtkWidget *widget,
GdkEventExpose *event);

/* Define type */
G_DEFINE_TYPE(MyCpu, my_cpu, GTK_TYPE_WIDGET)

/* Initialization */
static void my_cpu_class_init(MyCpuClass *klass) {

GObjectClass *g_class;
GtkWidgetClass *w_class;
GParamSpec *pspec;

g_class = G_OBJECT_CLASS(klass);
w_class = GTK_WIDGET_CLASS(klass);

/* Override widget class methods */


g_class->set_property = my_cpu_set_property;
g_class->get_property = my_cpu_get_property;

w_class->realize = my_cpu_realize;
w_class->size_request = my_cpu_size_request;
w_class->size_allocate = my_cpu_size_allocate;
w_class->expose_event = my_cpu_expose;

/* Install property */
pspec = g_param_spec_double("percent", "Percent",
"What CPU load should be displayed", 0, 1, 0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

g_object_class_install_property(g_class, P_PERCENT, pspec);

/* Add private data */


g_type_class_add_private(g_class, sizeof(MyCpuPrivate));
}

static void my_cpu_init(MyCpu *cpu) {

MyCpuPrivate *priv;

priv = G_TYPE_INSTANCE_GET_PRIVATE(cpu, MY_TYPE_CPU, MyCpuPrivate);

gtk_widget_set_has_window(GTK_WIDGET(cpu), TRUE);

/* Set default values */


priv->percent = 0;

/* Create cache for faster access */


cpu->priv = priv;
}

/* Overriden virtual methods */


static void my_cpu_set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec) {

MyCpu *cpu = MY_CPU(object);

switch(prop_id) {

case P_PERCENT:

my_cpu_set_percent(cpu, g_value_get_double(value));
break;

default:

G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);


break;
}
}

static void my_cpu_get_property(GObject *object, guint prop_id,


GValue *value, GParamSpec *pspec) {

MyCpu *cpu = MY_CPU(object);

switch(prop_id) {

case P_PERCENT:
g_value_set_double(value, cpu->priv->percent);
break;

default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}

static void my_cpu_realize(GtkWidget *widget) {

MyCpuPrivate *priv = MY_CPU(widget)->priv;


GtkAllocation alloc;
GdkWindowAttr attrs;
guint attrs_mask;

gtk_widget_set_realized(widget, TRUE);

gtk_widget_get_allocation(widget, &alloc);

attrs.x = alloc.x;
attrs.y = alloc.y;
attrs.width = alloc.width;
attrs.height = alloc.height;
attrs.window_type = GDK_WINDOW_CHILD;
attrs.wclass = GDK_INPUT_OUTPUT;
attrs.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;

attrs_mask = GDK_WA_X | GDK_WA_Y;

priv->window = gdk_window_new(gtk_widget_get_parent_window(widget),
&attrs, attrs_mask);
gdk_window_set_user_data(priv->window, widget);
gtk_widget_set_window(widget, priv->window);

widget->style = gtk_style_attach(gtk_widget_get_style( widget ),


priv->window);
gtk_style_set_background(widget->style, priv->window, GTK_STATE_NORMAL);
}

static void my_cpu_size_request(GtkWidget *widget,


GtkRequisition *requisition) {

requisition->width = WIDTH;
requisition->height = HEIGHT;
}

static void my_cpu_size_allocate(GtkWidget *widget,


GtkAllocation *allocation) {

MyCpuPrivate *priv;

priv = MY_CPU(widget)->priv;

gtk_widget_set_allocation(widget, allocation);

if (gtk_widget_get_realized(widget)) {

gdk_window_move_resize(priv->window, allocation->x, allocation->y,


WIDTH, HEIGHT);
}
}

static gboolean my_cpu_expose(GtkWidget *widget,


GdkEventExpose *event) {

MyCpuPrivate *priv = MY_CPU(widget)->priv;


cairo_t *cr;
gint limit;
gint i;

cr = gdk_cairo_create(event->window);

cairo_translate(cr, 0, 7);

cairo_set_source_rgb(cr, 0, 0, 0);
cairo_paint(cr);

limit = 20 - priv->percent / 5;

for (i = 1; i <= 20; i++) {

if (i > limit) {
cairo_set_source_rgb(cr, 0.6, 1.0, 0);
} else {
cairo_set_source_rgb(cr, 0.2, 0.4, 0);
}

cairo_rectangle(cr, 8, i * 4, 30, 3);


cairo_rectangle(cr, 42, i * 4, 30, 3);
cairo_fill(cr);
}

cairo_destroy(cr);

return TRUE;
}

/* Public API */
GtkWidget *my_cpu_new(void) {
return(g_object_new(MY_TYPE_CPU, NULL));
}

gdouble my_cpu_get_percent(MyCpu *cpu) {

g_return_val_if_fail(MY_IS_CPU(cpu), 0);

return(cpu->priv->percent);
}

void my_cpu_set_percent(MyCpu *cpu, gdouble sel) {

g_return_if_fail(MY_IS_CPU(cpu));

cpu->priv->percent = sel;
gtk_widget_queue_draw(GTK_WIDGET(cpu));
}

The mycpu.c is the implementation of the CPU widget. The CPU widget is a GtkWidget on
which we draw with Cairo API. We draw a black background and 40 small rectangles. The
rectangles are drawn in two colours: dark green and bright green colour. The GtkVScale widget
controls the number of the bright green rectangles drawn on the widget.
main.c
#include "mycpu.h"

void cb_changed(GtkRange *range, GtkWidget *cpu) {

my_cpu_set_percent(MY_CPU(cpu), gtk_range_get_value(range));
}

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

GtkWidget *window;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *cpu;
GtkWidget *scale;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_title(GTK_WINDOW(window), "CPU widget");

vbox = gtk_vbox_new(FALSE, 0);


hbox = gtk_hbox_new(FALSE, 25);

cpu = my_cpu_new();
gtk_box_pack_start(GTK_BOX(hbox), cpu, FALSE, FALSE, 0);

scale = gtk_vscale_new_with_range(0, 100, 1);


gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE);
gtk_range_set_inverted(GTK_RANGE(scale), TRUE);
gtk_box_pack_start(GTK_BOX(hbox), scale, FALSE, FALSE, 0);

gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);


gtk_container_add(GTK_CONTAINER(window), vbox);

g_signal_connect(scale, "value-changed", G_CALLBACK(cb_changed), cpu);


g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);

gtk_main();

return 0;
}

This is the main file. We create the custom CPU widget and make it work with the GtkVScale
widget.
gcc -Wall $(pkg-config --cflags gtk+-2.0) -o mycpu.o -c mycpu.c
gcc -Wall $(pkg-config --cflags gtk+-2.0) -o main.o -c main.c
gcc -o test_cpu main.o mycpu.o $(pkg-config --libs gtk+-2.0)

With these commands, we build the example.

Figure: CPU widget


In this chapter we have created a custom GTK+ widget.

S-ar putea să vă placă și