第3章 ウィジェットとコントロール  第2章では見た目だけはWindowsのプログラムに近づきました。第3章では各ウィジェッ トとコントロールの機能を比較し、プログラムを完成させます。 §GtkLabelウィジェット  GtkLabelウィジェットは文字列を表示するウィジェットです。WindowsのSTATICコント ロールをSS_LEFTまたはSS_CENTER、SS_RIGHTで開いたときと同じです。文字の表示位置 はGtkLabelウィジェットの上位のGtkMiscウィジェットのAPIを使います。  gtk_label_get_textで返される文字列は読み取り専用です(注意)。一般に、返される 文字列の型にconst属性が付いているときには文字列は読み取り専用になります。そうで ないときには返された文字列をg_freeで解放する必要があります。 CreateWindowEx (API) → gtk_label_new (API) GetDlgItemText、GetWindowText (API) → gtk_label_get_text (API) SetDlgItemText、SetWindowText (API) → gtk_label_set_text (API) SS_LEFT、SS_CENTER、SS_RIGHT (属性) → gtk_misc_set_alignment (API) SS_LEFTNOWORDWRAP (属性) → gtk_misc_set_alignment gtk_label_set_line_wrap (API) §GtkFrameウィジェット  GtkFrameウィジェットは枠を提供します。WindowsのBUTTONコントロールを BS_GROUPBOXで開いたときと同じです。 CreateWindowEx (API) → gtk_frame_new (API) GetDlgItemText、GetWindowText (API) → gtk_frame_get_label (API) SetDlgItemText、SetWindowText (API) → gtk_frame_set_label (API) §GtkButtonウィジェット  GtkButtonウィジェットは押すことができるボタンを提供するウィジェットです。 WindowsのBUTTONコントロールをBS_DEFPUSHBUTTONまたはBS_PUSHBUTTONとBS_TEXTで作成 したときと同じです。  GtkButtonウィジェットの上位のウィジェットはGtkBinウィジェットです。GtkBinウィ ジェットはGtkLabelウィジェットを持っています。gtk_bin_get_childで返されるウィ ジェットがGtkLabelウィジェットになります。このGtkLabelウィジェットに対して gtk_misc_set_alignmentを呼ぶことでBS_LEFTなどの属性に相当する設定が可能になりま す。 CreateWindowEx (API) → gtk_button_new gtk_button_new_with_label gtk_button_new_with_mnemonic gtk_button_new_from_stock (API) GetDlgItemText、GetWindowText (API) → gtk_button_get_label (API) SetDlgItemText、SetWindowText (API) → gtk_button_set_label (API) BN_CLICKED (メッセージ) → clicked (シグナル) BS_LEFT、BS_TOP、BS_RIGHT、BS_BOTTOM、BS_CENTER、BS_VCENTER (属性) → gtk_misc_set_alignment (API) §GtkEntryウィジェット  GtkEntryウィジェットは文字列を入力するためのウィジェットです。 Windowsでは複数行を編集するコントロールもエディトコントロールと呼ばれ、ウインド ウの属性だけで区別していましたが、GTK+では複数行を編集するウィジェットは GtkTextViewウィジェットという別のウィジェットです。  GtkEntryウィジェットを利用するにあたってはGtkEditableインターフェイスの仕様も 参照すべきです。 CreateWindowEx (API) → gtk_entry_new gtk_entry_new_with_max_length (API) GetDlgItemText、GetWindowText (API) → gtk_entry_get_text (API) SetDlgItemText、SetWindowText (API) → gtk_entry_set_text (API) EM_GETSEL (メッセージ) → gtk_editable_get_selection_bounds (API) EM_SETLIMITTEXT (メッセージ) → gtk_entry_set_max_length (API) EM_SETREADONLY (メッセージ)、ES_READONLY (属性) → gtk_editable_set_editable (API) EM_SETSEL (メッセージ) → gtk_editable_select_region (API) EN_CHANGE (メッセージ) → changed (シグナル) ES_PASSWORD (属性) → gtk_entry_set_visibility (API) §GtkCheckButtonウィジェット  GtkCheckButtonウィジェットはチェックマークとテキストを表示します。 GtkCheckButtonウィジェット専用のAPIは3つしかありませんが、上位のウィジェットの APIを使えばWindowsのチェックボックスコントロールと同等の機能があります。 CheckDlgButton (API) → gtk_toggle_button_set_active (API) CreateWindowEx (API) → gtk_check_button_new gtk_check_button_new_with_label gtk_check_button_new_with_mnemonic (API) GetDlgItemText、GetWindowText (API) → gtk_label_get_text (API) IsDlgButtonChecked (API) → gtk_toggle_button_get_active (API) SetDlgItemText、SetWindowText (API) → gtk_label_set_text (API) BS_LEFT、BS_TOP、BS_RIGHT、BS_BOTTOM、BS_CENTER、BS_VCENTER (属性) → gtk_misc_set_alignment (API) BN_CLICKED (メッセージ) → toggled (シグナル) §GtkRadioButtonウィジェット  GtkRadioButtonウィジェットはラジオボタンとテキストを表示します。 GtkRadioButtonウィジェットは単独では使われず、複数のGtkRadioButtonウィジェット と連携し、複数のGtkRadioButtonウィジェットのうち常にどれか1つだけがONになり、そ の他のGtkRadioButtonウィジェットはOFFになります。あるGtkRadioButtonウィジェット がONになると今までONになっていたGtkRadioButtonウィジェットは自動的にOFFになりま す。  WindowsではBS_AUTORADIOBUTTONを指定していれば、自動的にON/OFFの作業が行われま す。どのラジオボタンコントロールが連携するのかは、コントロールの配置によって決 まります。  GTK+では連携するGtkRadioButtonウィジェットを明示する必要があります。GSListと いう単方向線形リストを使います。初めはgtk_radio_button_new_with_labelの引数 groupにはNULLを渡します。その後、gtk_radio_button_groupで単方向線形リストを取得 し、次のgtk_radio_button_new_with_labelの引数には取得した単方向線形リストを渡し ます。gtk_radio_button_new_with_labelでラジオボタンを作るたびに単方向線形リスト を取得しなおす必要があります。  gtk_radio_button_new_with_label_from_widgetというAPIもあります。これはGSList の代わりにGtkRadioButtonウィジェットを引数にします。 ------------------------------------------------------------------------------- GSList *group; radio1 = gtk_radio_button_new_with_label (NULL, "スモール"); group = gtk_radio_button_group (GTK_RADIO_BUTTON (radio1)); radio2 = gtk_radio_button_new_with_label (group, "ミディアム"); group = gtk_radio_button_group (GTK_RADIO_BUTTON (radio2)); radio3 = gtk_radio_button_new_with_label (group, "ラージ"); ------------------------------------------------------------------------------- CreateWindowEx (API) → gtk_radio_button_new gtk_radio_button_new_from_widget gtk_radio_button_new_with_label gtk_radio_button_new_with_label_from_widget gtk_radio_button_new_with_mnemonic gtk_radio_button_new_with_mnemonic_from_widget (API) CheckDlgButton (API) → gtk_toggle_button_set_active (API) GetDlgItemText、GetWindowText (API) → gtk_label_get_text (API) IsDlgButtonChecked (API) → gtk_toggle_button_get_active (API) SetDlgItemText、SetWindowText (API) → gtk_label_set_text (API) BS_LEFT、BS_TOP、BS_RIGHT、BS_BOTTOM、BS_CENTER、BS_VCENTER (属性) → gtk_misc_set_alignment (API) BN_CLICKED (メッセージ) → toggled (シグナル) §Listウィジェット  Windowsのリストコントロールに対応するウィジェットとしてGtkListウィジェットが あります。しかしGtkListウィジェットはGTK+-1.2時代の遺産でありDeprecated(非推奨) 扱いになっています。新規に書くプログラムにGtkListウィジェットを使うべきではあり ません。GtkListウィジェットの代替としてGtkTreeViewウィジェットがあります。この ウィジェットはその名の通りツリー構造の表示を行うウィジェットですが、リスト表示 もできます。  GtkTreeViewウィジェットとWindowsのリストコントロールは違いが大きいです。 Deprecated(非推奨)扱いとはいえGtkListウィジェットを使った方が、Windowsプログラ ムの移植には適しています。ここではとりあえずGtkListウィジェットについて説明しま す。別の章で改めてGtkTreeViewウィジェットとWindowsのリストコントロールについて 説明します。  リストコントロールではリスト内の各項目はウインドウではありません。しかし GtkListウィジェットのリスト内の各項目はGtkListItemウィジェットというウィジェッ トです。  GtkListウィジェットのシグナルであるselection-changedやselect-child、 unselect-childで選択されている項目が変化したことがわかります。Windowsの LBN_SELCHANGEメッセージに対応していると言えます。select-childやunselect-childで は変化したGtkListItemウィジェットが引数として渡されます。 gtk_list_child_positionで変化した項目のインデックス(先頭を0とする数値)がわかり ます。GtkListItemウィジェットの上位のGtkItemウィジェットにはselect、deselectと いうシグナルがあります。これを使っても同じようなことが可能です。  インデックスから対応するGtkListItemウィジェットを取得するAPIはGtkListItemウィ ジェットでは提供されていませんが、上位のGtkContainerウィジェットのAPIを使えばイ ンデックスからウィジェットを取得できます。 ------------------------------------------------------------------------------- gint index; GLIST *glist; GtkWidget *list, *list_item; glist = gtk_container_children (GTK_CONTAINER (list)); list_item = g_list_nth_data (glist, index); g_list_free (glist); -------------------------------------------------------------------------------  WindowsではLB_GETITEMDATAとLB_SETITEMDATAで各項目にデータを記憶することができ ます。GtkListウィジェットでは各項目はウィジェットなのでウィジェットにデータを記 憶させるg_object_get_dataとg_object_set_dataを使うことで同じような効果が得られ ます。 ------------------------------------------------------------------------------- GtkWidget *list_item; list_item = gtk_list_item_new (); g_object_set_data (GOBJECT (list_item), "user_data", data); ------------------------------------------------------------------------------- data = g_object_get_data (GOBJECT (list_item), "user_data"); ------------------------------------------------------------------------------- CreateWindowEx (API) → gtk_list_new (API) LB_ADDSTRING (メッセージ) → gtk_container_add、gtk_list_append_items (API) LB_DELETESTRING (メッセージ) → gtk_container_remove、 gtk_list_remove_items (API) LB_GETCOUNT (メッセージ) → gtk_container_children、g_list_length (API) LB_GETITEMDATA (メッセージ) → gtk_object_get_user_data (API) LB_GETTEXT (メッセージ) → → gtk_label_get_text (API) LB_INSERTSTRING (メッセージ) → gtk_list_insert_items (API) LB_RESETCONTENT (メッセージ) → gtk_list_clear_items (API) LB_SELITEMRANGE、LB_SETCURSEL、LB_SETSEL (メッセージ) → gtk_list_select_item、gtk_list_unselect_item、 gtk_list_select_child、gtk_list_unselect_child、 gtk_list_item_select、gtk_list_item_deselect (API) LB_SETITEMDATA (メッセージ) → gtk_object_set_user_data (API) LBN_SELCANCEL (メッセージ) → selection-changed、select-child、select (シグナル) LBN_SELCHANGE (メッセージ) → selection-changed、unselect-child、deselect (シグナル) LBS_MULTIPLESEL (属性) → gtk_list_set_selection_mode (API) §GtkComboウィジェット  GtkComboウィジェットはGtkEntryウィジェットとGtkListウィジェットをあわせたよう なウィジェットです。Windowsでは仕様上はコンボボックスはエディトコントロールやリ ストコントロールからは独立しています。似てはいますが、別のメッセージが割り当て られています。  GTK+のGtkComboウィジェットはGtkEntryウィジェットとGtkListウィジェットから構成 されています。GtkComboウィジェットがGTK_COMBO (widget)->entry、 GTK_COMBO (widget)->listであることを知っていれば困ることはありません。 CreateWindowEx (API) → gtk_combo_new (API) CB_ADDSTRING、CB_INSERTSTRING (メッセージ) → gtk_combo_set_popdown_strings (API) §ウィジェットを動作させる  gtk0206.cを改造してwin0201.cと同じ働きをするgtk0301.cを作ります。 gtk0301.c---------------------------------------------------------------------- #include #include #include #define SPACING 8 typedef struct _MCITEM { gint price; gchar *name; } MCITEM; gint price = 0, money = 0; GtkWidget *button, *label; /* ラベルの設定とボタンの有効化/無効化 */ static void set_label (void) { gchar *text; text = g_strdup_printf ("-%d=%d", price, money - price); gtk_label_set_text (GTK_LABEL (label), text); g_free (text); gtk_widget_set_sensitive (button, money >= price); } /* リスト、コンボボックスの項目が選択された */ static void signal_select (GtkItem *item, gpointer user_data) { price += GPOINTER_TO_INT (user_data);/* 価格に加える */ set_label (); } /* リスト、コンボボックスの項目の選択が解除されたとき */ static void signal_deselect (GtkItem *item, gpointer user_data) { price -= GPOINTER_TO_INT (user_data);/* 価格を減らす */ set_label (); } /* ラジオボタン、チェックボタンが変化したとき */ static void signal_toggled (GtkToggleButton *togglebutton, gpointer user_data) { if (gtk_toggle_button_get_active (togglebutton)) price += GPOINTER_TO_INT (user_data);/* ONになったときには価格に加える */ else price -= GPOINTER_TO_INT (user_data);/* OFFになったときには価格を減らす */ set_label (); } /* エントリーが変更されたとき */ static void signal_changed (GtkEditable *editable, gpointer user_data) { money = atoi (gtk_entry_get_text (GTK_ENTRY (editable))); set_label (); } int main (int argc, char *argv[]) { gint i; GtkWidget *check, *combo, *entry, *frame, *hbox; GtkWidget *list, *list_item, *radio1, *radio2, *radio3, *vbox, *window; MCITEM burger[] = { { 80, "ハンバーガー"}, {120, "チーズバーガー"}, {150, "フランクバーガー"}, {190, "ベーコンレタスバーガー"}, {190, "フィレオフィッシュ"}, {190, "てりやきバーガー"}, {220, "スターバーガー"}, {220, "ダブルチーズバーガー"}, {250, "チキンタツタ"}, {250, "ビックバーガー"}, { 0, NULL}}; MCITEM drink[] = { {160, "コーラ"}, {160, "ニッキ水"}, {160, "炭酸入り麦茶"}, {160, "グレープジュース"}, {160, "緑茶"}, {160, "オレンジジュース"}, {160, "アイスコーヒー"}, {160, "アイスティー"}, {160, "牛乳"}, {180, "アイスカフェオレ"}, {180, "コーヒー"}, {180, "コーンポタージュスープ"}, {180, "ココア"}, {180, "紅茶"}, { 0, NULL}}; /* 初期化 */ gtk_set_locale (); gtk_init (&argc, &argv); /* ウインドウを作る */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL); /* ラベルを作る */ label = gtk_label_new ("Label"); /* ボタンを作る */ button = gtk_button_new_with_label ("購入"); g_signal_connect_swapped (G_OBJECT (button), "clicked", G_CALLBACK (gtk_widget_destroy), window); /* リストボックスを作る */ list = gtk_list_new (); gtk_list_set_selection_mode (GTK_LIST (list), GTK_SELECTION_MULTIPLE); for (i = 0; burger[i].name; i++) { list_item = gtk_list_item_new_with_label (burger[i].name); g_signal_connect (G_OBJECT (list_item), "select", G_CALLBACK (signal_select), GINT_TO_POINTER (burger[i].price)); g_signal_connect (G_OBJECT (list_item), "deselect", G_CALLBACK (signal_deselect), GINT_TO_POINTER (burger[i].price)); gtk_container_add (GTK_CONTAINER (list), list_item); } /* コンボボックスを作る */ combo = gtk_combo_new (); gtk_entry_set_editable (GTK_ENTRY (GTK_COMBO (combo)->entry), FALSE); gtk_list_set_selection_mode (GTK_LIST (GTK_COMBO (combo)->list), GTK_SELECTION_BROWSE); for (i = 0; drink[i].name; i++) { list_item = gtk_list_item_new_with_label (drink[i].name); g_signal_connect (G_OBJECT (list_item), "select", G_CALLBACK (signal_select), GINT_TO_POINTER (drink[i].price)); g_signal_connect (G_OBJECT (list_item), "deselect", G_CALLBACK (signal_deselect), GINT_TO_POINTER (drink[i].price)); gtk_widget_show (list_item); gtk_container_add (GTK_CONTAINER (GTK_COMBO (combo)->list), list_item); } /* ラジオボタンを作る */ radio1 = gtk_radio_button_new_with_label (NULL, "スモール"); radio2 = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio1), "ミディアム"); radio3 = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio1), "ラージ"); g_signal_connect (G_OBJECT (radio1), "toggled", G_CALLBACK (signal_toggled), GINT_TO_POINTER (150)); g_signal_connect (G_OBJECT (radio2), "toggled", G_CALLBACK (signal_toggled), GINT_TO_POINTER (240)); g_signal_connect (G_OBJECT (radio3), "toggled", G_CALLBACK (signal_toggled), GINT_TO_POINTER (290)); price += 150; /* チェックボタンを作る */ check = gtk_check_button_new_with_label ("ナゲット"); g_signal_connect (G_OBJECT (check), "toggled", G_CALLBACK (signal_toggled), GINT_TO_POINTER (190)); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), TRUE); /* エントリーを作る */ entry = gtk_entry_new (); gtk_entry_set_text (GTK_ENTRY (entry), "0"); g_signal_connect (G_OBJECT (entry), "changed", G_CALLBACK (signal_changed), NULL); /***** 配置 *****/ /* 水平ボックス */ hbox = gtk_hbox_new (FALSE, SPACING); /* 水平ボックスにラジオボタンを入れる */ gtk_box_pack_start (GTK_BOX (hbox), radio1, TRUE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), radio2, TRUE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), radio3, TRUE, FALSE, 0); /* 垂直ボックスと水平ボックス */ vbox = gtk_vbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, SPACING); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, SPACING); /* フレームを作る */ frame = gtk_frame_new ("ポテト"); /* フレームに水平ボックスを入れる */ gtk_container_add (GTK_CONTAINER (frame), hbox); /* 水平ボックス */ hbox = gtk_hbox_new (FALSE, 0); /* 水平ボックスに右下の3つのウィジェットを入れる */ gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); /* 垂直ボックス */ vbox = gtk_vbox_new (FALSE, SPACING); /* コンボボックス、 フレーム(ラジオボタンが3つ入っている)、チェックボックス、 水平ボックス(右下の3つのウィジェットが入っている)を入れる */ gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0); gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); /* 水平ボックス */ hbox = gtk_hbox_new (FALSE, SPACING); /* 水平ボックスにリストと 垂直ボックス(右側のウィジェットが入っている)を入れる */ gtk_box_pack_start (GTK_BOX (hbox), list, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); /* 垂直ボックスと水平ボックス */ vbox = gtk_vbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, SPACING); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, SPACING); /* ウインドウに水平ボックスを入れる */ gtk_container_add (GTK_CONTAINER (window), hbox); /* ウインドウを表示する */ gtk_widget_show_all (window); /* イベントループ */ gtk_main (); return 0; } -------------------------------------------------------------------------------  まず、GtkLabelウィジェットとGtkButtonウィジェットを先に作ります。この後、 GtkListウィジェットやGtkComboウィジェット、GtkCheckButtonウィジェット、 GtkRadioButtonウィジェットの内容が変更されるとき、シグナルが発生します。そのと きGtklabelウィジェットのテキストを書き換え、GtkButtonウィジェットの有効化/無効 化を行うので、先にGtkLabelウィジェットとGtkButtonウィジェットが必要になります。  GtkButtonウィジェットのclickedシグナルではgtk_widget_destroyを呼び出します。 このAPIはWindowsのDestroyWindowsに対応しています。GtkButtonウィジェットの有効化 /無効化はgtk_widget_set_sensitiveで行います。このAPIはWindowsのEnableWindowに対 応しています。  GtkListウィジェットでは各項目、GtkListItemウィジェットがselectシグナルと deselectシグナルに応答します。このシグナルはGtkListItemウィジェットが選択された ときと選択が解除されたときに発生します。選択されたときには合計の金額を加え、選 択が解除されたときには合計の金額を減らします。GtkListItemウィジェットに対応する 金額はg_signal_connectを呼び出すときに引数のuser_dataに格納しています。これで実 際にシグナルが発生したときには関数の引数にこの値が格納されます。 g_object_get_dataとg_object_set_dataを使って価格を記憶することも可能です。 GtkComboウィジェットでも同様のことを行っています。  GtkComboウィジェットで各GtkListItemウィジェットに対してgtk_widget_showを呼び 出しています。これを行わないとGtkListItemウィジェットが表示されません。最後の gtk_widget_show_allではGtkComboウィジェットの中の各GtkListItemウィジェットが表 示されません。  GtkRadioButtonウィジェットでは始めから最初のウィジェットのボタンが有効になっ ています。そのため変数priceに最初のGtkRadioButtonウィジェットに対応する価格であ る150を加えています。  GtkRadioButtonウィジェットとGtkCheckButtonウィジェットではtoggledシグナルに応 答しています。toggledシグナルが発生したらウィジェットの状態を取得し、ウィジェッ トがONになったためtoggledシグナルが発生したならば合計の金額を加えます。反対に ウィジェットがOFFになったためtoggledシグナルが発生したならば合計の金額を減らし ます。 §ウィジェットの動作を改良する  gtk0301.cを使ってみるとWindowsと違うところがあります。Windowsでは初めからエ ディトコントロールにフォーカスが設定されていました。これはダイアログの順番によ ります。またエディトコントロールの内容は選択されています。さらにエディトコント ロールでReturnキーを押すとボタンを押したときと同じ動作になります。  ダイアログ全体ではWindowsではEscapeキーを押すとウインドウの右上の閉じるボタン を押したときと同じ動作になります。  gtk0302.cではこれらを実装しよりwin0201.cに近づけます。 gtk0302.c---------------------------------------------------------------------- #include #include #include #define SPACING 8 typedef struct _MCITEM { gint price; gchar *name; } MCITEM; gint price = 0, money = 0; GtkWidget *button, *label; /* ラベルの設定とボタンの有効化/無効化 */ static void set_label (void) { gchar *text; text = g_strdup_printf ("-%d=%d", price, money - price); gtk_label_set_text (GTK_LABEL (label), text); g_free (text); gtk_widget_set_sensitive (button, money >= price); } /* リスト、コンボボックスの項目が選択された */ static void signal_select (GtkItem *item, gpointer user_data) { price += GPOINTER_TO_INT (user_data);/* 価格に加える */ set_label (); } /* リスト、コンボボックスの項目の選択が解除されたとき */ static void signal_deselect (GtkItem *item, gpointer user_data) { price -= GPOINTER_TO_INT (user_data);/* 価格を減らす */ set_label (); } /* ラジオボタン、チェックボタンが変化したとき */ static void signal_toggled (GtkToggleButton *togglebutton, gpointer user_data) { if (gtk_toggle_button_get_active (togglebutton)) price += GPOINTER_TO_INT (user_data);/* ONになったときには価格に加える */ else price -= GPOINTER_TO_INT (user_data);/* OFFになったときには価格を減らす */ set_label (); } /* エントリーが変更されたとき */ static void signal_changed (GtkEditable *editable, gpointer user_data) { money = atoi (gtk_entry_get_text (GTK_ENTRY (editable))); set_label (); } /* キーが押されたとき */ static gboolean signal_key_press (GtkWidget *widget, GdkEventKey *event, gpointer user_data) { if (event->keyval == GDK_Escape) gtk_widget_destroy (widget); return FALSE; } int main (int argc, char *argv[]) { gint i; GtkWidget *check, *combo, *entry, *frame, *hbox; GtkWidget *list, *list_item, *radio1, *radio2, *radio3, *vbox, *window; MCITEM burger[] = { { 80, "ハンバーガー"}, {120, "チーズバーガー"}, {150, "フランクバーガー"}, {190, "ベーコンレタスバーガー"}, {190, "フィレオフィッシュ"}, {190, "てりやきバーガー"}, {220, "スターバーガー"}, {220, "ダブルチーズバーガー"}, {250, "チキンタツタ"}, {250, "ビックバーガー"}, { 0, NULL}}; MCITEM drink[] = { {160, "コーラ"}, {160, "ニッキ水"}, {160, "炭酸入り麦茶"}, {160, "グレープジュース"}, {160, "緑茶"}, {160, "オレンジジュース"}, {160, "アイスコーヒー"}, {160, "アイスティー"}, {160, "牛乳"}, {180, "アイスカフェオレ"}, {180, "コーヒー"}, {180, "コーンポタージュスープ"}, {180, "ココア"}, {180, "紅茶"}, { 0, NULL}}; /* 初期化 */ gtk_set_locale (); gtk_init (&argc, &argv); /* ウインドウを作る */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_signal_connect_after (G_OBJECT (window), "key-press-event", G_CALLBACK (signal_key_press), NULL); g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL); /* ラベルを作る */ label = gtk_label_new ("Label"); /* ボタンを作る */ button = gtk_button_new_with_label ("購入"); g_signal_connect_swapped (G_OBJECT (button), "clicked", G_CALLBACK (gtk_widget_destroy), window); /* リストボックスを作る */ list = gtk_list_new (); gtk_list_set_selection_mode (GTK_LIST (list), GTK_SELECTION_MULTIPLE); for (i = 0; burger[i].name; i++) { list_item = gtk_list_item_new_with_label (burger[i].name); g_signal_connect (G_OBJECT (list_item), "select", G_CALLBACK (signal_select), GINT_TO_POINTER (burger[i].price)); g_signal_connect (G_OBJECT (list_item), "deselect", G_CALLBACK (signal_deselect), GINT_TO_POINTER (burger[i].price)); gtk_container_add (GTK_CONTAINER (list), list_item); } /* コンボボックスを作る */ combo = gtk_combo_new (); gtk_entry_set_editable (GTK_ENTRY (GTK_COMBO (combo)->entry), FALSE); gtk_list_set_selection_mode (GTK_LIST (GTK_COMBO (combo)->list), GTK_SELECTION_BROWSE); for (i = 0; drink[i].name; i++) { list_item = gtk_list_item_new_with_label (drink[i].name); g_signal_connect (G_OBJECT (list_item), "select", G_CALLBACK (signal_select), GINT_TO_POINTER (drink[i].price)); g_signal_connect (G_OBJECT (list_item), "deselect", G_CALLBACK (signal_deselect), GINT_TO_POINTER (drink[i].price)); gtk_widget_show (list_item); gtk_container_add (GTK_CONTAINER (GTK_COMBO (combo)->list), list_item); } /* ラジオボタンを作る */ radio1 = gtk_radio_button_new_with_label (NULL, "スモール"); radio2 = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio1), "ミディアム"); radio3 = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio1), "ラージ"); g_signal_connect (G_OBJECT (radio1), "toggled", G_CALLBACK (signal_toggled), GINT_TO_POINTER (150)); g_signal_connect (G_OBJECT (radio2), "toggled", G_CALLBACK (signal_toggled), GINT_TO_POINTER (240)); g_signal_connect (G_OBJECT (radio3), "toggled", G_CALLBACK (signal_toggled), GINT_TO_POINTER (290)); price += 150; /* チェックボタンを作る */ check = gtk_check_button_new_with_label ("ナゲット"); g_signal_connect (G_OBJECT (check), "toggled", G_CALLBACK (signal_toggled), GINT_TO_POINTER (190)); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), TRUE); /* エントリーを作る */ entry = gtk_entry_new (); gtk_entry_set_text (GTK_ENTRY (entry), "0"); g_signal_connect (G_OBJECT (entry), "changed", G_CALLBACK (signal_changed), NULL); /* Returnキーが押されたときにはボタンが押されたときと同じ動作をする */ g_signal_connect_swapped (G_OBJECT (entry), "activate", G_CALLBACK (gtk_widget_destroy), window); /* エントリーの内容をすべてを選択する */ gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1); /***** 配置 *****/ /* 水平ボックス */ hbox = gtk_hbox_new (FALSE, SPACING); /* 水平ボックスにラジオボタンを入れる */ gtk_box_pack_start (GTK_BOX (hbox), radio1, TRUE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), radio2, TRUE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), radio3, TRUE, FALSE, 0); /* 垂直ボックスと水平ボックス */ vbox = gtk_vbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, SPACING); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, SPACING); /* フレームを作る */ frame = gtk_frame_new ("ポテト"); /* フレームに水平ボックスを入れる */ gtk_container_add (GTK_CONTAINER (frame), hbox); /* 水平ボックス */ hbox = gtk_hbox_new (FALSE, 0); /* 水平ボックスに右下の3つのウィジェットを入れる */ gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); /* 垂直ボックス */ vbox = gtk_vbox_new (FALSE, SPACING); /* コンボボックス、 フレーム(ラジオボタンが3つ入っている)、チェックボックス、 水平ボックス(右下の3つのウィジェットが入っている)を入れる */ gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0); gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); /* 水平ボックス */ hbox = gtk_hbox_new (FALSE, SPACING); /* 水平ボックスにリストと 垂直ボックス(右側のウィジェットが入っている)を入れる */ gtk_box_pack_start (GTK_BOX (hbox), list, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); /* 垂直ボックスと水平ボックス */ vbox = gtk_vbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, SPACING); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, SPACING); /* ウインドウに水平ボックスを入れる */ gtk_container_add (GTK_CONTAINER (window), hbox); /* ウインドウを表示する */ gtk_widget_show_all (window); /* エントリーにフォーカスをあわせる */ gtk_widget_grab_focus (entry); /* イベントループ */ gtk_main (); return 0; } -------------------------------------------------------------------------------  gtk_editable_select_regionでGtkEntryウィジェットの内容をすべて選択します。そ してgtk_widget_grab_focusでGtkEntryウィジェットにフォーカスをあわせます。 gtk_widget_grab_focusはWindowsのSetFocusに対応しています。  GtkEntryウィジェットでReturnキーが押されたときにはactivateシグナルが発生しま す。これにGtkButtonウィジェットが押されたときと同じように応答します。  GTK+ではEscapeキーが押されたときに発生するシグナルはありません。そのため一般 的なキー入力のイベントに応答します。Escapeキーが押されたときにはウインドウを閉 じます。(キー入力については後で解説します)