PageRenderTime 58ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/nxhtml/util/inlimg.el

http://github.com/spastorino/my_emacs_for_rails
Emacs Lisp | 423 lines | 330 code | 40 blank | 53 comment | 7 complexity | 79917333dc75858739c7cceb0f77f3a4 MD5 | raw file
Possible License(s): GPL-2.0
  1. ;;; inlimg.el --- Display images inline
  2. ;;
  3. ;; Author: Lennart Borgman (lennart O borgman A gmail O com)
  4. ;; Created: 2008-09-27
  5. (defconst inlimg:version "0.7") ;; Version:
  6. ;; Last-Updated: 2009-07-14 Tue
  7. ;; URL:
  8. ;; Keywords:
  9. ;; Compatibility:
  10. ;;
  11. ;; Features that might be required by this library:
  12. ;;
  13. ;;
  14. ;;
  15. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  16. ;;
  17. ;;; Commentary:
  18. ;;
  19. ;; Display images inline. See `inlimg-mode' for more information.
  20. ;;
  21. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  22. ;;
  23. ;;; Change log:
  24. ;;
  25. ;;
  26. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  27. ;;
  28. ;; This program is free software; you can redistribute it and/or
  29. ;; modify it under the terms of the GNU General Public License as
  30. ;; published by the Free Software Foundation; either version 2, or
  31. ;; (at your option) any later version.
  32. ;;
  33. ;; This program is distributed in the hope that it will be useful,
  34. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  35. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  36. ;; General Public License for more details.
  37. ;;
  38. ;; You should have received a copy of the GNU General Public License
  39. ;; along with this program; see the file COPYING. If not, write to
  40. ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
  41. ;; Floor, Boston, MA 02110-1301, USA.
  42. ;;
  43. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  44. ;;
  45. ;;; Code:
  46. (eval-when-compile (require 'cl))
  47. (eval-when-compile (require 'mumamo nil t))
  48. (eval-when-compile (require 'ourcomments-util nil t))
  49. (defvar inlimg-assoc-ext
  50. '((png (".png"))
  51. (gif (".gif"))
  52. (tiff (".tiff"))
  53. (jpeg (".jpg" ".jpeg"))
  54. (xpm (".xpm"))
  55. (xbm (".xbm"))
  56. (pbm (".pbm"))))
  57. (defvar inlimg-img-regexp nil)
  58. (make-variable-buffer-local 'inlimg-img-regexp)
  59. (put 'inlimg-img-regexp 'permanent-local t)
  60. (defvar inlimg-img-regexp-html
  61. (rx (or (and "<img"
  62. (1+ space)
  63. (0+ (1+ (not (any " <>")))
  64. (1+ space))
  65. "src=\""
  66. (group (1+ (not (any "\""))))
  67. "\""
  68. (*? anything)
  69. "/>")
  70. (and "url("
  71. ?\"
  72. (group (1+ (not (any "\)"))))
  73. ?\"
  74. ")"
  75. )
  76. (and "url("
  77. (group (+? (not (any ")"))))
  78. ")"
  79. )
  80. )))
  81. (defvar inlimg-img-regexp-org
  82. (rx-to-string
  83. `(and "[[file:"
  84. (group (+? (not (any "\]")))
  85. ,(let ((types nil))
  86. (dolist (typ image-types)
  87. (when (image-type-available-p typ)
  88. (dolist (ext (cadr (assoc typ inlimg-assoc-ext)))
  89. (setq types (cons ext types)))))
  90. (cons 'or types)))
  91. "]"
  92. (optional "["
  93. (+? (not (any "\]")))
  94. "]")
  95. "]"
  96. )))
  97. (defconst inlimg-modes-img-values
  98. '(
  99. (html-mode inlimg-img-regexp-html)
  100. (org-mode inlimg-img-regexp-org)
  101. ))
  102. (defun inlimg-img-spec-p (spec)
  103. (assoc spec inlimg-modes-img-values))
  104. (defgroup inlimg nil
  105. "Customization group for inlimg."
  106. :group 'nxhtml)
  107. (defcustom inlimg-margins '(50 . 5)
  108. "Margins when displaying image."
  109. :type '(cons (integer :tag "Left margin")
  110. (integer :tag "Top margin"))
  111. :set (lambda (sym val)
  112. (set-default sym val)
  113. (when (fboundp 'inlimg-update-all-buffers)
  114. (inlimg-update-all-buffers)))
  115. :group 'inlimg)
  116. (defcustom inlimg-slice '(0 0 400 100)
  117. "How to slice images."
  118. :type '(choice (const :tag "Show whole images" nil)
  119. (list :tag "Show slice of image"
  120. (integer :tag "Top")
  121. (integer :tag "Left")
  122. (integer :tag "Width")
  123. (integer :tag "Height")))
  124. :set (lambda (sym val)
  125. (set-default sym val)
  126. (when (fboundp 'inlimg-update-all-buffers)
  127. (inlimg-update-all-buffers)))
  128. :group 'inlimg)
  129. (define-widget 'inlimg-spec-widget 'symbol
  130. "An inline image specification."
  131. :complete-function (lambda ()
  132. (interactive)
  133. (lisp-complete-symbol 'inlimg-img-spec-p))
  134. :prompt-match 'inlimg-img-spec-p
  135. :prompt-history 'widget-function-prompt-value-history
  136. :match-alternatives '(inlimg-img-spec-p)
  137. :validate (lambda (widget)
  138. (unless (inlimg-img-spec-p (widget-value widget))
  139. (widget-put widget :error (format "Invalid function: %S"
  140. (widget-value widget)))
  141. widget))
  142. :value 'org-mode
  143. :tag "Inlimg image values spec name")
  144. ;; (customize-option 'inlimg-mode-specs)
  145. (defcustom inlimg-mode-specs
  146. '(
  147. (xml-mode html-mode)
  148. (sgml-mode html-mode)
  149. (nxml-mode html-mode)
  150. (php-mode html-mode)
  151. (css-mode html-mode)
  152. )
  153. "Equivalent mode for image tag search.
  154. Note that derived modes \(see info) are recognized by default.
  155. To add new image tag patterns modify `inlimg-modes-img-values'."
  156. :type '(repeat
  157. (list (major-mode-function :tag "Major mode")
  158. (inlimg-spec-widget :tag "Use tags as specified in")))
  159. :group 'inlimg)
  160. (defface inlimg-img-tag '((t :inherit 'lazy-highlight))
  161. "Face added to img tag when displaying image."
  162. :group 'inlimg)
  163. (defface inlimg-img-remote '((t :inherit 'isearch-fail))
  164. "Face used for notes telling image is remote."
  165. :group 'inlimg)
  166. (defface inlimg-img-missing '((t :inherit 'trailing-whitespace))
  167. "Face used for notes telling image is missing."
  168. :group 'inlimg)
  169. (defvar inlimg-img-keymap
  170. (let ((map (make-sparse-keymap)))
  171. (define-key map [(control ?c) ?+] 'inlimg-toggle-display)
  172. (define-key map [(control ?c) ?%] 'inlimg-toggle-slicing)
  173. map)
  174. "Keymap on image overlay.")
  175. (eval-after-load 'gimp
  176. '(gimp-add-point-bindings inlimg-img-keymap))
  177. (defsubst inlimg-ovl-p (ovl)
  178. "Return non-nil if OVL is an inlimg image overlay."
  179. (overlay-get ovl 'inlimg-img))
  180. (defun inlimg-ovl-valid-p (ovl)
  181. (and (overlay-get ovl 'inlimg-img)
  182. (save-match-data
  183. (let ((here (point)))
  184. (goto-char (overlay-start ovl))
  185. (prog1
  186. (looking-at (symbol-value inlimg-img-regexp))
  187. (goto-char here))))))
  188. (defun inlimg-next (pt display-image)
  189. "Display or hide next image after point PT.
  190. If DISPLAY-IMAGE is non-nil then display image, otherwise hide it.
  191. Return non-nil if an img tag was found."
  192. (let (src dir beg end img ovl remote beg-face)
  193. (goto-char pt)
  194. (save-match-data
  195. (when (re-search-forward (symbol-value inlimg-img-regexp) nil t)
  196. (setq src (or (match-string-no-properties 1)
  197. (match-string-no-properties 2)
  198. (match-string-no-properties 3)))
  199. (setq beg (match-beginning 0))
  200. (setq beg-face (get-text-property beg 'face))
  201. (setq remote (string-match "^https?://" src))
  202. (setq end (- (line-end-position) 0))
  203. (setq ovl (catch 'old-ovl
  204. (dolist (ovl (overlays-at beg))
  205. (when (inlimg-ovl-p ovl)
  206. (throw 'old-ovl ovl)))
  207. nil))
  208. (unless ovl
  209. (setq ovl (make-overlay beg end))
  210. (overlay-put ovl 'inlimg-img t)
  211. (overlay-put ovl 'priority 100)
  212. (overlay-put ovl 'face 'inlimg-img-tag)
  213. (overlay-put ovl 'keymap inlimg-img-keymap))
  214. (overlay-put ovl 'image-file src)
  215. (overlay-put ovl 'inlimg-slice inlimg-slice)
  216. (if display-image
  217. (unless (memq beg-face '(font-lock-comment-face font-lock-string-face))
  218. (unless remote
  219. (setq dir (if (buffer-file-name)
  220. (file-name-directory (buffer-file-name))
  221. default-directory))
  222. (setq src (expand-file-name src dir)))
  223. (if (or remote (not (file-exists-p src)))
  224. (setq img (propertize
  225. (if remote " Image is on the web " " Image not found ")
  226. 'face (if remote 'inlimg-img-remote 'inlimg-img-missing)))
  227. (setq img (create-image src nil nil
  228. :relief 5
  229. :margin inlimg-margins))
  230. (setq img (inlimg-slice-img img inlimg-slice)))
  231. (let ((str (copy-sequence "\nX")))
  232. (setq str (propertize str 'face 'inlimg-img-tag))
  233. (put-text-property 1 2 'display img str)
  234. (overlay-put ovl 'after-string str)))
  235. (overlay-put ovl 'after-string nil))))
  236. ovl))
  237. (defun inlimg-slice-img (img slice)
  238. (if (not slice)
  239. img
  240. (let* ((sizes (image-size img t))
  241. (width (car sizes))
  242. (height (cdr sizes))
  243. (sl-left (nth 0 slice))
  244. (sl-top (nth 1 slice))
  245. (sl-width (nth 2 slice))
  246. (sl-height (nth 3 slice)))
  247. (when (> sl-left width) (setq sl-left 0))
  248. (when (> (+ sl-left sl-width) width) (setq sl-width (- width sl-left)))
  249. (when (> sl-top height) (setq sl-top 0))
  250. (when (> (+ sl-top sl-height) height) (setq sl-height (- height sl-top)))
  251. (setq img (list img))
  252. (setq img (cons (append '(slice)
  253. slice
  254. (list sl-top sl-left sl-width sl-height)
  255. nil)
  256. img)))))
  257. ;;;###autoload
  258. (define-minor-mode inlimg-mode
  259. "Display images inline.
  260. Search buffer for image tags. Display found images.
  261. Image tags are setup per major mode in `inlimg-mode-specs'.
  262. Images are displayed on a line below the tag referencing them.
  263. The whole image or a slice of it may be displayed, see
  264. `inlimg-slice'. Margins relative text are specified in
  265. `inlimg-margins'.
  266. See also the commands `inlimg-toggle-display' and
  267. `inlimg-toggle-slicing'.
  268. Note: This minor mode uses `font-lock-mode'."
  269. :keymap nil
  270. :group 'inlimg
  271. (if inlimg-mode
  272. (progn
  273. (let ((major-mode (or (and (boundp 'mumamo-multi-major-mode)
  274. mumamo-multi-major-mode
  275. (mumamo-main-major-mode))
  276. major-mode)))
  277. (add-hook 'font-lock-mode-hook 'inlimg-on-font-lock-off)
  278. (inlimg-get-buffer-img-values))
  279. (inlimg-font-lock t))
  280. (inlimg-font-lock nil)
  281. (inlimg-delete-overlays)))
  282. (put 'inlimg-mode 'permanent-local t)
  283. (defun inlimg-delete-overlays ()
  284. (save-restriction
  285. (widen)
  286. (let (ovl)
  287. (dolist (ovl (overlays-in (point-min) (point-max)))
  288. (when (inlimg-ovl-p ovl)
  289. (delete-overlay ovl))))))
  290. (defun inlimg-get-buffer-img-values ()
  291. (let* (rec
  292. (spec (or (catch 'spec
  293. (dolist (rec inlimg-mode-specs)
  294. (when (derived-mode-p (car rec))
  295. (throw 'spec (nth 1 rec)))))
  296. major-mode))
  297. (values (when spec (nth 1 (assoc spec inlimg-modes-img-values))))
  298. )
  299. (setq inlimg-img-regexp values)
  300. ))
  301. (defun inlimg--global-turn-on ()
  302. (inlimg-get-buffer-img-values)
  303. (when inlimg-img-regexp
  304. (inlimg-mode 1)))
  305. ;;;###autoload
  306. (define-globalized-minor-mode inlimg-global-mode inlimg-mode inlimg--global-turn-on)
  307. ;;;###autoload
  308. (defun inlimg-toggle-display (point)
  309. "Toggle display of image at point POINT.
  310. See also the command `inlimg-mode'."
  311. (interactive (list (point)))
  312. (let ((here (point))
  313. (ovl
  314. (catch 'ovl
  315. (dolist (ovl (overlays-at (point)))
  316. (when (inlimg-ovl-p ovl)
  317. (throw 'ovl ovl)))))
  318. is-displayed)
  319. (if (not ovl)
  320. (message "No image at point %s" here)
  321. (setq is-displayed (overlay-get ovl 'after-string))
  322. (inlimg-next (overlay-start ovl) (not is-displayed))
  323. (goto-char here))))
  324. ;;;###autoload
  325. (defun inlimg-toggle-slicing (point)
  326. "Toggle slicing of image at point POINT.
  327. See also the command `inlimg-mode'."
  328. (interactive (list (point)))
  329. (let* ((here (point))
  330. (ovl
  331. (catch 'ovl
  332. (dolist (ovl (overlays-at (point)))
  333. (when (inlimg-ovl-p ovl)
  334. (throw 'ovl ovl)))))
  335. (inlimg-slice inlimg-slice)
  336. is-displayed)
  337. (if (not ovl)
  338. (message "No image at point %s" here)
  339. (setq is-displayed (overlay-get ovl 'after-string))
  340. (when (overlay-get ovl 'inlimg-slice)
  341. (setq inlimg-slice nil))
  342. (inlimg-next (overlay-start ovl) is-displayed)
  343. (goto-char here))))
  344. (defun inlimg-font-lock-fun (bound)
  345. (let ((here (point))
  346. old-ovls new-ovls ovl)
  347. (goto-char (line-beginning-position))
  348. (dolist (ovl (overlays-in (point) bound))
  349. (when (inlimg-ovl-p ovl)
  350. (setq old-ovls (cons ovl old-ovls))))
  351. (while (and (< (point) bound)
  352. (setq ovl (inlimg-next (point) t)))
  353. (setq new-ovls (cons ovl new-ovls)))
  354. (dolist (ovl old-ovls)
  355. (unless (inlimg-ovl-valid-p ovl)
  356. (delete-overlay ovl)
  357. ))))
  358. ;; Fix-me: This stops working for changes with nxhtml-mumamo-mode, but
  359. ;; works for nxhtml-mode and html-mumamo-mode...
  360. (defvar inlimg-this-is-not-font-lock-off nil)
  361. (defun inlimg-font-lock (on)
  362. (let ((add-or-remove (if on 'font-lock-add-keywords 'font-lock-remove-keywords))
  363. (link-fun))
  364. (funcall add-or-remove nil
  365. `((inlimg-font-lock-fun
  366. 1
  367. mlinks-link
  368. prepend)))
  369. (let ((inlimg-this-is-not-font-lock-off t)
  370. (mumamo-multi-major-mode nil))
  371. (font-lock-mode -1)
  372. (font-lock-mode 1))))
  373. (defun inlimg-on-font-lock-off ()
  374. (unless (or inlimg-this-is-not-font-lock-off
  375. (and (boundp 'mumamo-multi-major-mode)
  376. mumamo-multi-major-mode))
  377. (when inlimg-mode
  378. (inlimg-mode -1)
  379. )))
  380. (put 'inlimg-on-font-lock-off 'permanent-local-hook t)
  381. (provide 'inlimg)
  382. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  383. ;;; inlimg.el ends here