メニューにファイル履歴をつけよう

最近使ったファイルをメニューから開けると便利ですね。
よってそれを実装してみましょうという話です。

まずはファイル履歴数分メニューの欄を作ります。
履歴データがないのにメニューが空白で表示されてたら寒い(場合にもよるとは思いますが)のでメニューの Caption は空白にして Visible を False にしておきましょう。
以下例では

RecentFile1Menu: TMenuItem;
RecentFile2Menu: TMenuItem;
・
・
・
RecentFile8Menu: TMenuItem;
RecentFile9Menu: TMenuItem;

という名前で9個履歴欄を用意したことにします。

最初に簡単な履歴の書き込み、読み出しから。
INI ファイルを使うと以下のように書けるでしょう。

procedure TMainForm.LoadFileHistory;
var
  I: Integer;
  S: string;
  Component: TComponent;
begin
  for I := 1 to 9 do
  begin
    S := IniFile.ReadString('RecentFile', 'File' + IntToStr(I), '');
    if S <> '' then
    begin
      Component := FindComponent('RecentFile' + IntToStr(I) + 'Menu');
      TMenuItem(Component).Caption := '&' + IntToStr(I) + ' ' + S;
      TMenuItem(Component).Visible := True;
    end;
  end;
end;

procedure TMainForm.SaveFileHistory;
var
  I: Integer;
  S: string;
  Component: TComponent;
begin
  for I := 1 to 9 do
  begin
    Component := FindComponent('RecentFile' + IntToStr(I) + 'Menu');

    if TMenuItem(Component).Caption <> '' then
    begin
      S := TMenuItem(Component).Caption;
      IniFile.WriteString('RecentFile', 'File' + IntToStr(I), Copy(S, 4, Length(S)));
    end;
  end;
end;

ポイントは FindComponent 関数でしょうか。
これは与えた文字列の名前のコンポーネントを返す関数です。
利用することで非常にソースがすっきりとしたものになります。

次にファイルの読み書きによって起こる履歴の更新を行いましょう。
以下のようなソースです。

procedure TMainForm.RegistFileNameToHistory(AFileName: TFileName);
var
  I, P: Integer;
  S1, S2: string;
  C1, C2: TComponent;
begin
  AFileName := AnsiLowerCase(AFileName);
  P := 9;

  for I := 1 to 9 do
  begin
    C1 := FindComponent('RecentFile' + IntToStr(I) + 'Menu');
    S1 := TMenuItem(C1).Caption;

    if AFileName = Copy(S1, 4, Length(S1)) then P := I;
  end;

  C1 := FindComponent('RecentFile' + IntToStr(P) + 'Menu');
  for I := P downto 2 do
  begin
    C2 := FindComponent('RecentFile' + IntToStr(I - 1) + 'Menu');
    S2 := TMenuItem(C2).Caption;

    if S2 <> '' then
    begin
      TMenuItem(C1).Caption := '&' + IntToStr(I) + ' ' + Copy(S2, 4, Length(S2));
      TMenuItem(C1).Visible := True;
    end;

    C1 := C2;
  end;
  TMenuItem(C1).Caption := '&1 ' + AFileName;
  TMenuItem(C1).Visible := True;
end;

ファイル名は Windows では C.txt も c.TxT も同じ物になるので一律に大文字か小文字に揃えてしまうと楽です。
当然ながら比較のときだけ変換すれば良いのですが、私は趣味で常に小文字で統一してます。
一般的ではないと思うので、気になる人は手を入れてください。
たいして難しくはないはず。

一つ目の for ループでは、現在の履歴に同名のファイルが存在してるかチェックしています。
存在していればその位置を P に代入します。

2つ目の for ループで、先頭に新しいファイル名をいれるために、履歴の位置をずらします。
現在の履歴の中に同名のファイルがあればそこまでずらし、なければ全てずらします。

最後に先頭にファイル名を入れます。

最後に、履歴をクリックされたらそのファイルが読まれるようにしておかなければいけません。
これは Click イベントを1個作って、それを全ての履歴欄で共有します。

procedure TMainForm.RecentFileMenuClick(Sender: TObject);
var
  AFileName: TFileName;
begin
  AFileName := Copy(TMenuItem(Sender).Caption, 4, Length(TMenuItem(Sender).Caption));
  if FileExists(AFileName) then
  begin
    if LoadFile(AFileName) then RegistFileNameToHistory(AFileName);
  end;
end;

LoadFile のところを、プログラム固有のファイル読み込み関数と置き換えてください。


Return index page