# GTK+ 中的菜单和工具栏

> 原文： [http://zetcode.com/gui/gtk2/menusandtoolbars/](http://zetcode.com/gui/gtk2/menusandtoolbars/)

在 GTK+ 编程教程的这一部分中，我们使用菜单和工具栏。菜单栏是 GUI 应用的常见部分。 它是位于各个菜单中的一组命令。

`GtkMenuBar`是创建菜单栏的窗口小部件。 它包含一对多`GtkMenuItems`。 菜单项是用户可以选择的对象。 `GtkMenu`实现了一个由`GtkMenuItem`对象列表组成的下拉菜单，用户可以对其进行导航和激活以执行应用功能。 `GtkMenu`附加到菜单栏的菜单项或另一个菜单的菜单项。

![Menus](img/c7c9dafe39ebc2e5aa265e4784b03d65.jpg)

图：菜单

该图显示了菜单栏及其菜单的结构。

## 简单菜单示例

在第一个示例中，我们创建带有一个“文件”菜单的菜单栏。

`simplemenu.c`

```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;
}

```

示例中的菜单具有一个菜单项。 通过选择项目，应用退出。

```c
menubar = gtk_menu_bar_new();

```

`gtk_menu_bar_new()`创建一个新的`GtkMenuBar`。

```c
filemenu = gtk_menu_new();

```

`gtk_menu_new()`函数创建一个新的`GtkMenu`。

```c
gtk_menu_item_set_submenu(GTK_MENU_ITEM(fileMi), fileMenu);

```

通过`gtk_menu_item_set_submenu()`函数将`fileMenu`设置为文件菜单项。 菜单是容纳菜单项的容器。 它们本身已插入特定的菜单项。

```c
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), quitMi);

```

通过`gtk_menu_shell_append()`函数将`quitMi`添加到文件菜单。

```c
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fileMi);

```

使用`gtk_menu_shell_append()`函数将文件菜单项添加到菜单栏。 `GtkMenu`和`GtkMenuBar`均来自`GtkMenuShell`。

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

```

通过选择退出菜单项，我们终止了该应用。

![Simple menu](img/6086b0edb74b2d95f71c750789b1f880.jpg)

图：简单菜单

## 子菜单

下一个示例演示如何创建子菜单。 子菜单是另一个菜单中的菜单。

`submenu.c`

```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;
}

```

该示例在另一个菜单内创建一个菜单。 子菜单具有三个菜单项。 我们还添加了一个水平分隔符。

```c
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...");

```

这是带有菜单项的子菜单。

```c
gtk_menu_item_set_submenu(GTK_MENU_ITEM(imprMi), imprMenu);

```

`imprMenu`子菜单被添加到其自己的菜单项中。

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

```

使用`gtk_menu_shell_append()`函数将三个菜单项添加到子菜单。

```c
sep = gtk_separator_menu_item_new();

```

使用`gtk_separator_menu_item_new()`函数创建水平菜单分隔符。

```c
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), imprMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), sep);

```

`imprMi`和分隔符通过`gtk_menu_shell_append()`函数添加到文件菜单。

![Submenu](img/8f3042025e488ddea53ead498798d4d4.jpg)

图：子菜单

## 图像菜单，助记符和加速器

`GtkImageMenuItem`是一个菜单项，在文本标签旁边带有一个图标。 由于用户可以禁用菜单图标的显示，因此我们仍然需要填写文本标签。加速器是用于激活菜单项的键盘快捷键。助记符是 GUI 元素的键盘快捷键。 它们以带下划线的字符表示。 请注意，在某些环境中，我们首先需要按无鼠标修饰符（通常为`Alt`）以显示带下划线的字符。

我们可能还配置了不显示菜单图像的环境。 要打开菜单图像，我们启动`gconf-editor`并转到`/desktop/gnome/interface/menus_have_icons`并检查选项。

`imagemenu.c`

```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;
}

```

该示例包含三个带有图标的菜单项。 菜单项可以用助记符选择。 退出菜单项具有键盘加速器。

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

```

加速器组是一组键盘加速器，通常附加到顶层窗口。 在这里，我们创建 `Ctrl + Q` 键盘加速器。

```c
fileMi = gtk_menu_item_new_with_mnemonic("_File");

```

`gtk_menu_item_new_with_mnemonic()`创建一个可以带有助记符的菜单项。 标签中的下划线表示菜单项的助记符。 该字符与无鼠标修饰符（通常为`Alt`）结合在一起。 在我们的情况下，我们创建了 `Alt + F` 助记符。

```c
newMi = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
openMi = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);

```

`gtk_image_menu_item_new_from_stock()`创建一个`GtkImageMenuItem`，其中包含来自库存项目的图像和文本。

![Menu items with icons](img/7f3fa48a794412d510b7e20af06cb32a.jpg)

图：带图标的菜单项

## `CheckMenuItem`

`GtkCheckMenuItem`是带有复选框的菜单项。

`checkmenuitem.c`

```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;
}

