第4章 GtkTreeViewウィジェットと対応コード  第3章ではGtkListウィジェットとGtkComboウィジェットを使っていました。しかしこ れらのウィジェットはDeprecated(非推奨)です。GtkListウィジェットの代わりに GtkTreeViewウィジェット、GtkComboウィジェットの代わりにGtkComboBoxウィジェット を使うべきです。  しかしこれらの新しいウィジェットはWindowsとの対応がListウィジェットやGtkCombo ウィジェットに比べて単純ではありません。そこでWindowsのAPIやメッセージに対応す るGTK+のコードを示すことで解説します。 §GtkTreeViewウィジェット  WindowsでCreateWindowExでリストボックスを作成することに対応するコードです。こ の関数の引数のmultipleが真ならば複数の項目の選択が可能です。LBS_MULTIPLESELに対 応します。 ------------------------------------------------------------------------------- GtkWidget * create_window_ex (const gboolean multiple) { GtkListStore *store; GtkTreeSelection *select; GtkWidget *list; store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT); list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); g_object_unref (store); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE); gtk_tree_view_append_column (GTK_TREE_VIEW (list), gtk_tree_view_column_new_with_attributes (NULL, gtk_cell_renderer_text_new (), "text", 0, NULL)); if (multiple) { select = gtk_tree_view_get_selection (GTK_TREE_VIEW (list)); gtk_tree_selection_set_mode (select, GTK_SELECTION_MULTIPLE); } return list; } -------------------------------------------------------------------------------  LB_ADDSTRINGに対応するコードです。gtk_tree_model_iter_n_childrenで親の項目を NULLとしています。この場合、ルートの項目になります。GtkTreeViewウィジェットにお けるリストは、ツリー構造にあてはめるとすべての項目がルートにあるとみなすことが できます。(以後、リストをツリーとして扱うときにはこの概念が重要になります) ------------------------------------------------------------------------------- gint lb_addstring (GtkTreeView *list, const gchar *str) { GtkListStore *store; GtkTreeIter iter; store = GTK_LIST_STORE (gtk_tree_view_get_model (list)); gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, 0, str, 1, 0, -1); return gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL) - 1; } -------------------------------------------------------------------------------  LB_DELETESTRINGに対応するコードです。 ------------------------------------------------------------------------------- gint lb_deletestring (GtkTreeView *list, const gint index) { GtkListStore *store; GtkTreeIter iter; store = GTK_LIST_STORE (gtk_tree_view_get_model (list)); if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, NULL, index)) { gtk_list_store_remove (store, &iter); return gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL); } return -1;/* LB_ERR */ } -------------------------------------------------------------------------------  LB_FINDSTRINGに対応するコードです。gtk_tree_model_getで取得した文字列をg_free で解放しています。gtk_list_store_newでG_TYPE_STRINGを指定した場合には、新規に確 保された文字列が返されるので、解放する必要があります。  LB_FINDSTRINGEXACTおよびLB_SELECTSTRINGに対応するコードはLB_FINDSTRINGに対応 するコードを改造すれば簡単にできます。 ------------------------------------------------------------------------------- gint lb_findstring (GtkTreeView *list, const gint index, const gchar *str) { gint i, n_children; GtkTreeModel *model; model = gtk_tree_view_get_model (list); n_children = gtk_tree_model_iter_n_children (model, NULL); if (-1 <= index && index < n_children) for (i = index + 1; i != index; i = i + 1 < n_children ? i + 1 : 0) { GtkTreeIter iter; if (gtk_tree_model_iter_nth_child (model, &iter, NULL, i)) { gchar *tmp; gint result; gtk_tree_model_get (model, &iter, 0, &tmp, -1); result = g_ascii_strncasecmp (str, tmp, g_strlen (str)); g_free (tmp); if (result == 0) return i; } } return -1;/* LB_ERR */ } -------------------------------------------------------------------------------  LB_GETCURSELに対応するコードです。 ------------------------------------------------------------------------------- gint lb_getanchorindex (GtkTreeView *list) { gint i; GtkTreeIter iter; GtkTreeModel *model; GtkTreeSelection *select; model = gtk_tree_view_get_model (list); select = gtk_tree_view_get_selection (list); for (i = 0; gtk_tree_model_iter_nth_child (model, &iter, NULL, i); i++) if (gtk_tree_selection_iter_is_selected (select, &iter)) return i; return -1;/* LB_ERR */ } -------------------------------------------------------------------------------  LB_GETCARETINDEXに対応するコードです。gtk_tree_path_get_indicesが返す配列は読 み取り専用であり解放してはいけません。gtk_tree_view_get_cursorで取得したpathは gtk_tree_path_freeで解放します。 ------------------------------------------------------------------------------- gint lb_getcaretindex (GtkTreeView *list) { gint *index; GtkTreePath *path; gtk_tree_view_get_cursor (list, &path, NULL); index = gtk_tree_path_get_indices (path); gtk_tree_path_free (path); return index ? index[0] : -1; } -------------------------------------------------------------------------------  LB_GETCOUNTに対応するコードです。 ------------------------------------------------------------------------------- gint lb_getcount (GtkTreeView *list) { return gtk_tree_model_iter_n_children (gtk_tree_view_get_model (list), NULL); } -------------------------------------------------------------------------------  LB_GETITEMDATAに対応するコードです。 ------------------------------------------------------------------------------- gint lb_getitemdata (GtkTreeView *list, const gint index) { gint n = -1;/* LB_ERR */ GtkTreeIter iter; GtkTreeModel *model; model = gtk_tree_view_get_model (list); if (gtk_tree_model_iter_nth_child (model, &iter, NULL, index)) gtk_tree_model_get (model, &iter, 1, &n, -1); return n; } -------------------------------------------------------------------------------  LB_GETSELに対応するコードです。 ------------------------------------------------------------------------------- gint lb_getsel (GtkTreeView *list, const gint index) { GtkTreeIter iter; GtkTreeModel *model; GtkTreeSelection *select; model = gtk_tree_view_get_model (list); select = gtk_tree_view_get_selection (list); if (gtk_tree_model_iter_nth_child (model, &iter, NULL, index)) return gtk_tree_selection_iter_is_selected (select, &iter) ? 1 : 0; return -1;/* LB_ERR */ } -------------------------------------------------------------------------------  LB_GETSELCOUNTに対応するコードです。 ------------------------------------------------------------------------------- gint lb_getselcount (GtkTreeView *list) { gint i, count = 0; GtkTreeIter iter; GtkTreeModel *model; GtkTreeSelection *select; model = gtk_tree_view_get_model (list); select = gtk_tree_view_get_selection (list); for (i = 0; gtk_tree_model_iter_nth_child (model, &iter, NULL, i); i++) if (gtk_tree_selection_iter_is_selected (select, &iter)) count++; return count; } -------------------------------------------------------------------------------  LB_GETSELITEMSに対応するコードです。 ------------------------------------------------------------------------------- gint lb_getselitems (GtkTreeView *list gint *items) { gint i, count = 0; GtkTreeIter iter; GtkTreeModel *model; GtkTreeSelection *select; model = gtk_tree_view_get_model (list); select = gtk_tree_view_get_selection (list); for (i = 0; gtk_tree_model_iter_nth_child (model, &iter, NULL, i); i++) if (gtk_tree_selection_iter_is_selected (select, &iter)) items[count++] = i; return count; } -------------------------------------------------------------------------------  LB_GETTEXTに対応するコードです。この関数で返される文字列はg_freeで解放する必 要があります。gtk_tree_model_getで取得した文字列は解放する必要があります。  LB_GETTEXTLENに対応するコードはLB_GETTEXTに対応するコードを改造すれば簡単にで きます。 ------------------------------------------------------------------------------- gchar * lb_gettext (GtkTreeView *list, const gint index) { gchar *str = NULL; GtkTreeIter iter; GtkTreeModel *model; model = gtk_tree_view_get_model (list); if (gtk_tree_model_iter_nth_child (model, &iter, NULL, index)) gtk_tree_model_get (model, &iter, 0, &str, -1); return str; } -------------------------------------------------------------------------------  LB_INSERTSTRINGに対応するコードです。LB_INSERTSTRINGとgtk_list_store_insertで は挿入位置に関する仕様が微妙に異なります。 ------------------------------------------------------------------------------- gint lb_insertstring (GtkTreeView *list, const gchar *str, const gint index) { gint i, n_children; GtkListStore *store; GtkTreeIter iter; store = GTK_LIST_STORE (gtk_tree_view_get_model (list)); n_children = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), NULL); i = index < 0 || index < n_children ? n_children : index; gtk_list_store_insert (store, &iter, i); gtk_list_store_set (store, &iter, 0, str, 1, 0, -1); return i; } -------------------------------------------------------------------------------  LB_RESETCONTENTに対応するコードです。 ------------------------------------------------------------------------------- void lb_resetcontent (GtkTreeView *list) { gtk_list_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (list))); } -------------------------------------------------------------------------------  LB_SELITEMRANGEおよびSELITEMRANGEEXに対応するコードです。 ------------------------------------------------------------------------------- gint lb_selitemrange (GtkTreeView *list, const gboolean selected, const gint start, const gint end) { gint i; GtkTreeModel *model; GtkTreeSelection *select; model = gtk_tree_view_get_model (list); select = gtk_tree_view_get_selection (list); for (i = start; i <= end; i++) { GtkTreeIter iter; if (!gtk_tree_model_iter_nth_child (model, &iter, NULL, i)) return -1;/* LB_ERR */ if (selected) gtk_tree_selection_select_iter (select, &iter); else gtk_tree_selection_unselect_iter (select, &iter); } return 0; } -------------------------------------------------------------------------------  LB_SETCARETINDEXに対応するコードです。 ------------------------------------------------------------------------------- gint lb_setcaretindex (GtkTreeView *list, const gint index) { GtkTreeModel *model; GtkTreePath *path; model = gtk_tree_view_get_model (list); if (gtk_tree_model_iter_nth_child (model, &iter, NULL, index) && (path = gtk_tree_model_get_path (model, &iter))) { gtk_tree_view_set_cursor (list, path, NULL, FALSE); gtk_tree_path_free (path); return 0; } return -1;/* LB_ERR */ } -------------------------------------------------------------------------------  LB_SETCURSELに対応するコードです。 ------------------------------------------------------------------------------- gint lb_selcursel (GtkTreeView *list, const gint index) { GtkTreeIter iter; if (!gtk_tree_model_iter_nth_child (gtk_tree_view_get_model (list), &iter, NULL, index)) return -1;/* LB_ERR */ gtk_tree_selection_select_iter (gtk_tree_view_get_selection (list), &iter); return 0; } -------------------------------------------------------------------------------  LB_SETITEMDATAに対応するコードです。このコードを改造することで、リストの文字 列を変更する関数を作ることができます。 ------------------------------------------------------------------------------- gint lb_setitemdata (GtkTreeView *list, const gint index, const gint data) { GtkListStore *store; GtkTreeIter iter; store = GTK_LIST_STORE (gtk_tree_view_get_model (list)); if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, NULL, i)) return -1;/* LB_ERR */ gtk_list_store_set (store, &iter, 1, data, -1); return 0; } -------------------------------------------------------------------------------  LBN_SELCANCELやLBN_SELCHANGEに対応するシグナルとしてchangedがあります。これは GtkTreeViewウィジェットではなく、GtkTreeViewSelectionで発生します。次のようにし てシグナルを受け取ることができます。 ------------------------------------------------------------------------------- GtkTreeSelection *select; select = gtk_tree_view_get_selection (list); g_signal_connect (G_OBJECT (select), "changed", G_CALLBACK (signal_changed), NULL); ------------------------------------------------------------------------------- なおgtk_tree_selection_get_tree_viewを呼び出せばGtkTreeViewSelectionから TreeViewウィジェットを取得できます。 §GtkComboBoxウィジェット/GtkComboBoxEntryウィジェット  WindowsでCreateWindowExでコンボボックスを作成することに対応するコードです。こ の関数の引数のeditableが真ならば項目を編集できます。CBS_DROPDOWNに対応します。 editableが偽ならば項目は編集できません。CBS_DROPDOWNLISTに対応します。ドロップ ダウン形式ではないWindowsのコンボボックスには対応していません。  GtkComboBoxウィジェットとGtkComboBoxEntryウィジェットはGTK+-2.4以降で使うこと ができます。それ以前のバージョンに対応したいときにはComboウィジェットも使うよう にします。 ------------------------------------------------------------------------------- GtkWidget * create_window_ex (const gboolean editable) { GtkCellRenderer *renderer; GtkListStore *store; GtkWidget *combo; store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT); combo = editable ? gtk_combo_box_entry_new_with_model (GTK_TREE_MODEL (store), 0) : gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); g_object_unref (store); renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "text", 0, NULL); return combo; } -------------------------------------------------------------------------------  CB_ADDSTRINGに対応するコードです。GtkTreeViewウィジェットのLB_ADDSTRINGに対応 するコードとほぼ同じです。gtk_tree_view_get_modelの代わりに gtk_combo_box_get_modelを使っているだけです。  GtkComboBoxウィジェットのドロップダウンリストに関する操作はGtkTreeViewウィ ジェットの場合と同じです。よって次のメッセージはgtk_tree_view_get_modelを gtk_combo_box_get_modelに置き換えるだけです。 CB_DELETESTRING LB_DELETESTRING CB_FINDSTRING LB_FINDSTRING CB_FINDSTRINGEXACT LB_FINDSTRINGEXACT CB_GETCOUNT LB_GETCOUNT CB_GETITEMDATA LB_GETITEMDATA CB_GETLBTEXT LB_GETTEXT CB_GETLBTEXTLEN LB_GETTEXTLEN CB_INSERTSTRING LB_INSERTSTRING CB_RESETCONTENT LB_RESETCONTENT CB_SETITEMDATA LB_SETITEMDATA ------------------------------------------------------------------------------- gint cb_addstring (GtkComboBox *combo, const gchar *str) { GtkListStore *store; GtkTreeIter iter; store = GTK_LIST_STORE (gtk_combo_box_get_model (combo)); gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, 0, str, 1, 0, -1); return gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL) - 1; } -------------------------------------------------------------------------------  CB_GETCURSELに対応するコードです。 ------------------------------------------------------------------------------- gint cb_getcursel (GtkComboBox *combo) { return gtk_combo_box_get_active (combo); } -------------------------------------------------------------------------------  CB_GETEDITSELに対応するコードです。GtkComboBoxウィジェットの上位はGtkBinウィ ジェットであり、gtk_bin_get_childでEntryウィジェットを取得できます。この GtkEntryウィジェットに対してgtk_editable_get_selection_boundsで選択範囲を取得し ます。  GtkComboBoxウィジェットのエディトに関する操作はGtkEntryウィジェットに対して行 うことになります。 ------------------------------------------------------------------------------- void cb_geteditsel (GtkComboBox *combo, gint *start, gint *end) { gtk_editable_get_selection_bounds (GTK_EDITABLE (gtk_bin_get_child (GTK_BIN (combo))), start, end); } -------------------------------------------------------------------------------  CB_LIMITTEXTに対応するコードです。 ------------------------------------------------------------------------------- void cb_limittext (GtkComboBox *combo, const gint max) { gtk_entry_set_max_length (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo))), max); } -------------------------------------------------------------------------------  CB_SETCURSELに対応するコードです。CB_SELECTSTRINGはこのCB_SETCURSELと CB_FINDSTRINGを組み合わせれば実装できます。 ------------------------------------------------------------------------------- void cb_setcursel (GtkComboBox *combo, const gint index) { gtk_combo_box_set_active (GTK_COMBO_BOX (combo), index); } -------------------------------------------------------------------------------  CB_SETEDITSELに対応するコードです。 ------------------------------------------------------------------------------- void cb_seteditsel (GtkComboBox *combo, const gint start, const gint end) { gtk_editable_select_region (GTK_EDITABLE (gtk_bin_get_child (GTK_BIN (combo))), start, end); } -------------------------------------------------------------------------------  CBN_EDITCHANGEやCBN_SELCHANGEに対応するシグナルとしてchangedがあります。 CBN_EDITCHANGE(エディトが変更されたときのシグナル)はGtkEditableインターフェイス で発生します。CBN_SELCHANGE(ドロップダウンリストから選択が変更されたときのシグ ナル)はGtkComboBoxウィジェットで発生します。 ------------------------------------------------------------------------------- g_signal_connect (G_OBJECT (gtk_bin_get_child (GTK_BIN (combo))), "changed", G_CALLBACK (signal_changed_entry), NULL); g_signal_connect (G_OBJECT (combo), "changed", G_CALLBACK (signal_changed_list), NULL); -------------------------------------------------------------------------------  GtkEditableインターフェイスではchangedシグナルのコールバックで元のGtkComboBox ウィジェットを取得できません。必要ならばg_signal_connectの第3番目の引数で GtkComboBoxウィジェットをコールバックに渡すなどの処理が必要になります。  GtkComboBoxウィジェットとGtkComboBoxEntryウィジェットには下記のテキストを選択 するだけのコンボボックスのための簡便なAPIがあります。 gtk_combo_box_new_text gtk_combo_box_append_text gtk_combo_box_insert_text gtk_combo_box_prepend_text gtk_combo_box_remove_text gtk_combo_box_get_active_text gtk_combo_box_entry_new_text これらのAPIを使うとGtkTreeModel/GtkListStoreを意識すること無くGtkComboBoxウィ ジェットを利用できるので簡単にプログラムを組むことができます。 §Deprecatedウィジェットを置き換える  gtk0401.cは上記を踏まえてGtkTreeViewウィジェットとGtkComboBoxウィジェットを 使っています。GTK+-2.4以前も考慮しGtkComboウィジェットを使ったコードも残してあ ります。GTK_CHECK_VERSIONはバージョンを判定するマクロで、指定したバージョン以上 ならば真になります。 gtk0401.c---------------------------------------------------------------------- #include #include #include #define SPACING 8 typedef struct _MCITEM { gint price; gchar *name; } MCITEM; static gint price = 0, money = 0; static 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_changed_list (GtkTreeSelection *select, gpointer user_data) { gint i; static gint prev = 0; GtkTreeIter iter; GtkTreeModel *model; price -= prev; prev = 0; model = gtk_tree_view_get_model (gtk_tree_selection_get_tree_view (select)); for (i = 0; gtk_tree_model_iter_nth_child (model, &iter, NULL, i); i++) if (gtk_tree_selection_iter_is_selected (select, &iter)) { gint n; gtk_tree_model_get (model, &iter, 1, &n, -1); prev += n; } price += prev; set_label (); } #if GTK_CHECK_VERSION(2,4,0) /* コンボボックスの項目が変化した */ static void signal_changed_combo (GtkComboBox *combo, gpointer user_data) { GtkTreeIter iter; if (gtk_combo_box_get_active_iter (combo, &iter)) { gint n; static gint prev = 0; GtkTreeModel *model; model = gtk_combo_box_get_model (combo); gtk_tree_model_get (model, &iter, 1, &n, -1); price += n - prev; prev = n; } set_label (); } #else /* not GTK_CHECK_VERSION(2,4,0) */ /* コンボボックスの項目が選択された */ 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 (); } #endif /* not GTK_CHECK_VERSION(2,4,0) */ /* ラジオボタン、チェックボタンが変化したとき */ 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_entry (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; #if GTK_CHECK_VERSION(2,4,0) GtkCellRenderer *renderer; #endif /* GTK_CHECK_VERSION(2,4,0) */ GtkListStore *store; GtkTreeSelection *select; GtkWidget *check, *combo, *entry, *frame, *hbox; GtkWidget *list, *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); /* リストボックスを作る */ store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT); for (i = 0; burger[i].name; i++) { GtkTreeIter iter; gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, 0, burger[i].name, 1, burger[i].price, -1); } list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); g_object_unref (store); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE); gtk_tree_view_append_column (GTK_TREE_VIEW (list), gtk_tree_view_column_new_with_attributes (NULL, gtk_cell_renderer_text_new (), "text", 0, NULL)); select = gtk_tree_view_get_selection (GTK_TREE_VIEW (list)); gtk_tree_selection_set_mode (select, GTK_SELECTION_MULTIPLE); g_signal_connect (G_OBJECT (select), "changed", G_CALLBACK (signal_changed_list), NULL); /* コンボボックスを作る */ #if GTK_CHECK_VERSION(2,4,0) store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT); for (i = 0; drink[i].name; i++) { GtkTreeIter iter; gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, 0, drink[i].name, 1, drink[i].price, -1); } combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); g_object_unref (store); renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "text", 0, NULL); g_signal_connect (G_OBJECT (combo), "changed", G_CALLBACK (signal_changed_combo), NULL); gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0); #else /* not GTK_CHECK_VERSION(2,4,0) */ 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++) { GtkWidget *item; item = gtk_list_item_new_with_label (drink[i].name); g_signal_connect (G_OBJECT (item), "select", G_CALLBACK (signal_select), GINT_TO_POINTER (drink[i].price)); g_signal_connect (G_OBJECT (item), "deselect", G_CALLBACK (signal_deselect), GINT_TO_POINTER (drink[i].price)); gtk_widget_show (item); gtk_container_add (GTK_CONTAINER (GTK_COMBO (combo)->list), item); } #endif /* not GTK_CHECK_VERSION(2,4,0) */ /* ラジオボタンを作る */ 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_entry), 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; } -------------------------------------------------------------------------------