ファイル選択ダイアログを追加する
[1] 主筆嬢 06/02/11 17:42
何となく目次
今回はファイル選択ダイアログに関する話。
[2] 主筆嬢 06/02/11 17:45
普通、ユーザにファイルを選択させるためには、ファイル選択ダイアログを使う。でもって、大概はその手のダイアログはシステム側で標準の機能を提供している。
Windowsであれば、こういったダイアログが使われる。
もちろん、Motifでも同等の機能を提供していて、Sun Studio 11のX-Designerでも使うことができるようになってる。
[3] 主筆嬢 06/02/11 17:47
基本的なところは普通のダイアログと同じだから、ダイアログを追加するとダイアログをモーダルにするで言ったことをそのままやればいいだけ。
でも、それ以外にファイル選択ダイアログに依存する細かい話があるから、ここで別枠で解説しておく。
[4] nabiki_t 06/02/11 17:48
前口上は良いから、早く始めろよ。
[5] 主筆嬢 06/02/11 17:50
うるさい。暇人は黙ってろ。
[6] 主筆嬢 06/02/11 17:52
まずは、例のごとくSun Studio 11を起動する。
でもってダイアログを追加するの手順に従って、TestPrj2にファイル選択ダイアログを追加する。
デザインの画面はこんな感じ。

[7] 主筆嬢 06/02/11 17:55
シェルウィジットには"FileNameShell"、ダイアログ構造のウィジット( <-これ)には"FileNameDlg"という名前を付けておいた。

クラス名は"CFileNameShell"、インスタンス名は"CTP2FileNameShell"としておいた。
[8] 主筆嬢 06/02/11 17:56
それと、今回は余り関係ないけど、一応ファイル選択ダイアログはモーダルダイアログにしておいた。
余談だけど、ダイアログをモーダルにするためのコードは、ダイアログの内容に依らずほとんどいつも同じだから、クラス化するかどうにかして、共通に使えるようにしておいた方が良いと思う。
今回は簡潔さのために「CTP2AdditionShell」クラスのコードをほとんどそのままコピー&ペーストして使ったけどね。
[9] 主筆嬢 06/02/11 18:59
最後に、「CTP2Shell::OnOK」を書き換えて、了解ボタンが押されたときに今回追加したファイル選択ダイアログが表示されるようにする。
// OKボタンが押下されると呼び出される
void CTP2Shell::OnOK( Widget w, XtPointer d )
{
// ファイル選択ダイアログを表示させる
FileNameShell->Show();
}
|
とりあえず、ここで一旦動かしてみる。

ソースコードはこれ
特に書いてないけど、MakefileやTestPrj2_gol.hにも修正が入ってるから注意してね。
[10] 広告 06/02/11 18:00
[11] 主筆嬢 06/02/11 18:02
ここからが本題。
ファイル選択ダイアログを表示したら、ユーザが入力したファイル名を取得しなきゃいけない。
[12] 主筆嬢 06/02/11 18:04
そのためには「ファイル名が入力されるウィジット」からXmTextGetStringで入力された文字列を取得してあげればいい。
そう言うことで、まずは「ファイル名が入力されるウィジット」に名前を付けてあげる。

ウィジット名のところに「(Text)」と書いてある奴にファイル名が入力されるから、そいつの変数名を設定する。ここでは一応"FileNameText"としておいた。
[12] 主筆嬢 06/02/11 18:05
そして、CTP2FileNameShell::OnOKにファイル名を取得するためのコードを記述する。
さっきも言ったとおり、FileNameTextにXmTextGetStringをしてあげれば良いだけだから、こうなる。
// 了解ボタンが押下されたときに呼ばれる
void CTP2FileNameShell::OnOK( Widget w, XtPointer d )
{
char *pFileName = NULL;
CloseFlg = true;
// 選択されたファイル名を取得する
pFileName = XmTextGetString( FileNameText );
printf( "File Name = %s\n", pFileName );
XtFree( pFileName );
}
|
[13] 主筆嬢 06/02/11 18:06
この状態で実行してみる。

まずは「ほげほげ」と書かれたウインドウの「了解」ボタンを押して、ファイル選択ダイアログを表示させる。
そして、適当なファイルを選択して「了解」ボタンを押下する。

そうすると、めでたく端末エミュレータにファイル名が表示される。
[14] 主筆嬢 06/02/11 18:08
本当ならこれで、”はい、めでたしめでたし”、で終わるところなんだけど・・・ そうもいかない理由がある。
実は、時と場合によってはファイル名がフルパスで取得できないことがある。
[15] 主筆嬢 06/02/11 18:09
>>13の画面をよく見てもらえば判るけど、ファイル名を入力するテキストボックスには、ファイル名がフルパスで入力されている。そして、取得されるファイル名もフルパスになっている。
じゃ、もしここでユーザがパスを消して、ファイル名だけにしたらどうなるか。

上記の状態で「了解」ボタンを押すと、取得される値は"hosts"になる。
ユーザとしては"/etc/hosts"を指定したつもりなのかも知れないけど、これでは(プロセスの)カレントディレクトリにあるhostsファイルを開くことになっちゃう。
もっとも、この場合はわざわざパスを消したユーザが悪い、と言うこともできるんだけど。
[16] 主筆嬢 06/02/11 18:11
それと、もっと困るのはファイル選択ダイアログのリソースで、「pathMode」の値に「XmPATH_MODE_RELATIVE」を指定された場合。
実はファイル選択ダイアログは、リソースを修正することで見た目や動作を変更することができる。その最たる物が「pathMode」で、これを指定すると見た目がちょっと変わる。

