Merge branch 'master' into comment-cache
[emacs.git] / lisp / image-file.el
blob33cea95d538cc4fc9cb105bd00132a7b3c45f163
1 ;;; image-file.el --- support for visiting image files
2 ;;
3 ;; Copyright (C) 2000-2017 Free Software Foundation, Inc.
4 ;;
5 ;; Author: Miles Bader <miles@gnu.org>
6 ;; Keywords: multimedia
8 ;; This file is part of GNU Emacs.
10 ;; GNU Emacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23 ;;; Commentary:
25 ;; Defines a file-name-handler hook that transforms visited (or
26 ;; inserted) image files so that they are displayed by Emacs as
27 ;; images. This is done by putting a `display' text-property on the
28 ;; image data, with the image-data still present underneath; if the
29 ;; resulting buffer file is saved to another name it will correctly save
30 ;; the image data to the new file.
32 ;;; Code:
34 (require 'image)
37 ;;;###autoload
38 (defcustom image-file-name-extensions
39 (purecopy '("png" "jpeg" "jpg" "gif" "tiff" "tif" "xbm" "xpm" "pbm" "pgm" "ppm" "pnm" "svg"))
40 "A list of image-file filename extensions.
41 Filenames having one of these extensions are considered image files,
42 in addition to those matching `image-file-name-regexps'.
44 See `auto-image-file-mode'; if `auto-image-file-mode' is enabled,
45 setting this variable directly does not take effect unless
46 `auto-image-file-mode' is re-enabled; this happens automatically when
47 the variable is set using \\[customize]."
48 :type '(repeat string)
49 :set (lambda (sym val)
50 (set-default sym val)
51 (when auto-image-file-mode
52 ;; Re-initialize the image-file handler
53 (auto-image-file-mode t)))
54 :initialize 'custom-initialize-default
55 :group 'image)
57 ;;;###autoload
58 (defcustom image-file-name-regexps nil
59 "List of regexps matching image-file filenames.
60 Filenames matching one of these regexps are considered image files,
61 in addition to those with an extension in `image-file-name-extensions'.
63 See function `auto-image-file-mode'; if `auto-image-file-mode' is
64 enabled, setting this variable directly does not take effect unless
65 `auto-image-file-mode' is re-enabled; this happens automatically when
66 the variable is set using \\[customize]."
67 :type '(repeat regexp)
68 :set (lambda (sym val)
69 (set-default sym val)
70 (when auto-image-file-mode
71 ;; Re-initialize the image-file handler
72 (auto-image-file-mode t)))
73 :initialize 'custom-initialize-default
74 :group 'image)
77 ;;;###autoload
78 (defun image-file-name-regexp ()
79 "Return a regular expression matching image-file filenames."
80 (let ((exts-regexp
81 (and image-file-name-extensions
82 (concat "\\."
83 (regexp-opt (nconc (mapcar #'upcase
84 image-file-name-extensions)
85 image-file-name-extensions)
87 "\\'"))))
88 (if image-file-name-regexps
89 (mapconcat 'identity
90 (if exts-regexp
91 (cons exts-regexp image-file-name-regexps)
92 image-file-name-regexps)
93 "\\|")
94 exts-regexp)))
97 ;;;###autoload
98 (defun insert-image-file (file &optional visit beg end replace)
99 "Insert the image file FILE into the current buffer.
100 Optional arguments VISIT, BEG, END, and REPLACE are interpreted as for
101 the command `insert-file-contents'."
102 (let ((rval
103 (image-file-call-underlying #'insert-file-contents-literally
104 'insert-file-contents
105 file visit beg end replace)))
106 ;; Turn the image data into a real image, but only if the whole file
107 ;; was inserted
108 (when (and (or (null beg) (zerop beg)) (null end))
109 (let* ((ibeg (point))
110 (iend (+ (point) (cadr rval)))
111 (visitingp (and visit (= ibeg (point-min)) (= iend (point-max))))
112 (data
113 (string-make-unibyte
114 (buffer-substring-no-properties ibeg iend)))
115 (image
116 (create-image data nil t))
117 (props
118 `(display ,image
119 yank-handler
120 (image-file-yank-handler nil t)
121 intangible ,image
122 rear-nonsticky (display intangible)
123 ;; This a cheap attempt to make the whole buffer
124 ;; read-only when we're visiting the file (as
125 ;; opposed to just inserting it).
126 ,@(and visitingp
127 '(read-only t front-sticky (read-only))))))
128 (add-text-properties ibeg iend props)
129 (when visitingp
130 ;; Inhibit the cursor when the buffer contains only an image,
131 ;; because cursors look very strange on top of images.
132 (setq cursor-type nil)
133 ;; This just makes the arrow displayed in the right fringe
134 ;; area look correct when the image is wider than the window.
135 (setq truncate-lines t))))
136 rval))
138 ;; We use a yank-handler to make yanked images unique, so that
139 ;; yanking two copies of the same image next to each other are
140 ;; recognized as two different images.
141 (defun image-file-yank-handler (string)
142 "Yank handler for inserting an image into a buffer."
143 (let ((len (length string))
144 (image (get-text-property 0 'display string)))
145 (remove-text-properties 0 len yank-excluded-properties string)
146 (if (consp image)
147 (add-text-properties 0
148 (or (next-single-property-change 0 'image-counter string)
149 (length string))
150 `(display
151 ,(cons (car image) (cdr image))
152 yank-handler
153 ,(cons 'image-file-yank-handler '(nil t)))
154 string))
155 (insert string)))
157 (put 'image-file-handler 'safe-magic t)
158 (defun image-file-handler (operation &rest args)
159 "Filename handler for inserting image files.
160 OPERATION is the operation to perform, on ARGS.
161 See `file-name-handler-alist' for details."
162 (if (and (eq operation 'insert-file-contents)
163 auto-image-file-mode)
164 (apply #'insert-image-file args)
165 ;; We don't handle OPERATION, use another handler or the default
166 (apply #'image-file-call-underlying operation operation args)))
168 (defun image-file-call-underlying (function operation &rest args)
169 "Call FUNCTION with `image-file-handler' and OPERATION inhibited.
170 Optional argument ARGS are the arguments to call FUNCTION with."
171 (let ((inhibit-file-name-handlers
172 (cons 'image-file-handler
173 (and (eq inhibit-file-name-operation operation)
174 inhibit-file-name-handlers)))
175 (inhibit-file-name-operation operation))
176 (apply function args)))
179 ;;;###autoload
180 (define-minor-mode auto-image-file-mode
181 "Toggle visiting of image files as images (Auto Image File mode).
182 With a prefix argument ARG, enable Auto Image File mode if ARG is
183 positive, and disable it otherwise. If called from Lisp, enable
184 the mode if ARG is omitted or nil.
186 An image file is one whose name has an extension in
187 `image-file-name-extensions', or matches a regexp in
188 `image-file-name-regexps'."
189 :global t
190 :group 'image
191 ;; Remove existing handler
192 (let ((existing-entry
193 (rassq 'image-file-handler file-name-handler-alist)))
194 (when existing-entry
195 (setq file-name-handler-alist
196 (delq existing-entry file-name-handler-alist))))
197 ;; Add new handler, if enabled
198 (when auto-image-file-mode
199 (push (cons (image-file-name-regexp) 'image-file-handler)
200 file-name-handler-alist)))
203 (provide 'image-file)
205 ;;; image-file.el ends here