diff options
Diffstat (limited to 'lisp/image-mode.el')
-rw-r--r-- | lisp/image-mode.el | 166 |
1 files changed, 123 insertions, 43 deletions
diff --git a/lisp/image-mode.el b/lisp/image-mode.el index 1bb213c2489..032ebf38733 100644 --- a/lisp/image-mode.el +++ b/lisp/image-mode.el @@ -40,6 +40,7 @@ (require 'image) (require 'exif) +(require 'dired) (eval-when-compile (require 'cl-lib)) ;;; Image mode window-info management. @@ -611,24 +612,35 @@ Key bindings: (setq major-mode 'image-mode) (setq image-transform-resize image-auto-resize) + ;; Bail out early if we have no image data. + (if (zerop (buffer-size)) + (funcall (if (called-interactively-p 'any) 'error 'message) + (if (file-exists-p buffer-file-name) + "Empty file" + "(New file)")) + (image-mode--display))) + +(defun image-mode--display () (if (not (image-get-display-property)) (progn (when (condition-case err - (progn - (image-toggle-display-image) - t) - (unknown-image-type - (image-mode-as-text) - (funcall - (if (called-interactively-p 'any) 'error 'message) - "Unknown image type; consider switching `image-use-external-converter' on") - nil) - (error - (image-mode-as-text) - (funcall - (if (called-interactively-p 'any) 'error 'message) - "Cannot display image: %s" (cdr err)) - nil)) + (progn + (image-toggle-display-image) + t) + (unknown-image-type + (image-mode-as-text) + (funcall + (if (called-interactively-p 'any) 'error 'message) + (if image-use-external-converter + "Unknown image type" + "Unknown image type; consider switching `image-use-external-converter' on")) + nil) + (error + (image-mode-as-text) + (funcall + (if (called-interactively-p 'any) 'error 'message) + "Cannot display image: %s" (cdr err)) + nil)) ;; If attempt to display the image fails. (if (not (image-get-display-property)) (error "Invalid image")) @@ -706,7 +718,7 @@ A non-mage major mode found from `auto-mode-alist' or fundamental mode displays an image file as text." ;; image-mode-as-text = normal-mode + image-minor-mode (let ((previous-image-type image-type)) ; preserve `image-type' - (major-mode-restore '(image-mode image-mode-maybe image-mode-as-text)) + (major-mode-restore '(image-mode image-mode-as-text)) ;; Restore `image-type' after `kill-all-local-variables' in `normal-mode'. (setq image-type previous-image-type) ;; Enable image minor mode with `C-c C-c'. @@ -756,8 +768,6 @@ on these modes." (if (image-get-display-property) "text" "an image or hex") "."))) -(define-obsolete-function-alias 'image-mode-maybe 'image-mode "23.2") - (defun image-toggle-display-text () "Show the image file as text. Remove text properties that display the image." @@ -816,13 +826,21 @@ was inserted." (- (nth 2 edges) (nth 0 edges)))) (max-height (when edges (- (nth 3 edges) (nth 1 edges)))) - (type (if (image--imagemagick-wanted-p filename) - 'imagemagick - (image-type file-or-data nil data-p))) (inhibit-read-only t) (buffer-undo-list t) (modified (buffer-modified-p)) - props image) + props image type) + + ;; If the data in the current buffer isn't from an existing file, + ;; but we have a file name (this happens when visiting images from + ;; a zip file, for instance), provide a type hint based on the + ;; suffix. + (when (and data-p filename) + (setq data-p (intern (format "image/%s" + (file-name-extension filename))))) + (setq type (if (image--imagemagick-wanted-p filename) + 'imagemagick + (image-type file-or-data nil data-p))) ;; Get the rotation data from the file, if any. (when (zerop image-transform-rotation) ; don't reset modified value @@ -839,10 +857,13 @@ was inserted." ;; :scale 1: If we do not set this, create-image will apply ;; default scaling based on font size. (setq image (if (not edges) - (create-image file-or-data type data-p :scale 1) + (create-image file-or-data type data-p :scale 1 + :format (and filename data-p)) (create-image file-or-data type data-p :scale 1 :max-width max-width - :max-height max-height))) + :max-height max-height + ;; Type hint. + :format (and filename data-p)))) ;; Discard any stale image data before looking it up again. (image-flush image) @@ -1072,28 +1093,87 @@ replacing the current Image mode buffer." (error "The buffer is not in Image mode")) (unless buffer-file-name (error "The current image is not associated with a file")) - (let* ((file (file-name-nondirectory buffer-file-name)) - (images (image-mode--images-in-directory file)) - (idx 0)) - (catch 'image-visit-next-file - (dolist (f images) - (if (string= f file) - (throw 'image-visit-next-file (1+ idx))) - (setq idx (1+ idx)))) - (setq idx (mod (+ idx (or n 1)) (length images))) - (let ((image (nth idx images)) - (dir (file-name-directory buffer-file-name))) - (find-alternate-file image) - ;; If we have dired buffer(s) open to where this image is, then - ;; place point on it. + (let ((next (image-mode--next-file buffer-file-name n))) + (unless next + (user-error "No %s file in this directory" + (if (> n 0) + "next" + "prev"))) + (if (stringp next) + (find-alternate-file next) + (funcall next)))) + +(defun image-mode--directory-buffers (file) + "Return a alist of type/buffer for all \"parent\" buffers to image FILE. +This is normally a list of dired buffers, but can also be archive and +tar mode buffers." + (let ((buffers nil) + (dir (file-name-directory file))) + (cond + ((and (boundp 'tar-superior-buffer) + tar-superior-buffer) + (when (buffer-live-p tar-superior-buffer) + (push (cons 'tar tar-superior-buffer) buffers))) + ((and (boundp 'archive-superior-buffer) + archive-superior-buffer) + (when (buffer-live-p archive-superior-buffer) + (push (cons 'archive archive-superior-buffer) buffers))) + (t + ;; Find a dired buffer. (dolist (buffer (buffer-list)) - (with-current-buffer buffer - (when (and (derived-mode-p 'dired-mode) + (with-current-buffer buffer + (when (and (derived-mode-p 'dired-mode) (equal (file-truename dir) (file-truename default-directory))) - (save-window-excursion - (switch-to-buffer (current-buffer) t t) - (dired-goto-file (expand-file-name image dir))))))))) + (push (cons 'dired (current-buffer)) buffers)))) + ;; If we can't find any buffers to navigate in, we open a dired + ;; buffer. + (unless buffers + (push (cons 'dired (find-file-noselect dir)) buffers) + (message "Opened a dired buffer on %s" dir)))) + buffers)) + +(declare-function archive-next-file-displayer "arc-mode") +(declare-function tar-next-file-displayer "tar-mode") + +(defun image-mode--next-file (file n) + "Go to the next image file in the parent buffer of FILE. +This is typically a dired buffer, but may also be a tar/archive buffer. +Return the next image file from that buffer. +If N is negative, go to the previous file." + (let ((regexp (image-file-name-regexp)) + (buffers (image-mode--directory-buffers file)) + next) + (dolist (buffer buffers) + ;; We do this traversal for all the dired buffers open on this + ;; directory. There probably is just one, but we want to move + ;; point in all of them. + (save-window-excursion + (switch-to-buffer (cdr buffer) t t) + (cl-case (car buffer) + ('dired + (dired-goto-file file) + (let (found) + (while (and (not found) + ;; Stop if we reach the end/start of the buffer. + (if (> n 0) + (not (eobp)) + (not (bobp)))) + (dired-next-line n) + (let ((candidate (dired-get-filename nil t))) + (when (and candidate + (string-match-p regexp candidate)) + (setq found candidate)))) + (if found + (setq next found) + ;; If we didn't find a next/prev file, then restore + ;; point. + (dired-goto-file file)))) + ('archive + (setq next (archive-next-file-displayer file regexp n))) + ('tar + (setq next (tar-next-file-displayer file regexp n)))))) + next)) (defun image-previous-file (&optional n) "Visit the preceding image in the same directory as the current file. |