>>13や>>15の様な状態(pathModeにXmPATH_MODE_FULLを指定した状態)では、「選択」の値はデフォルトではフルパスで入力されるようになっている。だから、ユーザが意図的に書き換えない限りは相対パスで入力されることはない。
でも上図のタイプにすると、「選択」のところは相対パスで入力されるようになっちゃう。
どちらかといえばこっちの方が使いやすいし上に、ユーザにリソースの値を強要するわけにもいかない。だから、相対パスで入力される問題は、一方的にユーザの責任に帰するわけにはいかない。
[17] nabiki_t 06/02/11 18:14
画面に表示されているディレクトリ名が取得できればいいのさ。
>>15の画面であれば"/etc"が、>>16であれば"/home/nabiki"が得られれば、全ての問題は解決する。
[18] 主筆嬢 06/02/11 18:15
人の台詞を取るな。
[19] 主筆嬢 06/02/11 18:17
まぁとにかく、ユーザが選択したディレクトリ名が取得できればいいんだから、ファイル名と同じ要領で、ディレクトリ名が入力されるテキストボックスに対してXmTextGetStringをやってみる。

デザイン画面を見れば、都合よく「(DirText)」という名前のウィジットもあるから、そいつに"DirNameText"という変数名を指定して、下記のコードでディレクトリ名を取得してみる。
// 了解ボタンが押下されたときに呼ばれる
void CTP2FileNameShell::OnOK( Widget w, XtPointer d )
{
char *pFileName = NULL;
char *pDirName = NULL;
CloseFlg = true;
// ファイル名を取得する
pFileName = XmTextGetString( FileNameText );
// ディレクトリ名を取得する
pDirName = XmTextGetString( DirNameText );
printf( "File Name = %s\n", pFileName );
printf( "Directory Name = %s\n", pDirName );
XtFree( pFileName );
XtFree( pDirName );
}
|
[20] 主筆嬢 06/02/11 18:19
もっともらしく紹介しておいて言うのは心苦しいけど、実は上のコードは動作しない。DirNameTextがNULLになってて、XmTextGetStringでプロセスが落ちる。
>>15・>>16の画面と>>19の画面をよく見比べてもらえば判ると思うけど、デザイン時(>>19)に表示されてる「ファイル名またはフォルダ名を入力してください:」と書かれたところにあるテキストボックスは、実行時には表示されない。
pathModeをXmPATH_MODE_FULLにしたとき(>>15)には、そのテキストボックスはすっぽりと抜け落ちてる。XmPATH_MODE_RELATIVEにしたとき(>>16)には、そこにはコンボボックスが表示されてる。
一体何の罠なのかは知らないけど。
[21] 広告 06/02/11 18:19
[22] 主筆嬢 06/02/11 18:23
じゃ、どうしようもないのかと言うと、そうでもない。一応、やり方はある。
<Xm/FileSBP.h>という、どう見てもプライベートなヘッダファイルの中にFS_Directoryというマクロが定義されてる。これを使えばディレクトリ名をコンパウンドストリングで取得することができる。
だからこれを使って、こういう風にコードを記述する。
// 了解ボタンが押下されたときに呼ばれる
void CTP2FileNameShell::OnOK( Widget w, XtPointer d )
{
char *pFileName = NULL;
char *pDirName = NULL;
CloseFlg = true;
// ファイル名を取得する
pFileName = XmTextGetString( FileNameText );
// ディレクトリ名を取得する
XmStringGetLtoR(
FS_Directory( FileNameDlg ),
XmFONTLIST_DEFAULT_TAG,
&pDirName
);
printf( "File Name = %s\n", pFileName );
printf( "Directory Name = %s\n", pDirName );
XtFree( pFileName );
XtFree( pDirName );
}
|
[23] 主筆嬢 06/02/11 18:24
FS_Directoryというマクロを使うと、ダイアログ構造のウィジット"FileNameDlg"が保持している、コンパウンドストリングのディレクトリ名を参照することができる。
だから、そこからディレクトリ名をchar*の形式で取得してあげればいい。
[24] 主筆嬢 06/02/11 18:26
これでコンパイルして動作させてみると、こうなる。

ファイル選択ダイアログを表示させて、ファイルを選択して、「了解」ボタンを押す。

そうすると、入力したファイル名とディレクトリ名をが表示される。
[25] 主筆嬢 06/02/11 18:29
そーゆーことで、めでたしめでたし。
一応、ここまでのソースコードを公開しておく。
[26] nabiki_t 06/02/11 18:32
FS_Directoryって使っても問題ないのだろうか・・・
互換性とか何とかは保証されるのだろうか・・・
もっとうまいやり方があるような気がしなくもないんだがなぁ・・・
Solaris8,9,10では問題無かったのだが・・・
[27] 主筆嬢 06/02/11 18:33
いいんじゃない? 動くんだから。
[28] nabiki_t 06/02/11 18:34
そういう問題では無いだろう。 まったく・・・
まぁ、公開されてるヘッダファイル内で定義されてるマクロなんだから、問題ないとは思うけどな。
[29] 名無しさん 06/02/11 18:38
目次はこちら。
|