ァイル選択ダイアログの挙動を正したい,の巻
2002/05/12 新規

JFileChooser クラス は、使いやすいファイル選択ダイアログを提供してくれます。 Java で GUI アプリの開発に携わったことがあるなら、大抵の人がその恩恵を受けたコトでしょう。

この選択ダイアログでファイルを選ぶとき、なにを対象とするかを事前に設定することができます。 その設定をすることで「ファイル」が対象なのか「ディレクトリ」なのか、もしくは両方かといった挙動が変わります。 「単一か複数か」という話は、今回は脇において置いてください。

実際には setFileSelectionMode() メソッドにて、上記いずれかのパラメータを指定します。 たとえば
JFileChooser chooser=new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
とすれば、ファイルだけを選択できます。 このときディレクトリを選んで「開く」(または「保存」など)ボタンを押しても、 選択されたことにはならず、ダイアログも表示されたままの状態です。 挙動としては、そのディレクトリ直下に移動することになります。
これは、まぁ、納得のいく動作と言えるでしょう。

ただし、1つだけ欠点があります。
それは「ファイル」だけを対象としても、ディレクトリを選んだときに、ファイル名を記述する欄にディレクトリ名が入ってしまいます。 つまり任意のファイル名を保持したまま、ディレクトリ移動することができないというコトになります。

これでは「ファイルのみ対象」という設定の有効性が、半減してしまっています。 今回の小技は、この挙動を修正することです。

ディレクトリを選択しても、あら不思議!

基本的な理論は、こうです。 まずは JFileChooser クラス内部での挙動から。

  1. ユーザが、ファイルまたはディレクトリを選択
  2. 選択イベントが発生、該当する File オブジェクトを取得
  3. そのオブジェクトから名前を得て、ファイル名記述欄に反映
となっている(と思う)ので、このファイル名記述欄に反映のトコを抑えてしまえばコチラの勝ちと言えるでしょう。
  1. 取得した File オブジェクトから、ファイルかディレクトリかを判定
  2. getFileSelectionMode() メソッドを用いて、現在のモード(対象が何か)を取得
  3. モードに合わせてファイル名記述欄に反映させるか否かを決定
この仕組みを入れることで、ファイル名を保持したままディレクトリ移動することが可能となって、 とてもユーザ・フレンドリなファイル選択ダイアログになります。

さて。 理論がわかったところで、いざ実装をしてみようとすると、実はかなりの難問があることに気付きます。 API ドキュメント を見ても、また JFileChooser クラスのソースを見ても、どこにも「ファイル名記述欄に反映」している仕組みが見当たりません。 100% pure Java であると謳っているなら、そのソース・ファイルのどこかに記述があるはずです。 ・・・まぁ、配布されているソース一式には、省略されている部分も多いのですが。

ではドコにあるかと捜索してみると、けっこう深いところ、〜FileChooserUI クラスに隠されていました。 つまり、ファイル選択ダイアログの挙動を正すためには、自分なりのルック&フィールを用意しなければなりません。 修正する手法は説明も不要なほどに単純なのに、いざ実装するには遠回りをして面倒な手続きが必要です。
この面倒な手続き、独自のルック&フィール設定方法は自分だけのカッコ良いアプリを作りたい,の巻を参照してください。 後半部分は手抜きをしてますが、サンプル・ソースは参考になるはずです。  → Musi_chanLookAndFeel.java

と言ったところで、あとは下のソース・コードを真似すれば、当初の目的を達することができます。
〜FileChooserUI クラスの setFileName() メソッドをどう変更するかで、ダイアログ内テキスト・フィールド(ファイル名記述欄)の挙動は自由自在。思いのままです。


「Musi_chanFileChooserUI.java」のソースコード
import javax.swing.*;
import javax.swing.plaf.*;
import javax.swing.plaf.metal.*;
import java.io.*;

/**
 * Original look&feel (filechooser).
 * 
 * @author Musi_chan
 * (Musi_chan@cool.biglobe.ne.jp)
 * @version 2002/05/10 18:09
 */
public class Musi_chanFileChooserUI extends MetalFileChooserUI {
  public Musi_chanFileChooserUI(JFileChooser filechooser) {
    super(filechooser);
  }

  public static ComponentUI createUI(JComponent c) {
    return new Musi_chanFileChooserUI((JFileChooser) c);
  }

  public void setFileName(String filename) {
    String oldName;
    File file;

    oldName=getFileName();
    file=getFileChooser().getSelectedFile();
    if(file==null) {
      super.setFileName(oldName);

      return;
    }

    if(file.isDirectory()) {
      if(getFileChooser().isDirectorySelectionEnabled()) {
        super.setFileName(filename);
      } else {
        super.setFileName(oldName);
      }
    } else {
      if(getFileChooser().isFileSelectionEnabled()) {
        super.setFileName(filename);
      } else {
        super.setFileName(oldName);
      }
    }
  }
}

戻る