]> code.delx.au - gnu-emacs/blobdiff - lisp/image-dired.el
* progmodes/python.el: Explain how to restore "cc-mode"-like
[gnu-emacs] / lisp / image-dired.el
index ffefef847a763fbb7e2d5addb7679dfe48503c68..f0483e6217a26d9a46234ef88c26dbaa4b4d524f 100644 (file)
@@ -1,6 +1,6 @@
 ;;; image-dired.el --- use dired to browse and manipulate your images
 ;;
-;; Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+;; Copyright (C) 2005-2013 Free Software Foundation, Inc.
 ;;
 ;; Version: 0.4.11
 ;; Keywords: multimedia
@@ -8,10 +8,10 @@
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -19,9 +19,7 @@
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 ;;
 (require 'widget)
 
 (eval-when-compile
+  (require 'cl-lib)
   (require 'wid-edit))
 
 (defgroup image-dired nil
   :prefix "image-dired-"
   :group 'multimedia)
 
-(defcustom image-dired-dir (concat user-emacs-directory "image-dired/")
+(defcustom image-dired-dir (locate-user-emacs-file "image-dired/")
   "Directory where thumbnail images are stored."
   :type 'string
   :group 'image-dired)
@@ -188,19 +187,19 @@ that allows sharing of thumbnails across different programs."
   :group 'image-dired)
 
 (defcustom image-dired-db-file
-  (concat user-emacs-directory "image-dired/.image-dired_db")
+  (expand-file-name ".image-dired_db" image-dired-dir)
   "Database file where file names and their associated tags are stored."
   :type 'string
   :group 'image-dired)
 
 (defcustom image-dired-temp-image-file
-  (concat user-emacs-directory "image-dired/.image-dired_temp")
+  (expand-file-name ".image-dired_temp" image-dired-dir)
   "Name of temporary image file used by various commands."
   :type 'string
   :group 'image-dired)
 
 (defcustom image-dired-gallery-dir
-  (concat user-emacs-directory "image-dired/.image-dired_gallery")
+  (expand-file-name ".image-dired_gallery" image-dired-dir)
   "Directory to store generated gallery html pages.
 This path needs to be \"shared\" to the public so that it can access
 the index.html page that image-dired creates."
@@ -231,7 +230,7 @@ Used together with `image-dired-cmd-create-thumbnail-options'."
   :group 'image-dired)
 
 (defcustom image-dired-cmd-create-thumbnail-options
-  "%p -size %wx%h \"%f\" -resize %wx%h -strip jpeg:\"%t\""
+  "%p -size %wx%h \"%f\" -resize \"%wx%h>\" -strip jpeg:\"%t\""
   "Format of command used to create thumbnail image.
 Available options are %p which is replaced by
 `image-dired-cmd-create-thumbnail-program', %w which is replaced by
@@ -249,7 +248,7 @@ Used together with `image-dired-cmd-create-temp-image-options'."
   :group 'image-dired)
 
 (defcustom image-dired-cmd-create-temp-image-options
-  "%p -size %wx%h \"%f\" -resize %wx%h -strip jpeg:\"%t\""
+  "%p -size %wx%h \"%f\" -resize \"%wx%h>\" -strip jpeg:\"%t\""
   "Format of command used to create temporary image for display window.
 Available options are %p which is replaced by
 `image-dired-cmd-create-temp-image-program', %w and %h which is replaced by
@@ -282,7 +281,7 @@ with the information required by the Thumbnail Managing Standard."
       "-set \"Thumb::URI\" \"file://%f\" "
       "-set \"Description\" \"Thumbnail of file://%f\" "
       "-set \"Software\" \"" (emacs-version) "\" "))
-   "-thumbnail %wx%h png:\"%t\""
+   "-thumbnail \"%wx%h>\" png:\"%t\""
    (if image-dired-cmd-pngnq-program
        (concat
         " ; " image-dired-cmd-pngnq-program " -f \"%t\""
@@ -345,7 +344,7 @@ original image file name and %t which is replaced by
   :group 'image-dired)
 
 (defcustom image-dired-temp-rotate-image-file
-  (concat user-emacs-directory "image-dired/.image-dired_rotate_temp")
+  (expand-file-name ".image-dired_rotate_temp" image-dired-dir)
   "Temporary file for rotate operations."
   :type 'string
   :group 'image-dired)
@@ -368,8 +367,8 @@ Used together with `image-dired-cmd-write-exif-data-options'."
   "%p -%t=\"%v\" \"%f\""
   "Format of command used to write EXIF data.
 Available options are %p which is replaced by
-`image-dired-cmd-write-exif-data-program', %f which is replaced by the
-image file name, %t which is replaced by the tag name and %v
+`image-dired-cmd-write-exif-data-program', %f which is replaced by
+the image file name, %t which is replaced by the tag name and %v
 which is replaced by the tag value."
   :type 'string
   :group 'image-dired)
@@ -385,7 +384,7 @@ Used together with `image-dired-cmd-read-exif-data-program-options'."
   "%p -s -s -s -%t \"%f\""
   "Format of command used to read EXIF data.
 Available options are %p which is replaced by
-`image-dired-cmd-write-exif-data-options', %f which is replaced
+`image-dired-cmd-write-exif-data-program', %f which is replaced
 by the image file name and %t which is replaced by the tag name."
   :type 'string
   :group 'image-dired)
@@ -399,7 +398,8 @@ Used by `image-dired-gallery-generate' to leave out \"hidden\" images."
 
 (defcustom image-dired-thumb-size (if (eq 'standard image-dired-thumbnail-storage) 128 100)
   "Size of thumbnails, in pixels.
-This is the default size for both `image-dired-thumb-width' and `image-dired-thumb-height'."
+This is the default size for both `image-dired-thumb-width'
+and `image-dired-thumb-height'."
   :type 'integer
   :group 'image-dired)
 
@@ -426,11 +426,11 @@ This is where you see the cursor."
 
 (defcustom image-dired-line-up-method 'dynamic
   "Default method for line-up of thumbnails in thumbnail buffer.
-Used by `image-dired-display-thumbs' and other functions that needs to
-line-up thumbnails.  Dynamic means to use the available width of the
-window containing the thumbnail buffer, Fixed means to use
-`image-dired-thumbs-per-row', Interactive is for asking the user, and No
-line-up means that no automatic line-up will be done."
+Used by `image-dired-display-thumbs' and other functions that needs
+to line-up thumbnails.  Dynamic means to use the available width of
+the window containing the thumbnail buffer, Fixed means to use
+`image-dired-thumbs-per-row', Interactive is for asking the user,
+and No line-up means that no automatic line-up will be done."
   :type '(choice :tag "Default line-up method"
                  (const :tag "Dynamic" dynamic)
                 (const :tag "Fixed" fixed)
@@ -469,7 +469,7 @@ For more information, see the documentation for
 If non-nil, using `image-dired-next-line-and-display' and
 `image-dired-previous-line-and-display' will leave a trail of thumbnail
 images in the thumbnail buffer.  If you enable this and want to clean
-the thumbnail buffer because it is filled with too many thumbmnails,
+the thumbnail buffer because it is filled with too many thumbnails,
 just call `image-dired-display-thumb' to display only the image at point.
 This value can be toggled using `image-dired-toggle-append-browsing'."
   :type 'boolean
@@ -511,11 +511,19 @@ Used by `image-dired-copy-with-exif-file-name'."
   :group 'image-dired)
 
 (defcustom image-dired-show-all-from-dir-max-files 50
-  "Maximum number of files to show using `image-dired-show-all-from-dir'.
+  "Maximum number of files to show using `image-dired-show-all-from-dir'
 before warning the user."
   :type 'integer
   :group 'image-dired)
 
+(defmacro image-dired--with-db-file (&rest body)
+  "Run BODY in a temp buffer containing `image-dired-db-file'.
+Return the last form in BODY."
+  `(with-temp-buffer
+     (if (file-exists-p image-dired-db-file)
+        (insert-file-contents image-dired-db-file))
+     ,@body))
+
 (defun image-dired-dir ()
   "Return the current thumbnails directory (from variable `image-dired-dir').
 Create the thumbnails directory if it does not exist."
@@ -551,7 +559,7 @@ Create the thumbnails directory if it does not exist."
     ))
 
 (defun image-dired-insert-thumbnail (file original-file-name
-                                    associated-dired-buffer)
+                                     associated-dired-buffer)
   "Insert thumbnail image FILE.
 Add text properties ORIGINAL-FILE-NAME and ASSOCIATED-DIRED-BUFFER."
   (let (beg end)
@@ -594,14 +602,14 @@ according to the Thumbnail Managing Standard."
                  (md5 (file-name-as-directory (file-name-directory f)))))
            (format "%s%s%s.thumb.%s"
                    (file-name-as-directory (expand-file-name (image-dired-dir)))
-                   (file-name-sans-extension (file-name-nondirectory f))
+                   (file-name-base f)
                    (if md5-hash (concat "_" md5-hash) "")
                    (file-name-extension f))))
         ((eq 'per-directory image-dired-thumbnail-storage)
          (let ((f (expand-file-name file)))
            (format "%s.image-dired/%s.thumb.%s"
                    (file-name-directory f)
-                   (file-name-sans-extension (file-name-nondirectory f))
+                   (file-name-base f)
                    (file-name-extension f))))))
 
 (defun image-dired-create-thumb (original-file thumbnail-file)
@@ -633,31 +641,40 @@ according to the Thumbnail Managing Standard."
     (call-process shell-file-name nil nil nil shell-command-switch command)))
 
 ;;;###autoload
-(defun image-dired-dired-insert-marked-thumbs ()
-  "Insert thumbnails before file names of marked files in the dired buffer."
-  (interactive)
+(defun image-dired-dired-toggle-marked-thumbs (&optional arg)
+  "Toggle thumbnails in front of file names in the dired buffer.
+If no marked file could be found, insert or hide thumbnails on the
+current line.  ARG, if non-nil, specifies the files to use instead
+of the marked files.  If ARG is an integer, use the next ARG (or
+previous -ARG, if ARG<0) files."
+  (interactive "P")
   (dired-map-over-marks
-   (let* ((image-pos (dired-move-to-filename))
-          (image-file (dired-get-filename))
-          (thumb-file (image-dired-get-thumbnail-image image-file))
+   (let* ((image-pos  (dired-move-to-filename))
+          (image-file (dired-get-filename nil t))
+          thumb-file
           overlay)
-     ;; If image is not already added, then add it.
-     (unless (delq nil (mapcar (lambda (o) (overlay-get o 'put-image))
-                               ;; Can't use (overlays-at (point)), BUG?
-                               (overlays-in (point) (1+ (point)))))
-       (put-image thumb-file image-pos)
-       (setq
-       overlay
-       (car (delq nil (mapcar (lambda (o) (and (overlay-get o 'put-image) o))
-                              (overlays-in (point) (1+ (point)))))))
-       (overlay-put overlay 'image-file image-file)
-       (overlay-put overlay 'thumb-file thumb-file)))
-   nil)
-  (add-hook 'dired-after-readin-hook 'image-dired-dired-after-readin-hook nil t))
+     (when (and image-file
+                (string-match-p (image-file-name-regexp) image-file))
+       (setq thumb-file (image-dired-get-thumbnail-image image-file))
+       ;; If image is not already added, then add it.
+       (let ((cur-ov (overlays-in (point) (1+ (point)))))
+         (if cur-ov
+             (delete-overlay (car cur-ov))
+          (put-image thumb-file image-pos)
+          (setq overlay
+                 (cl-loop for o in (overlays-in (point) (1+ (point)))
+                          when (overlay-get o 'put-image) collect o into ov
+                          finally return (car ov)))
+          (overlay-put overlay 'image-file image-file)
+          (overlay-put overlay 'thumb-file thumb-file)))))
+   arg             ; Show or hide image on ARG next files.
+   'show-progress) ; Update dired display after each image is updated.
+  (add-hook 'dired-after-readin-hook
+            'image-dired-dired-after-readin-hook nil t))
 
 (defun image-dired-dired-after-readin-hook ()
   "Relocate existing thumbnail overlays in dired buffer after reverting.
-Move them to their corresponding files if they are still exist.
+Move them to their corresponding files if they still exist.
 Otherwise, delete overlays."
   (mapc (lambda (overlay)
           (when (overlay-get overlay 'put-image)
@@ -768,13 +785,12 @@ calling `image-dired-restore-window-configuration'."
     (dired dir)
     (delete-other-windows)
     (when (not arg)
-      (split-window-horizontally)
+      (split-window-right)
       (setq truncate-lines t)
       (save-excursion
         (other-window 1)
         (switch-to-buffer buf)
-        (split-window-vertically)
-        (other-window 1)
+        (select-window (split-window-below))
         (switch-to-buffer buf2)
         (other-window -2)))))
 
@@ -802,7 +818,7 @@ you have the dired buffer in the left window and the
 With optional argument APPEND, append thumbnail to thumbnail buffer
 instead of erasing it first.
 
-Option argument DO-NOT-POP controls if `pop-to-buffer' should be
+Optional argument DO-NOT-POP controls if `pop-to-buffer' should be
 used or not.  If non-nil, use `display-buffer' instead of
 `pop-to-buffer'.  This is used from functions like
 `image-dired-next-line-and-display' and
@@ -810,7 +826,7 @@ used or not.  If non-nil, use `display-buffer' instead of
 thumbnail buffer to be selected."
   (interactive "P")
   (let ((buf (image-dired-create-thumbnail-buffer))
-        curr-file thumb-name files count dired-buf beg)
+        thumb-name files dired-buf)
     (if arg
         (setq files (list (dired-get-filename)))
       (setq files (dired-get-marked-files)))
@@ -887,88 +903,81 @@ Signal error if there are problems creating it."
 
 (defun image-dired-write-tags (file-tags)
   "Write file tags to database.
-Write each file and tag in FILE-TAGS to the database.  FILE-TAGS
-is an alist in the following form:
+Write each file and tag in FILE-TAGS to the database.
+FILE-TAGS is an alist in the following form:
  ((FILE . TAG) ... )"
   (image-dired-sane-db-file)
   (let (end file tag)
-    (with-temp-file image-dired-db-file
-      (insert-file-contents image-dired-db-file)
-      (dolist (elt file-tags)
-       (setq file (car elt)
-             tag (cdr elt))
-       (goto-char (point-min))
-       (if (search-forward-regexp (format "^%s.*$" file) nil t)
-           (progn
-             (setq end (point))
-             (beginning-of-line)
-             (when (not (search-forward (format ";%s" tag) end t))
-               (end-of-line)
-               (insert (format ";%s" tag))))
-         (goto-char (point-max))
-         (insert (format "\n%s;%s" file tag)))))))
+    (image-dired--with-db-file
+     (setq buffer-file-name image-dired-db-file)
+     (dolist (elt file-tags)
+       (setq file (car elt)
+            tag (cdr elt))
+       (goto-char (point-min))
+       (if (search-forward-regexp (format "^%s.*$" file) nil t)
+          (progn
+            (setq end (point))
+            (beginning-of-line)
+            (when (not (search-forward (format ";%s" tag) end t))
+              (end-of-line)
+              (insert (format ";%s" tag))))
+        (goto-char (point-max))
+        (insert (format "\n%s;%s" file tag))))
+     (save-buffer))))
 
 (defun image-dired-remove-tag (files tag)
   "For all FILES, remove TAG from the image database."
   (image-dired-sane-db-file)
-  (save-excursion
-    (let (end buf start)
-      (setq buf (find-file image-dired-db-file))
-      (if (not (listp files))
-          (if (stringp files)
-              (setq files (list files))
-            (error "Files must be a string or a list of strings!")))
-      (mapc
-       (lambda (file)
-         (goto-char (point-min))
-         (when (search-forward-regexp
-                (format "^%s" file) nil t)
-           (end-of-line)
-           (setq end (point))
-           (beginning-of-line)
-           (when (search-forward-regexp (format "\\(;%s\\)" tag) end t)
-             (delete-region (match-beginning 1) (match-end 1))
-             ;; Check if file should still be in the database. If
-             ;; it has no tags or comments, it will be removed.
-             (end-of-line)
-             (setq end (point))
-             (beginning-of-line)
-             (when (not (search-forward ";" end t))
-               (kill-line 1)
-               ;; If on empty line at end of buffer
-               (when (and (eobp)
-                          (looking-at "^$"))
-                 (delete-backward-char 1))))))
-       files)
-      (save-buffer)
-      (kill-buffer buf))))
+  (image-dired--with-db-file
+   (setq buffer-file-name image-dired-db-file)
+   (let (end)
+     (unless (listp files)
+       (if (stringp files)
+          (setq files (list files))
+        (error "Files must be a string or a list of strings!")))
+     (dolist (file files)
+       (goto-char (point-min))
+       (when (search-forward-regexp (format "^%s" file) nil t)
+        (end-of-line)
+        (setq end (point))
+        (beginning-of-line)
+        (when (search-forward-regexp (format "\\(;%s\\)" tag) end t)
+          (delete-region (match-beginning 1) (match-end 1))
+          ;; Check if file should still be in the database. If
+          ;; it has no tags or comments, it will be removed.
+          (end-of-line)
+          (setq end (point))
+          (beginning-of-line)
+          (when (not (search-forward ";" end t))
+            (kill-line 1)
+            ;; If on empty line at end of buffer
+            (and (eobp)
+                 (looking-at "^$")
+                 (delete-char -1)))))))
+   (save-buffer)))
 
 (defun image-dired-list-tags (file)
   "Read all tags for image FILE from the image database."
   (image-dired-sane-db-file)
-  (save-excursion
-    (let (end buf (tags ""))
-      (setq buf (find-file image-dired-db-file))
-      (goto-char (point-min))
-      (when (search-forward-regexp
-             (format "^%s" file) nil t)
-        (end-of-line)
-        (setq end (point))
-        (beginning-of-line)
-        (if (search-forward ";" end t)
-            (if (search-forward "comment:" end t)
-                (if (search-forward ";" end t)
-                    (setq tags (buffer-substring (point) end)))
-              (setq tags (buffer-substring (point) end)))))
-      (kill-buffer buf)
-      (split-string tags ";"))))
+  (image-dired--with-db-file
+   (let (end (tags ""))
+     (when (search-forward-regexp (format "^%s" file) nil t)
+       (end-of-line)
+       (setq end (point))
+       (beginning-of-line)
+       (if (search-forward ";" end t)
+          (if (search-forward "comment:" end t)
+              (if (search-forward ";" end t)
+                  (setq tags (buffer-substring (point) end)))
+            (setq tags (buffer-substring (point) end)))))
+     (split-string tags ";"))))
 
 ;;;###autoload
 (defun image-dired-tag-files (arg)
   "Tag marked file(s) in dired.  With prefix ARG, tag file at point."
   (interactive "P")
   (let ((tag (read-string "Tags to add (separate tags with a semicolon): "))
-        curr-file files)
+        files)
     (if arg
         (setq files (list (dired-get-filename)))
       (setq files (dired-get-marked-files)))
@@ -1023,8 +1032,8 @@ With prefix argument ARG, remove tag from file at point."
 
 (defun image-dired-track-original-file ()
   "Track the original file in the associated dired buffer.
-See documentation for `image-dired-toggle-movement-tracking'.  Interactive
-use only useful if `image-dired-track-movement' is nil."
+See documentation for `image-dired-toggle-movement-tracking'.
+Interactive use only useful if `image-dired-track-movement' is nil."
   (interactive)
   (let ((old-buf (current-buffer))
         (dired-buf (image-dired-associated-dired-buffer))
@@ -1049,8 +1058,8 @@ position in the other buffer."
 
 (defun image-dired-track-thumbnail ()
   "Track current dired file's thumb in `image-dired-thumbnail-buffer'.
-This is almost the same as what `image-dired-track-original-file' does, but
-the other way around."
+This is almost the same as what `image-dired-track-original-file' does,
+but the other way around."
   (let ((file (dired-get-filename))
         (old-buf (current-buffer))
         prop-val found)
@@ -1073,8 +1082,8 @@ the other way around."
 
 (defun image-dired-dired-next-line (&optional arg)
   "Call `dired-next-line', then track thumbnail.
-This can safely replace `dired-next-line'.  With prefix argument, move
-ARG lines."
+This can safely replace `dired-next-line'.
+With prefix argument, move ARG lines."
   (interactive "P")
   (dired-next-line (or arg 1))
   (if image-dired-track-movement
@@ -1082,8 +1091,8 @@ ARG lines."
 
 (defun image-dired-dired-previous-line (&optional arg)
   "Call `dired-previous-line', then track thumbnail.
-This can safely replace `dired-previous-line'.  With prefix argument,
-move ARG lines."
+This can safely replace `dired-previous-line'.
+With prefix argument, move ARG lines."
   (interactive "P")
   (dired-previous-line (or arg 1))
   (if image-dired-track-movement
@@ -1160,7 +1169,7 @@ image."
 (defun image-dired-format-properties-string (buf file props comment)
   "Format display properties.
 BUF is the associated dired buffer, FILE is the original image file
-name, PROPS is a list of tags and COMMENT is the image files's
+name, PROPS is a list of tags and COMMENT is the image file's
 comment."
   (format-spec
    image-dired-display-properties-format
@@ -1585,19 +1594,21 @@ Note that n, p and <down> and <up> will be hijacked and bound to
     [menu-bar image-dired image-dired-next-line-and-display]
     '("Display thumb for next file" . image-dired-next-line-and-display)))
 
+(declare-function clear-image-cache "image.c" (&optional filter))
+
 (defun image-dired-create-thumbs (&optional arg)
   "Create thumbnail images for all marked files in dired.
 With prefix argument ARG, create thumbnails even if they already exist
-\(i.e.  use this to refresh your thumbnails)."
+\(i.e. use this to refresh your thumbnails)."
   (interactive "P")
-  (let (curr-file thumb-name files count)
+  (let (thumb-name files)
     (setq files (dired-get-marked-files))
     (mapcar
      (lambda (curr-file)
        (setq thumb-name (image-dired-thumb-name curr-file))
        ;; If the user overrides the exist check, we must clear the
        ;; image cache so that if the user wants to display the
-       ;; thumnail, it is not fetched from cache.
+       ;; thumbnail, it is not fetched from cache.
        (if arg
            (clear-image-cache))
        (if (or (not (file-exists-p thumb-name))
@@ -1898,7 +1909,7 @@ overwritten.  This confirmation can be turned off using
   (if (not (image-dired-image-at-point-p))
       (message "No image at point")
     (let ((file (image-dired-original-file-name))
-          command temp-file)
+          command)
       (if (not (string-match "\.[jJ][pP[eE]?[gG]$" file))
           (error "Only JPEG images can be rotated!"))
       (setq command (format-spec
@@ -2035,7 +2046,7 @@ function.  The result is a couple of new files in
      files)))
 
 (defun image-dired-display-next-thumbnail-original ()
-  "In thubnail buffer, move to next thumbnail and display the image."
+  "In thumbnail buffer, move to next thumbnail and display the image."
   (interactive)
   (image-dired-forward-image)
   (image-dired-display-thumbnail-original-image))
@@ -2048,39 +2059,40 @@ function.  The result is a couple of new files in
 
 (defun image-dired-write-comments (file-comments)
   "Write file comments to database.
-Write file comments to one or more files.  FILE-COMMENTS is an alist on
-the following form:
+Write file comments to one or more files.
+FILE-COMMENTS is an alist on the following form:
  ((FILE . COMMENT) ... )"
   (image-dired-sane-db-file)
   (let (end comment-beg-pos comment-end-pos file comment)
-    (with-temp-file image-dired-db-file
-      (insert-file-contents image-dired-db-file)
-      (dolist (elt file-comments)
-       (setq file (car elt)
-             comment (cdr elt))
-       (goto-char (point-min))
-       (if (search-forward-regexp (format "^%s.*$" file) nil t)
-           (progn
-             (setq end (point))
-             (beginning-of-line)
-             ;; Delete old comment, if any
-             (when (search-forward ";comment:" end t)
-               (setq comment-beg-pos (match-beginning 0))
-               ;; Any tags after the comment?
-               (if (search-forward ";" end t)
-                   (setq comment-end-pos (- (point) 1))
-                 (setq comment-end-pos end))
-               ;; Delete comment tag and comment
-               (delete-region comment-beg-pos comment-end-pos))
-             ;; Insert new comment
-             (beginning-of-line)
-             (unless (search-forward ";" end t)
-               (end-of-line)
-               (insert ";"))
-             (insert (format "comment:%s;" comment)))
-         ;; File does not exist in database - add it.
-         (goto-char (point-max))
-         (insert (format "\n%s;comment:%s" file comment)))))))
+    (image-dired--with-db-file
+     (setq buffer-file-name image-dired-db-file)
+     (dolist (elt file-comments)
+       (setq file (car elt)
+            comment (cdr elt))
+       (goto-char (point-min))
+       (if (search-forward-regexp (format "^%s.*$" file) nil t)
+          (progn
+            (setq end (point))
+            (beginning-of-line)
+            ;; Delete old comment, if any
+            (when (search-forward ";comment:" end t)
+              (setq comment-beg-pos (match-beginning 0))
+              ;; Any tags after the comment?
+              (if (search-forward ";" end t)
+                  (setq comment-end-pos (- (point) 1))
+                (setq comment-end-pos end))
+              ;; Delete comment tag and comment
+              (delete-region comment-beg-pos comment-end-pos))
+            ;; Insert new comment
+            (beginning-of-line)
+            (unless (search-forward ";" end t)
+              (end-of-line)
+              (insert ";"))
+            (insert (format "comment:%s;" comment)))
+        ;; File does not exist in database - add it.
+        (goto-char (point-max))
+        (insert (format "\n%s;comment:%s" file comment))))
+     (save-buffer))))
 
 (defun image-dired-update-property (prop value)
   "Update text property PROP with value VALUE at point."
@@ -2112,8 +2124,7 @@ the following form:
 
 (defun image-dired-read-comment (&optional file)
   "Read comment for an image.
-Read comment for an image, optionally using old comment from FILE
-as initial value."
+Optionally use old comment from FILE as initial value."
   (let ((comment
          (read-string
           "Comment: "
@@ -2123,24 +2134,20 @@ as initial value."
 (defun image-dired-get-comment (file)
   "Get comment for file FILE."
   (image-dired-sane-db-file)
-  (save-excursion
-    (let (end buf comment-beg-pos comment-end-pos comment)
-      (setq buf (find-file image-dired-db-file))
-      (goto-char (point-min))
-      (when (search-forward-regexp
-             (format "^%s" file) nil t)
-        (end-of-line)
-        (setq end (point))
-        (beginning-of-line)
-        (cond ((search-forward ";comment:" end t)
-               (setq comment-beg-pos (point))
-               (if (search-forward ";" end t)
-                   (setq comment-end-pos (- (point) 1))
-                 (setq comment-end-pos end))
-               (setq comment (buffer-substring
-                              comment-beg-pos comment-end-pos)))))
-      (kill-buffer buf)
-      comment)))
+  (image-dired--with-db-file
+   (let (end comment-beg-pos comment-end-pos comment)
+     (when (search-forward-regexp (format "^%s" file) nil t)
+       (end-of-line)
+       (setq end (point))
+       (beginning-of-line)
+       (when (search-forward ";comment:" end t)
+        (setq comment-beg-pos (point))
+        (if (search-forward ";" end t)
+            (setq comment-end-pos (- (point) 1))
+          (setq comment-end-pos end))
+        (setq comment (buffer-substring
+                       comment-beg-pos comment-end-pos))))
+     comment)))
 
 ;;;###autoload
 (defun image-dired-mark-tagged-files ()
@@ -2149,37 +2156,31 @@ A `tag' is a keyword, a piece of meta data, associated with an
 image file and stored in image-dired's database file.  This command
 lets you input a regexp and this will be matched against all tags
 on all image files in the database file.  The files that have a
-matching tags will be marked in the dired buffer."
+matching tag will be marked in the dired buffer."
   (interactive)
   (image-dired-sane-db-file)
   (let ((tag (read-string "Mark tagged files (regexp): "))
         (hits 0)
-        files buf)
-    (save-excursion
-      (setq buf (find-file image-dired-db-file))
-      (goto-char (point-min))
-      ;; Collect matches
-      (while (search-forward-regexp
-              (concat "\\(^[^;\n]+\\);.*" tag ".*$") nil t)
-        (setq files (append (list (match-string 1)) files)))
-      (kill-buffer buf)
-      ;; Mark files
-      (mapc
-       ;; I tried using `dired-mark-files-regexp' but it was
-       ;; waaaay to slow.
-       (lambda (curr-file)
-         ;; Don't bother about hits found in other directories than
-         ;; the current one.
-         (when (string= (file-name-as-directory
-                         (expand-file-name default-directory))
-                      (file-name-as-directory
-                       (file-name-directory curr-file)))
-           (setq curr-file (file-name-nondirectory curr-file))
-           (goto-char (point-min))
-           (when (search-forward-regexp (format "\\s %s$" curr-file) nil t)
-             (setq hits (+ hits 1))
-             (dired-mark 1))))
-       files))
+        files)
+    (image-dired--with-db-file
+     ;; Collect matches
+     (while (search-forward-regexp
+            (concat "\\(^[^;\n]+\\);.*" tag ".*$") nil t)
+       (push (match-string 1) files)))
+    ;; Mark files
+    (dolist (curr-file files)
+      ;; I tried using `dired-mark-files-regexp' but it was waaaay to
+      ;; slow.  Don't bother about hits found in other directories
+      ;; than the current one.
+      (when (string= (file-name-as-directory
+                     (expand-file-name default-directory))
+                    (file-name-as-directory
+                     (file-name-directory curr-file)))
+       (setq curr-file (file-name-nondirectory curr-file))
+       (goto-char (point-min))
+       (when (search-forward-regexp (format "\\s %s$" curr-file) nil t)
+         (setq hits (+ hits 1))
+         (dired-mark 1))))
     (message "%d files with matching tag marked." hits)))
 
 (defun image-dired-mouse-display-image (event)
@@ -2187,26 +2188,25 @@ matching tags will be marked in the dired buffer."
 Track this in associated dired buffer if `image-dired-track-movement' is
 non-nil."
   (interactive "e")
-  (let (file)
-    (mouse-set-point event)
-    (goto-char (posn-point (event-end event)))
-    (setq file (image-dired-original-file-name))
-    (if image-dired-track-movement
-        (image-dired-track-original-file))
-    (image-dired-create-display-image-buffer)
-    (display-buffer image-dired-display-image-buffer)
-    (image-dired-display-image file)))
+  (mouse-set-point event)
+  (goto-char (posn-point (event-end event)))
+  (let ((file (image-dired-original-file-name)))
+    (when file
+      (if image-dired-track-movement
+         (image-dired-track-original-file))
+      (image-dired-create-display-image-buffer)
+      (display-buffer image-dired-display-image-buffer)
+      (image-dired-display-image file))))
 
 (defun image-dired-mouse-select-thumbnail (event)
   "Use mouse EVENT to select thumbnail image.
 Track this in associated dired buffer if `image-dired-track-movement' is
 non-nil."
   (interactive "e")
-  (let (file)
-    (mouse-set-point event)
-    (goto-char (posn-point (event-end event)))
-    (if image-dired-track-movement
-        (image-dired-track-original-file)))
+  (mouse-set-point event)
+  (goto-char (posn-point (event-end event)))
+  (if image-dired-track-movement
+      (image-dired-track-original-file))
   (image-dired-display-thumb-properties))
 
 (defun image-dired-mouse-toggle-mark (event)
@@ -2214,11 +2214,10 @@ non-nil."
 Track this in associated dired buffer if `image-dired-track-movement' is
 non-nil."
   (interactive "e")
-  (let (file)
-    (mouse-set-point event)
-    (goto-char (posn-point (event-end event)))
-    (if image-dired-track-movement
-        (image-dired-track-original-file)))
+  (mouse-set-point event)
+  (goto-char (posn-point (event-end event)))
+  (if image-dired-track-movement
+      (image-dired-track-original-file))
   (image-dired-toggle-mark-thumb-original-file))
 
 (defun image-dired-dired-display-properties ()
@@ -2317,29 +2316,26 @@ image-dired-file-comment-list:
 (defun image-dired-create-gallery-lists ()
   "Create temporary lists used by `image-dired-gallery-generate'."
   (image-dired-sane-db-file)
-  (let ((buf (find-file image-dired-db-file))
-        end beg file row-tags)
-    (setq image-dired-tag-file-list nil)
-    (setq image-dired-file-tag-list nil)
-    (setq image-dired-file-comment-list nil)
-    (goto-char (point-min))
-    (while (search-forward-regexp "^." nil t)
-      (end-of-line)
-      (setq end (point))
-      (beginning-of-line)
-      (setq beg (point))
-      (if (not (search-forward ";" end nil))
-          (error "Something is really wrong, check format of database"))
-      (setq row-tags (split-string
-                      (buffer-substring beg end) ";"))
-      (setq file (car row-tags))
-      (mapc
-       (lambda (x)
-         (if (not (string-match "^comment:\\(.*\\)" x))
-             (image-dired-add-to-tag-file-lists x file)
-           (image-dired-add-to-file-comment-list file (match-string 1 x))))
-       (cdr row-tags)))
-    (kill-buffer buf))
+  (image-dired--with-db-file
+   (let (end beg file row-tags)
+     (setq image-dired-tag-file-list nil)
+     (setq image-dired-file-tag-list nil)
+     (setq image-dired-file-comment-list nil)
+     (goto-char (point-min))
+     (while (search-forward-regexp "^." nil t)
+       (end-of-line)
+       (setq end (point))
+       (beginning-of-line)
+       (setq beg (point))
+       (unless (search-forward ";" end nil)
+        (error "Something is really wrong, check format of database"))
+       (setq row-tags (split-string
+                      (buffer-substring beg end) ";"))
+       (setq file (car row-tags))
+       (dolist (x (cdr row-tags))
+        (if (not (string-match "^comment:\\(.*\\)" x))
+            (image-dired-add-to-tag-file-lists x file)
+          (image-dired-add-to-file-comment-list file (match-string 1 x)))))))
   ;; Sort tag-file list
   (setq image-dired-tag-file-list
         (sort image-dired-tag-file-list
@@ -2360,14 +2356,15 @@ image-dired-file-comment-list:
   "Generate gallery pages.
 First we create a couple of Lisp structures from the database to make
 it easier to generate, then HTML-files are created in
-`image-dired-gallery-dir'"
+`image-dired-gallery-dir'."
   (interactive)
   (if (eq 'per-directory image-dired-thumbnail-storage)
       (error "Currently, gallery generation is not supported \
 when using per-directory thumbnail file storage"))
   (image-dired-create-gallery-lists)
   (let ((tags image-dired-tag-file-list)
-        count curr tag index-buf tag-buf
+       (index-file (format "%s/index.html" image-dired-gallery-dir))
+        count tag tag-file
         comment file-tags tag-link tag-link-list)
     ;; Make sure gallery root exist
     (if (file-exists-p image-dired-gallery-dir)
@@ -2375,85 +2372,75 @@ when using per-directory thumbnail file storage"))
             (error "Variable image-dired-gallery-dir is not a directory"))
       (make-directory image-dired-gallery-dir))
     ;; Open index file
-    (setq index-buf (find-file
-                     (format "%s/index.html" image-dired-gallery-dir)))
-    (erase-buffer)
-    (insert "<html>\n")
-    (insert "  <body>\n")
-    (insert "   <h2>Image-Dired Gallery</h2>\n")
-    (insert (format "<p>\n    Gallery generated %s\n   <p>\n"
-                    (current-time-string)))
-    (insert "   <h3>Tag index</h3>\n")
-    (setq count 1)
-    ;; Pre-generate list of all tag links
-    (mapc
-     (lambda (curr)
-       (setq tag (car curr))
-       (when (not (member tag image-dired-gallery-hidden-tags))
-         (setq tag-link (format "<a href=\"%d.html\">%s</a>" count tag))
-         (if tag-link-list
-             (setq tag-link-list
-                   (append tag-link-list (list (cons tag tag-link))))
-           (setq tag-link-list (list (cons tag tag-link))))
-         (setq count (1+ count))))
-     tags)
-    (setq count 1)
-    ;; Main loop where we generated thumbnail pages per tag
-    (mapc
-     (lambda (curr)
-       (setq tag (car curr))
-       ;; Don't display hidden tags
-       (when (not (member tag image-dired-gallery-hidden-tags))
-         ;; Insert link to tag page in index
-         (insert (format "    %s<br>\n" (cdr (assoc tag tag-link-list))))
-         ;; Open per-tag file
-         (setq tag-buf (find-file
-                        (format "%s/%s.html" image-dired-gallery-dir count)))
-         (erase-buffer)
-         (insert "<html>\n")
-         (insert "  <body>\n")
-         (insert "  <p><a href=\"index.html\">Index</a></p>\n")
-         (insert (format "  <h2>Images with tag &quot;%s&quot;</h2>" tag))
-         ;; Main loop for files per tag page
-         (mapc
-          (lambda (file)
-            (when (not (image-dired-hidden-p file))
-              ;; Insert thumbnail with link to full image
-              (insert
-               (format "<a href=\"%s/%s\"><img src=\"%s/%s\"%s></a>\n"
-                       image-dired-gallery-image-root-url
-                      (file-name-nondirectory file)
-                       image-dired-gallery-thumb-image-root-url
-                       (file-name-nondirectory (image-dired-thumb-name file)) file))
-              ;; Insert comment, if any
-              (if (setq comment (cdr (assoc file image-dired-file-comment-list)))
-                  (insert (format "<br>\n%s<br>\n" comment))
-                (insert "<br>\n"))
-              ;; Insert links to other tags, if any
-              (when (> (length
-                        (setq file-tags (assoc file image-dired-file-tag-list))) 2)
-                (insert "[ ")
-                (mapc
-                 (lambda (extra-tag)
-                   ;; Only insert if not file name or the main tag
-                   (if (and (not (equal extra-tag tag))
-                            (not (equal extra-tag file)))
-                       (insert
-                        (format "%s " (cdr (assoc extra-tag tag-link-list))))))
-                 file-tags)
-                (insert "]<br>\n"))))
-          (cdr curr))
-         (insert "  <p><a href=\"index.html\">Index</a></p>\n")
-         (insert "  </body>\n")
-         (insert "</html>\n")
-         (save-buffer)
-         (kill-buffer tag-buf)
-         (setq count (1+ count))))
-       tags)
-    (insert "  </body>\n")
-    (insert "</html>")
-    (save-buffer)
-    (kill-buffer index-buf)))
+    (with-temp-file index-file
+      (if (file-exists-p index-file)
+         (insert-file-contents index-file))
+      (insert "<html>\n")
+      (insert "  <body>\n")
+      (insert "   <h2>Image-Dired Gallery</h2>\n")
+      (insert (format "<p>\n    Gallery generated %s\n   <p>\n"
+                     (current-time-string)))
+      (insert "   <h3>Tag index</h3>\n")
+      (setq count 1)
+      ;; Pre-generate list of all tag links
+      (dolist (curr tags)
+       (setq tag (car curr))
+       (when (not (member tag image-dired-gallery-hidden-tags))
+         (setq tag-link (format "<a href=\"%d.html\">%s</a>" count tag))
+         (if tag-link-list
+             (setq tag-link-list
+                   (append tag-link-list (list (cons tag tag-link))))
+           (setq tag-link-list (list (cons tag tag-link))))
+         (setq count (1+ count))))
+      (setq count 1)
+      ;; Main loop where we generated thumbnail pages per tag
+      (dolist (curr tags)
+       (setq tag (car curr))
+       ;; Don't display hidden tags
+       (when (not (member tag image-dired-gallery-hidden-tags))
+         ;; Insert link to tag page in index
+         (insert (format "    %s<br>\n" (cdr (assoc tag tag-link-list))))
+         ;; Open per-tag file
+         (setq tag-file (format "%s/%s.html" image-dired-gallery-dir count))
+         (with-temp-file tag-file
+           (if (file-exists-p tag-file)
+               (insert-file-contents tag-file))
+           (erase-buffer)
+           (insert "<html>\n")
+           (insert "  <body>\n")
+           (insert "  <p><a href=\"index.html\">Index</a></p>\n")
+           (insert (format "  <h2>Images with tag &quot;%s&quot;</h2>" tag))
+           ;; Main loop for files per tag page
+           (dolist (file (cdr curr))
+             (unless (image-dired-hidden-p file)
+               ;; Insert thumbnail with link to full image
+               (insert
+                (format "<a href=\"%s/%s\"><img src=\"%s/%s\"%s></a>\n"
+                        image-dired-gallery-image-root-url
+                        (file-name-nondirectory file)
+                        image-dired-gallery-thumb-image-root-url
+                        (file-name-nondirectory (image-dired-thumb-name file)) file))
+               ;; Insert comment, if any
+               (if (setq comment (cdr (assoc file image-dired-file-comment-list)))
+                   (insert (format "<br>\n%s<br>\n" comment))
+                 (insert "<br>\n"))
+               ;; Insert links to other tags, if any
+               (when (> (length
+                         (setq file-tags (assoc file image-dired-file-tag-list))) 2)
+                 (insert "[ ")
+                 (dolist (extra-tag file-tags)
+                   ;; Only insert if not file name or the main tag
+                   (if (and (not (equal extra-tag tag))
+                            (not (equal extra-tag file)))
+                       (insert
+                        (format "%s " (cdr (assoc extra-tag tag-link-list))))))
+                 (insert "]<br>\n"))))
+           (insert "  <p><a href=\"index.html\">Index</a></p>\n")
+           (insert "  </body>\n")
+           (insert "</html>\n"))
+         (setq count (1+ count))))
+      (insert "  </body>\n")
+      (insert "</html>"))))
 
 (defun image-dired-kill-buffer-and-window ()
   "Kill the current buffer and, if possible, also the window."
@@ -2526,7 +2513,7 @@ the operation by activating the Cancel button.\n\n")
     (widget-insert "\n")
     (widget-create 'push-button
                  :notify
-                 (lambda (&rest ignore)
+                 (lambda (&rest _ignore)
                    (image-dired-save-information-from-widgets)
                    (bury-buffer)
                    (message "Done."))
@@ -2534,7 +2521,7 @@ the operation by activating the Cancel button.\n\n")
     (widget-insert " ")
     (widget-create 'push-button
                    :notify
-                   (lambda (&rest ignore)
+                   (lambda (&rest _ignore)
                      (bury-buffer)
                      (message "Operation canceled."))
                    "Cancel")
@@ -2585,7 +2572,7 @@ tags to their respective image file.  Internal function used by
 ;;                 `(,(nth 4 fattribs) ,(nth 7 fattribs) ,f)))
 ;;             (directory-files (image-dired-dir) t ".+\.thumb\..+$"))
 ;;            ;; Sort function. Compare time between two files.
-;;            '(lambda (l1 l2)
+;;            (lambda (l1 l2)
 ;;               (time-less-p (car l1) (car l2)))))
 ;;          (dirsize (apply '+ (mapcar (lambda (x) (cadr x)) files))))
 ;;     (while (> dirsize image-dired-dir-max-size)
@@ -2615,5 +2602,4 @@ tags to their respective image file.  Internal function used by
 
 (provide 'image-dired)
 
-;; arch-tag: 9d11411d-331f-4380-8b44-8adfe3a0343e
 ;;; image-dired.el ends here