gtk_widget_add_accelerator( )関数で
メニュアイテムにアクセラレータを追加します。
ショートカットキーのことを正確にはアクセラレータと言うみたいです。
引数は次のとおりです。
第1引数にアクセラレータを追加するウィジェットを指定します
第2引数にアクセラレータと結びつけるイベントを指定します
第3引数にアクセルグループを指定します
第4引数に登録するキーを指定します。これは定数になっていますが、
多くのキーは、GDK_KEY_キー という形で登録できるみたいです
第5引数にキーと組み合わせるモディファイキー(modify key、修飾キー)
を指定します。登録できる修飾キーは次のとおりです
GDK_SHIFT_MASK シフトキー
GDK_CONTROL_MASK コントロールキー
GDK_MOD1_MASK アルト(Alt)キー
第6引数にアクセラレータの状態を指定するみたいです。状態は次のとおりです
GTK_ACCEL_VISIBLE 見える状態
GTK_ACCEL_LOCKED 取り外しできない状態
GTK_ACCEL_MASK どういう状態か分かりません
ステータスバーとチェックメニュー
statusbar.c
#include <gtk/gtk.h>
void activate();
void showmessage();
int main(int argc, char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("jp.vivacocoa.statusbar", 0 );
g_signal_connect (app, "activate",
G_CALLBACK( activate), NULL);
status = g_application_run (G_APPLICATION(app), argc, argv);
g_object_unref (app);
return status;
}
void activate(GtkApplication *app, gpointer data)
{
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *menubar;
GtkWidget *viewitem;
GtkWidget *viewmenu;
GtkWidget *showitem;
GtkWidget *statusbar;
guint statusbar_id;
/* Window */
window =
gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW(window), "StatusBar");
gtk_window_set_default_size (GTK_WINDOW(window), 300, 200);
gtk_window_set_position (GTK_WINDOW(window), 1 );
/* Container */
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0 );
gtk_container_add (GTK_CONTAINER(window), vbox);
/* Menu */
menubar = gtk_menu_bar_new ();
viewmenu
= gtk_menu_new ();
viewitem
= gtk_menu_item_new_with_label("View");
showitem
= gtk_check_menu_item_new_with_label
("Show statusbar message");
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(showitem),
TRUE);
gtk_menu_item_set_submenu (GTK_MENU_ITEM(viewitem),
viewmenu);
gtk_menu_shell_append (GTK_MENU_SHELL(menubar),
viewitem);
gtk_menu_shell_append (GTK_MENU_SHELL(viewmenu),
showitem);
/* StatusBar */
statusbar
= gtk_statusbar_new ();
statusbar_id
= gtk_statusbar_get_context_id
(GTK_STATUSBAR(statusbar), "none");
gtk_statusbar_push (GTK_STATUSBAR(statusbar),
statusbar_id,
"This is a statusbar.");
/* Pack to box */
gtk_box_pack_start (GTK_BOX(vbox), menubar, 0, 0, 0);
gtk_box_pack_end (GTK_BOX(vbox), statusbar,0, 1, 0);
/* Signal */
g_signal_connect (showitem, "activate",
G_CALLBACK(showmessage),
statusbar);
gtk_widget_show_all (window);
}
void showmessage(GtkWidget *menuitem, gpointer statusbar)
{
guint id = gtk_statusbar_get_context_id(statusbar, "none");
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)))
gtk_statusbar_push(statusbar, id, "This is a statusbar.");
else
gtk_statusbar_push(statusbar, id, "");
}
実行結果
macOSでチェックボタン、ラジオボタン、チェックメニューを使った場合、
実行中に次のような警告が出ますが、実行には問題ないようです。
(a.out:2365): Gtk-WARNING **: 17:36:05.780: Could not load a pixbuf
from /org/gtk/libgtk/theme/Adwaita/assets/check-symbolic.svg.
This may indicate that pixbuf loaders or the mime database could not be
found.
コード説明
guint statusbar_id;
guintはGTKで再定義された unsigned int です。
ステータスバーにメッセージを記述する時に id 番号を格納するために使います。
gtk_check_menu_item_new_with_label( )関数で
チェックメニューアイテムを作ります。引数にメニューに表示される文字列を指定します。
gtk_check_menu_item_set_active( )関数で
チェックメニューアイテムにチェックをつけます。
第1引数にGTK_CHECK_MENU_ITEM( )マクロで囲んで
チェックメニューアイテムを指定します
第2引数にTRUEを指定するとチェックメニューアイテムにチェックが付きます
gtk_statusbar_new( )関数でステータスバーを作ります。引数はありません。
gtk_statusbar_get_context_id( )関数で
ステータスバーのIDを整数で取得します。引数は次のとおりです。
第1引数にGTK_STATUSBAR( )マクロで囲んでステータスバーを指定します
第2引数に任意の文字列を指定します
gtk_statusbar_push( )関数でステータスバーに文字列を表示します。
引数は次のとおりです。
第1引数にGTK_STATUSBAR( )マクロで囲んでステータスバーを指定します
第2引数に先ほど取得したIDを指定します
第3引数にステータスバーに表示する文字列を指定します
gtk_check_menu_item_get_active( )関数で
GTK_CHECK_MENU_ITEM( )マクロで囲んで指定したチェックメニューアイテムが
チェックされているかどうかを調べます。
チェックされていればTRUE、されていなければFALSEが返されます。
ポップアップメニュー
ポップアップメニューは、右クリックすると、その場所に現れるメニューです。
コンテキストメニューとも呼ばれます。
popupmenu.c
#include <gtk/gtk.h>
void activate();
void show_popup();
int main(int argc, char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("jp.vivacocoa.popupmenu", 0 );
g_signal_connect (app, "activate",
G_CALLBACK(activate), NULL);
status = g_application_run (G_APPLICATION(app), argc, argv);
g_object_unref (app);
return status;
}
void activate(GtkApplication *app, gpointer data)
{
GtkWidget *window;
GtkWidget *ebox;
GtkWidget *menu;
GtkWidget *minimize;
GtkWidget *quit;
/* Window */
window =
gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW(window), "PopupMenu");
gtk_window_set_default_size (GTK_WINDOW(window), 300, 200);
gtk_window_set_position (GTK_WINDOW(window), 1 );
/* Event box */
ebox = gtk_event_box_new ();
gtk_container_add (GTK_CONTAINER(window), ebox);
/* Menu */
menu = gtk_menu_new ();
minimize
= gtk_menu_item_new_with_label("Minimize");
quit
= gtk_menu_item_new_with_label("Quit");
gtk_menu_shell_append (GTK_MENU_SHELL(menu), minimize);
gtk_menu_shell_append (GTK_MENU_SHELL(menu), quit );
gtk_widget_show (minimize);
gtk_widget_show (quit);
/* Signals */
g_signal_connect_swapped (minimize, "activate",
G_CALLBACK(gtk_window_iconify),
window);
g_signal_connect_swapped (quit, "activate",
G_CALLBACK(gtk_widget_destroy),
window);
g_signal_connect_swapped (ebox, "button-press-event",
G_CALLBACK(show_popup), menu);
gtk_widget_show_all (window);
}
void show_popup(GtkWidget *widget, GdkEvent *event)
{
const gint RIGHT_CLICK = 3;
GdkEventButton *eventbutton = (GdkEventButton *)event;
if (eventbutton->button == RIGHT_CLICK)
gtk_menu_popup_at_pointer(GTK_MENU(widget), event);
}
実行結果
ウィンドウ内を右クリックすると、その場所にメニューが現れます。Minimize を選択すると、ウィンドウが最小化します。
コード説明
gtk_event_box_new関数でイベントボックス(GtkEventBox)を作っています。
イベントボックスは目には見えないウィジェットで
ウィンドウなどに新しいイベントを追加するために使います。
gtk_container_add(GTK_CONTAINER(window), ebox);
イベントボックスをウィンドウ一杯に配置しています。
gtk_widget_show(minimize);
minimizeとquitは個別にshowしておかなければならないみたいです。
ウィンドウに所属していないから? と考えれば良いのでしょうか。
g_signal_connect_swapped(minimize,"activate",
G_CALLBACK(gtk_window_iconify), window);
g_signal_connect関数では、イベントを発生したウィジェットが
イベントハンドラーの第1引数に渡されます。
g_signal_connect_swapped関数では、第4引数のウィジェットが
イベントハンドラーの第1引数に渡されます。
g_signal_connect_swapped(quit,"activate", G_CALLBACK(gtk_widget_destroy), window);
gtk_widget_destroy関数は第1引数に破棄(destroy)するウィジェットを取ります。
スワップ(swap、交換)しているので、windowが第1引数として渡されます。
g_signal_connect_swapped(ebox, "button-press-event",
G_CALLBACK(show_popup), menu);
g_signal_connect_swapped関数を使っていますので
show_popupイベントハンドラーの第1引数に渡されるのはイベントボックスではなく
menu ウィジェットです。
マウスのボタンが押された時のイベントは "button-press-event" です。
void show_popup(GtkWidget *widget, GdkEvent *event)
イベントボックスで発生したイベントでは、イベントハンドラーの第2引数へ、
イベントそのものが自動的に渡されます。
const gint RIGHT_CLICK = 3;
マウスの右ボタンは 3 という数値で表されています。
後で分かりやすいように定数化しています。そして続けて次の処理をします。
GdkEventButton *eventbutton = (GdkEventButton *)event;
プレスされた(押された)ボタンを eventbutton ポインターに入れます。
if (eventbutton->button == RIGHT_CLICK)
そして、ボタンが 3 (右ボタン)だったら
gtk_menu_popup_at_pointer(GTK_MENU(widget), event);
gtk_menu_popup_at_pointer関数でポップアップメニューを表示します。
第1引数にメニューを、第2引数にイベントを渡します。
ツールバー
CentOS 7、Ubuntu、Mint以外では
ツールバーのアイコンは正しく表示されません。
toolbar.c
#include <gtk/gtk.h>
#include <stdlib.h> // for atoi
#include <string.h> // for strcmp
GtkToolItem *undo;
GtkToolItem *redo;
void activate();
void undo_redo();
int main(int argc, char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("jp.vivacocoa.toolbar", 0 );
g_signal_connect (app, "activate",
G_CALLBACK(activate), NULL);
status = g_application_run (G_APPLICATION(app), argc, argv);
g_object_unref (app);
return status;
}
void activate(GtkApplication *app, gpointer data)
{
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *toolbar;
GtkWidget *undo_icon;
GtkWidget *redo_icon;
GtkWidget *quit_icon;
GtkToolItem *sep;
GtkToolItem *quit;
GtkWidget *label;
/* Window */
window =
gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW(window), "ToolBar" );
gtk_window_set_default_size (GTK_WINDOW(window), 300, 200);
gtk_window_set_position (GTK_WINDOW(window), 1 );
/* Container */
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0 );
gtk_container_add (GTK_CONTAINER(window), vbox);
/* ToolBar */
toolbar = gtk_toolbar_new ();
gtk_container_set_border_width (GTK_CONTAINER(toolbar), 0 );
undo_icon
= gtk_image_new_from_icon_name("gtk-undo",
GTK_ICON_SIZE_LARGE_TOOLBAR );
undo = gtk_tool_button_new (undo_icon, "Undo" );
gtk_widget_set_name (GTK_WIDGET(undo), "undo" );
gtk_toolbar_insert (GTK_TOOLBAR(toolbar), undo, -1 );
redo_icon
= gtk_image_new_from_icon_name("gtk-redo",
GTK_ICON_SIZE_LARGE_TOOLBAR );
redo = gtk_tool_button_new (redo_icon, "Redo" );
gtk_widget_set_name (GTK_WIDGET(redo), "redo" );
gtk_toolbar_insert (GTK_TOOLBAR(toolbar), redo, -1 );
gtk_widget_set_sensitive (GTK_WIDGET(redo), FALSE );
sep
= gtk_separator_tool_item_new ();
gtk_toolbar_insert (GTK_TOOLBAR(toolbar), sep, -1 );
quit_icon
= gtk_image_new_from_icon_name("gtk-quit",
GTK_ICON_SIZE_LARGE_TOOLBAR );
quit = gtk_tool_button_new (quit_icon, "Quit" );
gtk_toolbar_insert (GTK_TOOLBAR(toolbar), quit, -1 );
/* Label */
label = gtk_label_new ("4");
/* Pack to box */
gtk_box_pack_start (GTK_BOX(vbox), toolbar, 0, 0, 0);
gtk_box_pack_end (GTK_BOX(vbox), label , 1, 0, 0);
/* Signals */
g_signal_connect (undo, "clicked",
G_CALLBACK(undo_redo), label );
g_signal_connect (redo, "clicked",
G_CALLBACK(undo_redo), label );
g_signal_connect_swapped (quit, "clicked",
G_CALLBACK(gtk_widget_destroy),
window);
gtk_widget_show_all (window);
}
void undo_redo(GtkWidget *widget, GtkLabel *label)
{
const gchar *text = gtk_label_get_text(label);
const gchar *name = gtk_widget_get_name(widget);
guchar num = atoi(text);
if (strcmp(name, "undo") == 0)
/* g_strcmp0 という関数もあります */
{
num--;
switch(num)
{
case 0:
gtk_widget_set_sensitive(GTK_WIDGET(undo), FALSE);
break;
case 3:
gtk_widget_set_sensitive(GTK_WIDGET(redo), TRUE);
break;
}
} else {
num++;
switch(num)
{
case 1:
gtk_widget_set_sensitive(GTK_WIDGET(undo), TRUE);
break;
case 4:
gtk_widget_set_sensitive(GTK_WIDGET(redo), FALSE);
break;
}
}
gchar *str = g_strdup_printf("%d", num);
gtk_label_set_text(label, str);
g_free(str);
}
実行結果
コード説明
#include <stdlib.h> // for atoi
#include <string.h> // for strcmp
環境によっては記述しないと警告が出る場合があります。
GtkToolItem *undo;
GtkToolItem *redo;
g_signal_connect関数では
イベントハンドラーに渡せる引数は、イベントを発したウィジェット以外に
追加で一つだけです。そのため undoとredoはグローバル変数にしました。
ツールボタンはGtkToolItem型になります。
void undo_redo();
GTKでは、プロトタイプ(関数前方宣言)には、引数の明記は必要ないみたいです。
気になる方は、引数も記述してください。
toolbar = gtk_toolbar_new();
ツールバーを作っています。引数はありません。
gtk_toolbar_set_styleGTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
ツールバーのスタイルを設定するのにこの関数を使います。
ただしツールバーはデフォルトでTOOL_BAR_ICONS(アイコン表示)に設定されています。
gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
この関数でツールバーの周りのボーダー(border、枠)の幅を設定できます。
undo_icon = gtk_image_new_from_icon_name("gtk-undo",
GTK_ICON_SIZE_LARGE_TOOLBAR);
アイコンを作ります。引数は次のとおりです。
第1引数にアイコンの名前を "gtk-undo" という形で指定します
第2引数にアイコンのサイズを指定します。次のような定数があります
GTK_ICON_SIZE_INVALID 無効なサイズ
GTK_ICON_SIZE_MENU メニューに適したサイズ(16px)
GTK_ICON_SIZE_SMALL_TOOLBAR
小さなツールバーに適したサイズ(16px)
GTK_ICON_SIZE_LARGE_TOOLBAR
大きなツールバーに適したサイズ(24px)
GTK_ICON_SIZE_BUTTON
ボタンに適したサイズ(16px)
GTK_ICON_SIZE_DND
ドラッグ&ドロップに適したサイズ(32px)
GTK_ICON_SIZE_DIALOG
ダイアログに適したサイズ(48px)
redo = gtk_tool_button_new(redo_icon, "Redo");
ツールボタンを作っています。第1引数にアイコンウィジェットを、
第2引数にツールボタンに表示する文字列を指定します。
gtk_widget_set_name(GTK_WIDGET(redo), "redo");
ウィジェットには、ウィジェットを識別するための名前を付けられます。
この名前は後で使います。
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), undo, -1);
ツールバーにツールアイテムを挿入しています。引数は次のとおりです。
第1引数に挿入されるツールバーを指定します。ツールバーが一つとは限りません
第2引数に挿入するツールアイテムを指定します
第3引数に挿入する位置を指定します。挿入順序は左から 0、1、
2 になります。-1 を指定すると最後に追加されます。
gtk_widget_set_sensitive(GTK_WIDGET(redo), FALSE);
ウィジェットの使用が可能かどうかを設定しています。
第1引数に対象のウィジェットを指定します
第2引数にTRUEを指定した場合は、対象のウィジェットは使用可能になります。
FALSEを指定した場合は、対象のウィジェットは仕様不可能になります。
sep = gtk_separator_tool_item_new();
ツールバーのセパレーター(縦線)を作っています。
g_signal_connect(undo, "clicked", G_CALLBACK(undo_redo), label);
ツールボタンが押された時のイベントは "clicked" です。
const gchar *name = gtk_widget_get_name(widget);
先ほど設定したウィジェットの名前を取得しています。
guchar num = atoi(text);
atoiは C の関数です。引数の文字列を整数に変換した新たな値を作ります。
guchar は、GTKで再定義された unsigned char です。
if (strcmp(name, "undo") == 0)
strcmpも C の関数です。第1引数と第2引数の文字列を比較して、
第1引数が大きい場合は 1 を、第2引数が大きい場合は -1 を、
第1引数と第2引数が同じ場合は 0 を返します。
文字の大小は辞書の順序になります。辞書の末尾の方が大きい文字列になります。
GTKには同じ働きをする g_strcmp0 という関数もあります。
gchar *str = g_strdup_printf("%d", num);
g_strdup_printf関数は、変換指定子で変換された新しい文字列を作ります。
gtk_label_set_text(label, str);
この関数でラベルに表示される文字列を設定します。
第1引数に対象となるラベルを指定します
第2引数に設定する文字列を指定します
g_free(str);
g_strdup_printf関数で作った文字列は、g_free関数で
文字列に割り当てられたメモリーを解放しておいた方が良いです。