最近使ったファイル

xyzzy の音 - ファイル操作 #最近使ったファイル一覧インスパイヤされました。
今回の改造にあたって加えられた新たなオリジナリティは以下。

  • ダイアログで複数選択できるようにしました。
  • 存在しないファイルを開こうとした時に、ヒストリから削除するようにしました。

以下の環境で動作確認しました。

最近使ったファイルをダイアログ表示します。
ダイアログでファイルを選択して『OK』を押すと、選択したファイルを開きます。

コード

;; 最近使ったファイル
; * xyzzy の音 - ファイル操作 #最近使ったファイル一覧
;   <http://hie.s64.xrea.com/xyzzy/note/file-handling.html#list-recents>
; から朴りました。

(defvar *recent-file-open-dialog-template*
  `(dialog 0 0 298 117
           (:caption "最近使ったファイル")
           (:font 9 "MS UI Gothic")
           (:control
            (:listbox IDC_LIST nil #x50b10119 5 6 288 84)
            (:button IDOK "&OK" #x50010001 6 96 50 14)
            (:button IDCANCEL "&Cancel" #x50010000 60 96 50 14))))

(defun recent-file-open-dialog ()
  (interactive)
  (let ((hlist (mapcar #'(lambda (x)
                           (list (abbreviate-display-string x 96 t) x))
                       *minibuffer-file-name-history*)))
    (multiple-value-bind (result data)
        (let ((omode (get-ime-mode)))
          (toggle-ime nil)
          (unwind-protect
              (dialog-box *recent-file-open-dialog-template*
                          (list (cons 'IDC_LIST hlist))
                          '(list (IDC_LIST :column (1)
                                           :must-match t
                                           :enable (IDOK))))
            (toggle-ime omode)))
      (if result
          (let ((lst (cdr (assoc 'IDC_LIST data)))
                dlst)
            (dolist (var lst t)
              (let ((filename (nth 1 var)))
                (cond ((file-exist-p filename)
                       (find-file filename)
                       (add-history filename '*minibuffer-file-name-history*))
                      (t
                       (push filename dlst)
                       (or (delete filename *minibuffer-file-name-history*)
                           (setq *minibuffer-file-name-history* nil))))))
            (if dlst
                (let ((lstlen (list-length lst))
                      (dlstlen (list-length dlst)))
                  (if (= lstlen dlstlen 1)
                      (message "~A: そんなファイルはありません" (car dlst))
                    (msgbox (format nil
                                    "~D 個中 ~D 個のファイルが見つかりませんでした~%~{~%~A~}"
                                    lstlen dlstlen dlst)))
                  nil)
              t))))))

; メニューの『ファイル』->『最近使ったファイル...』と入れ替える
(add-hook '*post-startup-hook*
          #'(lambda ()
              (let* ((file (get-menu *app-menu* 'ed::file))
                     (pos (1+ (get-menu-position file :above-recent))))
                (delete-menu file pos t)
                (insert-menu-item file pos nil
                                  "最近使ったファイル(&F)..."
                                  'recent-file-open-dialog
                                  #'(lambda ()
                                      (or *minibuffer-file-name-history*
                                          :disable))))))