Sunday, April 14, 2013

Menus and Toolbars in GTK+

Menus and Toolbars in GTK+

In this part of the GTK+ programming tutorial, we will work with menus and toolbars.
A menubar is one of the most common parts of the GUI application. It is a group of commands located in various menus. While in console applications you had to remember all those arcane commands, here we have most of the commands grouped into logical parts. There are accepted standards that further reduce the amount of time spending to learn a new application.

Simple menu example

In our first example, we will create a menubar with one file menu. The menu will have only one menu item. By selecting the item the application quits.
#include <gtk/gtk.h>


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

GtkWidget *window;
GtkWidget *vbox;

GtkWidget *menubar;
GtkWidget *filemenu;
GtkWidget *file;
GtkWidget *quit;

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), "menu");

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

menubar = gtk_menu_bar_new();
filemenu = gtk_menu_new();

file = gtk_menu_item_new_with_label("File");
quit = gtk_menu_item_new_with_label("Quit");

gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);

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

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

gtk_widget_show_all(window);

gtk_main();

return 0;
}
Creating a menubar is a bit confusing. We must bear in mind that both a menubar and menus are derived from the same widget, namely a menu shell. menu items are only valid children for menus. They are also used to implement submenus.
menubar = gtk_menu_bar_new();
filemenu = gtk_menu_new();
In this code we create a menubar and a menu.
 gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
This code line implements a file menu. The logic is that the menubar is a menu shell. file menu is also a menu shell. That's why we look at the file menu as a submenu or a subshell.
 gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
Menu items are implemented by calling the gtk_menu_shell_append() function. menu items are appended to menu shells. In our case, quit menu item is appended to a file menu and also the file menu item is appended to the menubar.
g_signal_connect(G_OBJECT(quit), "activate",
G_CALLBACK(gtk_main_quit), NULL);
By selecting the quit menu item, we quit the application.

Simple menu
Figure: Simple menu

Image menus, mnemonics & accelerators

In the next example, we will further explore the functionality that we can use in GTK+. Accelerators are keyboard shortcuts for activating a menu item. Mnemonics are keyboard shortcuts for GUI elements. They are represented as underlined characters.
Note, that you might have Gnome configured not to show menu images. To turn the menu images On/Off, you launch the gconf-editor and go to /desktop/gnome/interface/menus_have_icons. Check/uncheck the option.
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>


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

GtkWidget *window;
GtkWidget *vbox;

GtkWidget *menubar;
GtkWidget *filemenu;
GtkWidget *file;
GtkWidget *new;
GtkWidget *open;
GtkWidget *quit;

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), 250, 200);
gtk_window_set_title(GTK_WINDOW(window), "menu");

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

file = gtk_menu_item_new_with_mnemonic("_File");
new = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
open = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);
sep = gtk_separator_menu_item_new();
quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);

gtk_widget_add_accelerator(quit, "activate", accel_group,
GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);


gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), new);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), open);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), sep);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);

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

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

gtk_widget_show_all(window);

gtk_main();

return 0;
}
The example shows, how to add an image to our menu item. How to set up an accelerator and how to use mnemonics in our GTK+ applications.
accel_group = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
...
quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);
gtk_widget_add_accelerator(quit, "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 Ctrl + q keyboard accelerator.
 file = gtk_menu_item_new_with_mnemonic("_File");
To create a mnemonic, we call the gtk_menu_item_new_with_mnemonic()function. We select the file menu item by pressing the Alt + F.
new = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
open = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);
Here we create two image menu items. By setting the second parameter of the function to NULL, we automatically create accelerators. We provide an image and text for our menu item from internal GTK+ resources.
sep = gtk_separator_menu_item_new();
Menu items can be separated by a horizontal separator. This way we can put menu items into some logical groups.

Menu example
Figure: Menu example

Check menu item

A GtkCheckMenuItem is a menu item with a check box.
#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), 250, 200);
gtk_window_set_title(GTK_WINDOW(window), "view statusbar");

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

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

g_signal_connect_swapped(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;
}
In our code example we show a check menu item. If the check box is activated, the statusbar widget is shown. If not, the statusbar is hidden.
 tog_stat = gtk_check_menu_item_new_with_label("View Statusbar");
The gtk_check_menu_item_new_with_label() function call creates a new check menu item.
 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.

Check menu item
Figure: Check menu item

A toolbar

Menus group commands that we can use in application. Toolbars provide a quick access to the most frequently used commands.
#include <gtk/gtk.h>


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

GtkWidget *window;
GtkWidget *vbox;

GtkWidget *toolbar;
GtkToolItem *new;
GtkToolItem *open;
GtkToolItem *save;
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), 250, 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);

gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);

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

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

save = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), save, -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, 5);

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

g_signal_connect_swapped(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();
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS)
We create a new toolbar. We specify that the toobar buttons show only icons. No text.
 new = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), new, -1);
We create a toolbar button from stock. The toolbar buttons are inserted into the toolbar by the gtk_toolbar_insert() function call.
 sep = gtk_separator_tool_item_new();
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1);
Here we insert a separator into the toolbar.

Toolbar
Figure: Toolbar

Undo redo

The following example demonstrates, how we can 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.
#include <gtk/gtk.h>
#include <string.h>


void undo_redo(GtkWidget *widget, gpointer item)
{
static int count = 2;
const char *name = gtk_widget_get_name(widget);

if ( strcmp(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), 250, 200);
gtk_window_set_title(GTK_WINDOW(window), "undoredo");

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


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_swapped(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 call is used to activate/inactivate the toolbar buttons.

Undo redo
Figure: Undo redo

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

No comments:

Post a Comment