```

该示例在“查看”菜单中包含一个`GtkCheckMenuItem`。 如果该复选框已激活，则显示状态栏小部件。

```c
tog_stat = gtk_check_menu_item_new_with_label("View statusbar");

```

`gtk_check_menu_item_new_with_label()`函数创建一个新的`CheckMenuItem`。

```c
statusbar = gtk_statusbar_new();

```

`gtk_statusbar_new()`函数创建一个新的`GtkStatusbar`小部件。 它用于向用户报告次要消息。

```c
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {

  gtk_widget_show(statusbar);
} else {

  gtk_widget_hide(statusbar);
}

```

如果菜单项中的复选框被激活，我们将显示状态栏小部件。 否则状态栏将被隐藏。

![GtkCheckMenuItem](img/8a1ec68b36122b9dfd24b89b04fdcf56.jpg)

图：`GtkCheckMenuItem`

## 弹出菜单

在下一个示例中，我们创建一个弹出菜单。 弹出菜单也称为上下文菜单。 当我们右键单击 GUI 对象时，通常会显示这种类型的菜单。

`popupmenu.c`

```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;
}

```

在代码示例中，我们创建带有两个菜单项的弹出菜单。 第一个最小化窗口，第二个终止应用。

```c
ebox = gtk_event_box_new();
gtk_container_add(GTK_CONTAINER(window), ebox);

```

为了处理按钮按下事件，我们创建了一个`GtkEventBox`。

```c
pmenu = gtk_menu_new();

```

弹出菜单是`GtkMenu`。

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

```

第一个菜单项已添加到弹出菜单。

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

```

选择第一个菜单项将使窗口最小化。 我们将“隐藏”菜单项的`activate`信号连接到`gtk_window_iconify()`函数。 术语“图标化”是“最小化”的同义词。

```c
g_signal_connect_swapped(G_OBJECT(ebox), "button_press_event", 
    G_CALLBACK(show_popup), pmenu);   

```

当按下鼠标按钮时，会发出`button-press-event`信号。 我们将信号连接到`show_popup()`函数，然后将其传递给弹出菜单。

```c
if (event->type == GDK_BUTTON_PRESS) {

```

在事件处理器内部，我们检查按钮按下事件的类型。

```c
if (bevent->button == RIGHT_CLICK) {      

    gtk_menu_popup(GTK_MENU(widget), NULL, NULL, NULL, NULL,
        bevent->button, bevent->time);
}

```

当触发信号的按钮是鼠标右键时，我们将显示带有`gtk_menu_popup()`函数的弹出菜单。

![Popup menu](img/dca76d2defc272957cea80ad140184b5.jpg)

图：弹出菜单

## 工具栏

菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。 `GtkToolbar`是 GTK+ 中的工具栏小部件。 工具栏可以包含`GtkToolItem`子类的实例，例如 `GtkToolButton`或`GtkSeparatorToolItem`。

`toolbar.c`

```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;
}

```

该代码示例创建了简单的工具栏示例。

```c
toolbar = gtk_toolbar_new();

```

`gtk_toolbar_new()`函数创建一个新的`GtkToolBar`小部件。

```c
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS)

```

`gtk_toolbar_set_style()`函数将工具栏的视图更改为仅显示图标，仅显示文本或同时显示两者。 传递`GTK_TOOLBAR_ICONS`常量可使工具栏仅显示图标。

```c
newTb = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);

```

`gtk_tool_button_new_from_stock()`函数创建一个新的`GtkToolButton`，其中包含来自库存项目的图像和文本。

```c
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), new, -1);

```

`gtk_toolbar_insert()`函数将`GtkToolItem`插入工具栏的指定位置。 如果位置为负，则该项目将附加到工具栏的末尾。

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

```

`gtk_separator_tool_item_new()`函数创建一个新的`GtkSeparatorToolItem`。 它通过`gtk_toolbar_insert()`函数插入到工具栏中。

![Toolbar](img/0c04d4e2419082d3287dbc185f7f1b4c.jpg)

图：工具栏

## 撤销重做

下面的示例演示如何禁用工具栏上的工具栏按钮。 这是 GUI 编程中的常见做法。 例如，保存按钮； 如果我们将文档的所有更改都保存到磁盘上，则在大多数文本编辑器中，“保存”按钮均处于禁用状态。 这样，应用会向用户指示所有更改都已保存。

`undoredo.c`

```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;
}

```

我们的示例从 GTK+ 库存资源创建撤消和重做按钮。 单击几下后，每个按钮均被禁用。 按钮显示为灰色。

```c
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);
}

```

`gtk_widget_set_sensitive()`函数用于激活或禁用工具栏按钮。

![Undo redo](img/35593226507ebc62a95e3b271da58b0a.jpg)

图：撤销和重做

在本章中，我们介绍了 GTK+ 中的菜单和工具栏。