第 2 章 GTK+ で画像ビューワを作ってみよう 7
2.6 メニューバーの追加
画像ビューワ作成の最後のステップとして,メニューバーを追加します.メニューには,ダイアログから画像ファイルを指定
する“Open”と,アプリケーションを終了する“Quit”の2つを表示します.
メニューバーに関する拡張は,本節と次節で実装します.まず本節では,以下の項目について扱います.
• メニューバーの追加
• メニューアイテム“Quit”でのアプリケーションの終了
この拡張にともなって,アプリケーションを終了する“Quit”ボタンを配置する必要がなくなります.
そして次節では,メニューアイテム“Open”に関して以下の項目を扱い,画像ビューワを完成させます.
• ファイル選択ダイアログの実装
• ダイアログで選択した画像ファイルの表示
2.6.1 メニュー作成の手順
ここでは,メニューを作成するためにGtkUIManagerを使用します.GtkUIManagerは,これまで扱ってきたような GUIを構成するウィジェットではなく,メニュー情報を扱う枠組みだと思ってください.GtkUIManagerを利用してメニュー を作成する手順は,次の通りです.
1. メニュー構成の作成 2. メニューアイテムの詳細設定
3. メニュー構成とメニューアイテム情報の登録 4. メニューバーの取得
2.6.2 メニュー構成の作成
メニュー構成は,基本的にはXML形式で指定します.XMLを外部のテキストファイルに記述するか,その内容をソース コード中に文字列として保持するか,いずれかの方法で作成します.今回はメニュー構成を文字列として作成し,ソースコード 中に埋め込むことにします.今回作成するメニューの構成は,次のような文字列で表されます.
static const gchar *menu_info =
"<ui>"
" <menubar name=’Menubar’>"
" <menu name=’File’>"
" <menuitem name=’Open’/>"
" <separator/>"
" <menuitem name=’Quit’/>"
" </menu>"
" </menubar>"
"</ui>";
メニューの構成は,<ui></ui>タグの中に記述します.さらに今回はメニューバーを作成するので,<menubar></menubar>
タグ内に<menu></menu>タグや<menuitem></menuitem>タグを使用して,メニュー構成を記述していきます.
ここでは,メニューバーの中にFileメニューがあり,そのメニューアイテムとしてOpenとQuitが存在する構成となってい ます.また,このメニューでは,OpenとQuitの間に仕切り(セパレータ)を配置しています.
ここで,File,Open,Quitは実際にメニューとして表示されるラベルではなく,そのメニューおよびメニューアイテムを識 別するIDだと思ってください.表示されるラベルやそのメニューアイテムに対するショートカットキー,コールバック関数等 の設定については,次の項で説明します.
2.6.3 メニューアイテムの詳細設定
メニューアイテムの詳細設定は,GtkActionEntryで行います.主に,以下の6項目を設定します.
1. メニューもしくはメニューアイテムのID 2. メニューアイコン
3. メニューラベル 4. ショートカットキー 5. メニューアイテムの説明 6. コールバック関数
前項で定義したメニューおよびメニューアイテムの場合は,次のように詳細を設定します.
static GtkActionEntry entries[] = { {"File", NULL, "_File"},
{"Open", GTK_STOCK_OPEN, "_Open", "<control>O", "Open an image", G_CALLBACK (cb_open)},
{"Quit", GTK_STOCK_QUIT, "_Quit", "<control>Q", "Quit this program", G_CALLBACK (cb_quit)}
};
メニューアイテムの2番目の項目で指定しているGTK STOCK OPENやGTK STOCK QUITは,GTK+にあらかじめ 用意されているアイコンを表す文字列で,ストックアイテムと呼ばれます.ストックアイテムを利用することで,メニューアイ テムにアイコンを表示したり,ボタン上にアイコンを表示することが,簡単に実現できるようになっています.
それぞれの項目を設定する詳細は,ここでは省略します.詳しく知りたい場合は,第7章の7.4.3(p.160)を参照してくだ さい.
2.6.4 メニュー情報の登録
メニュー構成等が定義できたら,以下に示す手順でGtkUIManagerにそれらの情報を登録します.
1. UIマネージャの作成
2. アクショングループの作成
3. アクショングループにメニューアイテムを追加
4. UIマネージャにアクショングループを追加
5. UIマネージャにメニュー構成を追加
まず,次のようにしてUIマネージャを作成します.
GtkUIManager *ui;
ui = gtk_ui_manager_new ();
次に,アクショングループ(GtkActionGroup)を作成して,前節で定義したメニューアイテムの詳細情報を,関数 gtk action group add actionsで追加します.
GtkActionGroup *actions;
actions = gtk_action_group_new ("filemenu");
gtk_action_group_add_actions (actions, entries,
sizeof (entries) / sizeof (entries[0]), parent);
アクショングループを作成する関数gtk action group newの引数には,UIマネージャからそのアクショングループを取得す る際のIDとなる文字列を与えます.
v o i d g t k _ a c t i o n _ g r o u p _ a d d _ a c t i o n s (G t k A c t i o n G r o u p * a c t i o n _ g r o u p , c o n s t G t k A c t i o n E n t r y * e n t r i e s ,
g u i n t n _ e n t r i e s ,
g p o i n t e r u s e r _ d a t a ) ;
第1引数 アクショングループ 第2引数 メニューアイテムの定義情報 第3引数 メニューアイテム数
第4引数 コールバック関数に渡すデータ
アクショングループにメニューアイテム情報を登録したら,今度はそのアクショングループをUIマネージャに登録します.
gtk_ui_manager_insert_action_group (ui, actions, 0);
関数gtk ui manager insert action groupは,UIマネージャにアクショングループを登録する関数です.第3引数にはその アクショングループを登録する位置を指定しますが,0(先頭位置)を指定しておけばよいでしょう.
v o i d g t k _ u i _ m a n a g e r _ i n s e r t _ a c t i o n _ g r o u p (G t k U I M a n a g e r * s e l f ,
G t k A c t i o n G r o u p * a c t i o n _ g r o u p ,
g i n t p o s ) ;
第1引数 UIマネージャ
第2引数 登録するアクショングループ 第3引数 登録位置
最後に,メニュー構成情報をUIマネージャに登録します.
gtk_ui_manager_add_ui_from_string (ui, menu_info, -1, NULL);
関数gtk ui manager add ui from stringは,文字列からメニュー構成情報を登録する関数です.第3引数にはメニュー構成 を示す文字列の長さを指定しますが,文字列全体を使用する場合には−1を指定します.
g u i n t g t k _ u i _ m a n a g e r _ a d d _ u i _ f r o m _ s t r i n g (G t k U I M a n a g e r * s e l f , c o n s t g c h a r * b u f f e r , g s s i z e l e n g t h , G E r r o r * * e r r o r ) ; 第1引数 UIマネージャ
第2引数 メニュー構成文字列 第3引数 メニュー構成文字列の長さ
第4引数 エラー情報を格納する変数へのポインタ 戻り値 追加したUIのID
2.6.5 ショートカットキーの関連付け
GtkActionEntryでメニューアイテムの詳細を定義したとき,そのメニューアイテムに対するショートカットキーも定義して
いました.しかしそれだけでは,ウィンドウ上でそのショートカットキーを押しても反応してくれません.ウィンドウ上でメ ニューアイテムのショートカットキーを有効にするために,関数gtk window add accel groupで設定します.
gtk_window_add_accel_group (GTK_WINDOW (parent),
gtk_ui_manager_get_accel_group (ui));
この関数の第2引数にはGtkAccelGroup 型のショートカットキー情報を指定する必要がありますが,これを関数 gtk ui manager get accel groupを使用して取得しています.
v o i d g t k _ w i n d o w _ a d d _ a c c e l _ g r o u p (G t k W i n d o w * w i n d o w , G t k A c c e l G r o u p * a c c e l _ g r o u p ) ; 第1引数 ウィンドウウィジェット
第2引数 ショートカットキー情報
2.6.6 メニューバーウィジェットの取得
すべてのメニュー情報をUIマネージャに登録したら,関数gtk ui manager get widgetを使用して,メニューバーウィ ジェットを取得します.
ウィジェットを取得する際に指定するパスは,メニューを構成する際に指定したnameに対応する文字列を‘/’で継ぎ合わせ た形で指定します.メニューバーは今回作成したメニューの最上位に位置するので,“/Menubar”と先頭に‘/’を付けて指定し ていることに注意してください.
G t k W i d g e t* g t k _ u i _ m a n a g e r _ g e t _ w i d g e t (G t k U I M a n a g e r * s e l f , c o n s t g c h a r * p a t h ) ; 第1引数 UIマネージャ
第2引数 メニューアイテムパス 戻り値 取得したウィジェット
2.6.7 ウィジェットを取得するテクニック
アプリケーションを作成していると,コールバック関数内でさまざまなウィジェットを参照したいときがあります.今回の例 では,アプリケーションを終了する際にUIマネージャのメモリ領域を解放するために,コールバック関数cb quit内でUIマ ネージャを指す変数を参照する必要があります.
コールバック関数にはデータを1つだけ渡すことができるので,参照したいウィジェットが1つだけであれば問題ありませ ん.それではコールバック関数で,複数のウィジェットを参照したい場合はどうしたらよいでしょうか.1つの解決方法として はウィジェットをグローバル変数として定義する方法がありますが,ここではそれ以外の方法として,関数g object set data と関数g object get dataを利用した方法を説明します.
以 前 に 解 説 し た よ う に ,す べ て の ウ ィ ジ ェ ッ ト は GObject か ら 派 生 し た も の で す .こ の GObject 型 の 変 数 に 関 数
g object set dataを用いることで,さまざまなデータを登録できます.今回のプログラムでは,ソースコードの110行目
で次のように利用しています.
g_object_set_data (G_OBJECT (window), "ui", (gpointer) ui);
この例では,ウィンドウに“ui”という識別子でUIマネージャを登録しています.こうすることで,ウィンドウが参照できる 場所であれば,次のように関数g object get dataを利用して,登録したウィジェットを取得できます.
GtkUIManager *ui;
ui = (GtkUIManager *) g_object_get_data (G_OBJECT (window), "ui");
2.6.8 コンパイルと動作確認
本節では,UIマネージャを利用したメニュー作成部分を,1つの関数として分離しました.ソース2–5の53–78行目がその 関数です.この関数の戻り値(UIマネージャへのポインタ)をメイン関数の108行目で受け取り,112行目でUIマネージャか らメニューバーを取得しています.
ソースコードを入力し,コンパイルしたら,いつものように実行してみましょう.
$ ./image-viewer ~/images/bird.png
図2.9のようにメニューバーが表示され,メニューやショートカットキーを利用してプログラムを終了できるようになりま した.
図2.9 メニューバーを追加した画像ビューワ
ソース2–5 メニューバーの追加: image-viewer.c
1 # i n c l u d e <g t k / g t k . h >
2 # i n c l u d e <s t d l i b . h >
3 4 / *
5 O p e nメ ニ ュ ー が 選 択 さ れ た と き に 呼 び 出 さ れ る 関 数
6 * /
7 s t a t i c v o i d
8 c b _ o p e n (G t k A c t i o n * a c t i o n , g p o i n t e r u s e r _ d a t a )
9 {
10 g _ p r i n t ( " T h i s f u n c t i o n i s n o t i m p l e m e n t e d y e t . \ n " ) ;