Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

624 lines
23 KiB

4 years ago
  1. ;;; org-download.el --- Image drag-and-drop for Emacs org-mode. -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2014-2019 Free Software Foundation, Inc.
  3. ;; Author: Oleh Krehel
  4. ;; URL: https://github.com/abo-abo/org-download
  5. ;; Package-Version: 20191016.1227
  6. ;; Version: 0.1.0
  7. ;; Package-Requires: ((async "1.2"))
  8. ;; Keywords: images, screenshots, download
  9. ;; This file is not 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.
  14. ;; GNU Emacs is distributed in the hope that it will be useful,
  15. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;; GNU General Public License for more details.
  18. ;; You should have received a copy of the GNU General Public License
  19. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  20. ;;; Commentary:
  21. ;;
  22. ;; This extension facilitates moving images from point A to point B.
  23. ;;
  24. ;; Point A (the source) can be:
  25. ;; 1. An image inside your browser that you can drag to Emacs.
  26. ;; 2. An image on your file system that you can drag to Emacs.
  27. ;; 3. A local or remote image address in kill-ring.
  28. ;; Use the `org-download-yank' command for this.
  29. ;; Remember that you can use "0 w" in `dired' to get an address.
  30. ;; 4. An screenshot taken using `gnome-screenshot' or `scrot' or `gm'.
  31. ;; Use the `org-download-screenshot' command for this.
  32. ;; Customize the backend with `org-download-screenshot-method'.
  33. ;;
  34. ;; Point B (the target) is an Emacs `org-mode' buffer where the inline
  35. ;; link will be inserted. Several customization options will determine
  36. ;; where exactly on the file system the file will be stored.
  37. ;;
  38. ;; They are:
  39. ;; `org-download-method':
  40. ;; a. 'attach => use `org-mode' attachment machinery
  41. ;; b. 'directory => construct the directory in two stages:
  42. ;; 1. first part of the folder name is:
  43. ;; * either "." (current folder)
  44. ;; * or `org-download-image-dir' (if it's not nil).
  45. ;; `org-download-image-dir' becomes buffer-local when set,
  46. ;; so each file can customize this value, e.g with:
  47. ;; # -*- mode: Org; org-download-image-dir: "~/Pictures/foo"; -*-
  48. ;; 2. second part is:
  49. ;; * `org-download-heading-lvl' is nil => ""
  50. ;; * `org-download-heading-lvl' is n => the name of current
  51. ;; heading with level n. Level count starts with 0,
  52. ;; i.e. * is 0, ** is 1, *** is 2 etc.
  53. ;; `org-download-heading-lvl' becomes buffer-local when set,
  54. ;; so each file can customize this value, e.g with:
  55. ;; # -*- mode: Org; org-download-heading-lvl: nil; -*-
  56. ;;
  57. ;; `org-download-timestamp':
  58. ;; optionally add a timestamp to the file name.
  59. ;;
  60. ;; Customize `org-download-backend' to choose between `url-retrieve'
  61. ;; (the default) or `wget' or `curl'.
  62. ;;
  63. ;;; Code:
  64. (eval-when-compile
  65. (require 'cl))
  66. (require 'async)
  67. (require 'url-parse)
  68. (require 'url-http)
  69. (require 'org)
  70. (defgroup org-download nil
  71. "Image drag-and-drop for org-mode."
  72. :group 'org
  73. :prefix "org-download-")
  74. (defcustom org-download-method 'directory
  75. "The way images should be stored."
  76. :type '(choice
  77. (const :tag "Directory" directory)
  78. (const :tag "Attachment" attach)
  79. (function :tag "Custom function")))
  80. (defcustom org-download-image-dir nil
  81. "If set, images will be stored in this directory instead of \".\".
  82. See `org-download--dir-1' for more info."
  83. :type '(choice
  84. (const :tag "Default" nil)
  85. (string :tag "Directory")))
  86. (make-variable-buffer-local 'org-download-image-dir)
  87. (defcustom org-download-heading-lvl 0
  88. "Heading level to be used in `org-download--dir-2'."
  89. :type 'integer)
  90. (make-variable-buffer-local 'org-download-heading-lvl)
  91. (defvar org-download-path-last-file nil
  92. "Variable to hold the full path of the last downloaded file.
  93. See `org-download-rename-last-file'.")
  94. (defcustom org-download-backend t
  95. "Method to use for downloading."
  96. :type '(choice
  97. (const :tag "wget" "wget \"%s\" -O \"%s\"")
  98. (const :tag "curl" "curl \"%s\" -o \"%s\"")
  99. (const :tag "url-retrieve" t)))
  100. (defcustom org-download-timestamp "%Y-%m-%d_%H-%M-%S_"
  101. "This `format-time-string'-style string will be appended to the file name.
  102. Set this to \"\" if you don't want time stamps."
  103. :type 'string)
  104. (defcustom org-download-img-regex-list
  105. '("<img +src=\"" "<img +\\(class=\"[^\"]+\"\\)? *src=\"")
  106. "This regex is used to unalias links that look like images.
  107. The html to which the links points will be searched for these
  108. regexes, one by one, until one succeeds. The found image address
  109. will be used."
  110. :type '(repeat string))
  111. (defcustom org-download-screenshot-method "gnome-screenshot -a -f %s"
  112. "The tool to capture screenshots."
  113. :type '(choice
  114. (const :tag "gnome-screenshot" "gnome-screenshot -a -f %s")
  115. (const :tag "scrot" "scrot -s %s")
  116. (const :tag "gm" "gm import %s")
  117. (const :tag "imagemagick/import" "import %s")
  118. (const :tag "imagemagick/import + xclip to save to clipboard"
  119. "export filename=\"%s\"; import png:\"$filename\" ;xclip -selection clipboard -target image/png -filter < \"$filename\" &>/dev/null")
  120. (const :tag "xfce4-screenshooter" "xfce4-screenshooter -r -o cat > %s")
  121. ;; screenshot method in ms-windows, /capture=4 stands for interactive.
  122. (const :tag "IrfanView" "i_view64 /capture=4 /convert=\"%s\"")
  123. ;; screenshot script in osx, -i stands for interactive,
  124. ;; press space key to toggle between selection and
  125. ;; window/application mode.
  126. (const :tag "screencapture" "screencapture -i %s")
  127. ;; take an image that is already on the clipboard, for Linux
  128. (const :tag "xclip"
  129. "xclip -selection clipboard -t image/png -o > %s")
  130. ;; take an image that is already on the clipboard, for Windows
  131. (const :tag "imagemagick/convert" "convert clipboard: %s")
  132. (function :tag "Custom function")))
  133. (defcustom org-download-screenshot-file (expand-file-name "screenshot.png" temporary-file-directory)
  134. "The file to capture screenshots."
  135. :type 'string)
  136. (defcustom org-download-image-html-width 0
  137. "When non-zero add #+attr_html: :width tag to the image."
  138. :type 'integer)
  139. (defcustom org-download-image-latex-width 0
  140. "When non-zero add #+attr_latex: :width tag to the image."
  141. :type 'integer)
  142. (defcustom org-download-image-org-width 0
  143. "When non-zero add #+attr_org: :width tag to the image."
  144. :type 'integer)
  145. (defcustom org-download-image-attr-list nil
  146. "Add attr info to the image.
  147. For example:
  148. (\"#+attr_html: :width 80% :align center\"
  149. \"#+attr_org: :width 100px\")"
  150. :type '(repeat string))
  151. (defcustom org-download-delete-image-after-download nil
  152. "When non-nil delete local image after download."
  153. :type 'boolean)
  154. (defcustom org-download-display-inline-images t
  155. "When non-nil display inline images in org buffer after download."
  156. :type
  157. '(choice
  158. (const :tag "On" t)
  159. (const :tag "Off" nil)
  160. (const :tag "Posframe" posframe)))
  161. (defvar org-download-posframe-show-params
  162. '(;; Please do not remove :timeout or set it to large.
  163. :timeout 1
  164. :internal-border-width 1
  165. :internal-border-color "red"
  166. :min-width 40
  167. :min-height 10
  168. :poshandler posframe-poshandler-window-center)
  169. "List of parameters passed to `posframe-show'.")
  170. (declare-function posframe-workable-p "ext:posframe")
  171. (declare-function posframe-show "ext:posframe")
  172. (defun org-download--display-inline-images ()
  173. (cond
  174. ((eq org-download-display-inline-images t)
  175. (org-display-inline-images))
  176. ((eq org-download-display-inline-images 'posframe)
  177. (require 'posframe)
  178. (when (posframe-workable-p)
  179. (let ((buffer (get-buffer-create " *org-download-image")))
  180. (with-current-buffer buffer
  181. (erase-buffer)
  182. (insert-image-file org-download-path-last-file))
  183. (apply #'posframe-show
  184. buffer
  185. org-download-posframe-show-params))))))
  186. (defun org-download-get-heading (lvl)
  187. "Return the heading of the current entry's LVL level parent."
  188. (save-excursion
  189. (let ((cur-lvl (org-current-level)))
  190. (if cur-lvl
  191. (progn
  192. (unless (= cur-lvl 1)
  193. (org-up-heading-all (- (1- (org-current-level)) lvl)))
  194. (replace-regexp-in-string
  195. " " "_"
  196. (nth 4 (org-heading-components))))
  197. ""))))
  198. (defun org-download--dir-1 ()
  199. "Return the first part of the directory path for `org-download--dir'.
  200. It's `org-download-image-dir', unless it's nil. Then it's \".\"."
  201. (or org-download-image-dir "."))
  202. (defun org-download--dir-2 ()
  203. "Return the second part of the directory path for `org-download--dir'.
  204. Unless `org-download-heading-lvl' is nil, it's the name of the current
  205. `org-download-heading-lvl'-leveled heading. Otherwise it's \"\"."
  206. (when org-download-heading-lvl
  207. (org-download-get-heading
  208. org-download-heading-lvl)))
  209. (defun org-download--dir ()
  210. "Return the directory path for image storage.
  211. The path is composed from `org-download--dir-1' and `org-download--dir-2'.
  212. The directory is created if it didn't exist before."
  213. (if (eq major-mode 'org-mode)
  214. (let* ((part1 (org-download--dir-1))
  215. (part2 (org-download--dir-2))
  216. (dir (if part2
  217. (format "%s/%s" part1 part2)
  218. part1)))
  219. (unless (file-exists-p dir)
  220. (make-directory dir t))
  221. dir)
  222. default-directory))
  223. (defvar org-download-file-format-function #'org-download-file-format-default)
  224. (defun org-download--fullname (link &optional ext)
  225. "Return the file name where LINK will be saved to.
  226. It's affected by `org-download--dir'.
  227. EXT can hold the file extension, in case LINK doesn't provide it."
  228. (let ((filename
  229. (file-name-nondirectory
  230. (car (url-path-and-query
  231. (url-generic-parse-url link)))))
  232. (dir (org-download--dir)))
  233. (when (string-match ".*?\\.\\(?:png\\|jpg\\)\\(.*\\)$" filename)
  234. (setq filename (replace-match "" nil nil filename 1)))
  235. (when ext
  236. (setq filename (concat filename "." ext)))
  237. (abbreviate-file-name
  238. (expand-file-name
  239. (funcall org-download-file-format-function filename)
  240. dir))))
  241. (defun org-download-file-format-default (filename)
  242. "It's affected by `org-download-timestamp'."
  243. (concat
  244. (format-time-string org-download-timestamp)
  245. filename))
  246. (defun org-download--image (link filename)
  247. "Save LINK to FILENAME asynchronously and show inline images in current buffer."
  248. (when (string= "file" (url-type (url-generic-parse-url link)))
  249. (setq link (url-unhex-string (url-filename (url-generic-parse-url link)))))
  250. (cond ((and (not (file-remote-p link))
  251. (file-exists-p link))
  252. (copy-file link (expand-file-name filename)))
  253. ((eq org-download-backend t)
  254. (org-download--image/url-retrieve link filename))
  255. (t
  256. (org-download--image/command org-download-backend link filename))))
  257. (defun org-download--image/command (command link filename)
  258. "Using COMMAND, save LINK to FILENAME.
  259. COMMAND is a format-style string with two slots for LINK and FILENAME."
  260. (async-start
  261. `(lambda () (shell-command
  262. ,(format command link
  263. (expand-file-name filename))))
  264. (lexical-let ((cur-buf (current-buffer)))
  265. (lambda (_x)
  266. (with-current-buffer cur-buf
  267. (org-download--display-inline-images))))))
  268. (defun org-download--write-image (status filename)
  269. ;; Write current buffer to FILENAME
  270. (let ((err (plist-get status :error)))
  271. (when err
  272. (error
  273. "HTTP error %s"
  274. (downcase (nth 2 (assq (nth 2 err) url-http-codes))))))
  275. (delete-region
  276. (point-min)
  277. (progn
  278. (re-search-forward "\n\n" nil 'move)
  279. (point)))
  280. (let ((coding-system-for-write 'no-conversion))
  281. (write-region nil nil filename nil nil nil 'confirm)))
  282. (defun org-download--image/url-retrieve (link filename)
  283. "Save LINK to FILENAME using `url-retrieve'."
  284. (let ((mode major-mode))
  285. (url-retrieve
  286. link
  287. (lambda (status filename buffer)
  288. (org-download--write-image status filename)
  289. (cond ((eq mode 'org-mode)
  290. (with-current-buffer buffer
  291. (org-download--display-inline-images)))
  292. ((eq mode 'dired-mode)
  293. (let ((inhibit-message t))
  294. (with-current-buffer (dired (file-name-directory filename))
  295. (revert-buffer nil t))))))
  296. (list
  297. (expand-file-name filename)
  298. (current-buffer))
  299. nil t)))
  300. (defun org-download-yank ()
  301. "Call `org-download-image' with current kill."
  302. (interactive)
  303. (org-download-image
  304. (replace-regexp-in-string "\n+$" "" (current-kill 0))))
  305. (defun org-download-screenshot ()
  306. "Capture screenshot and insert the resulting file.
  307. The screenshot tool is determined by `org-download-screenshot-method'."
  308. (interactive)
  309. (let ((default-directory "~"))
  310. (make-directory (file-name-directory org-download-screenshot-file) t)
  311. (if (functionp org-download-screenshot-method)
  312. (funcall org-download-screenshot-method
  313. org-download-screenshot-file)
  314. (shell-command-to-string
  315. (format org-download-screenshot-method
  316. org-download-screenshot-file))))
  317. (org-download-image org-download-screenshot-file))
  318. (declare-function org-attach-dir "org-attach")
  319. (declare-function org-attach-attach "org-attach")
  320. (declare-function org-attach-sync "org-attach")
  321. (defun org-download-annotate-default (link)
  322. "Annotate LINK with the time of download."
  323. (format "#+DOWNLOADED: %s @ %s\n"
  324. (if (equal link org-download-screenshot-file)
  325. "screenshot"
  326. link)
  327. (format-time-string "%Y-%m-%d %H:%M:%S")))
  328. (defvar org-download-annotate-function
  329. #'org-download-annotate-default
  330. "Function that takes LINK and returns a string.
  331. It's inserted before the image link and is used to annotate it.")
  332. (defvar org-download-link-format
  333. "[[file:%s]]\n"
  334. "Format of the file link to insert.")
  335. (defun org-download-image (link)
  336. "Save image at address LINK to `org-download--dir'."
  337. (interactive "sUrl: ")
  338. (let (ext)
  339. (unless (image-type-from-file-name link)
  340. (with-current-buffer (url-retrieve-synchronously link t)
  341. (cond ((let ((regexes org-download-img-regex-list)
  342. lnk)
  343. (while (and (not lnk) regexes)
  344. (goto-char (point-min))
  345. (when (re-search-forward (pop regexes) nil t)
  346. (backward-char)
  347. (setq lnk (read (current-buffer)))))
  348. (when lnk
  349. (setq link lnk))))
  350. ((progn
  351. (goto-char (point-min))
  352. (when (re-search-forward "^Content-Type: image/\\(.*\\)$")
  353. (setq ext (match-string 1)))))
  354. (t
  355. (error "link %s does not point to an image; unaliasing failed" link)))))
  356. (let ((filename
  357. (cond ((eq org-download-method 'attach)
  358. (let ((org-download-image-dir (progn (require 'org-attach)
  359. (org-attach-dir t)))
  360. org-download-heading-lvl)
  361. (org-download--fullname link ext)))
  362. ((fboundp org-download-method)
  363. (funcall org-download-method link))
  364. (t
  365. (org-download--fullname link ext)))))
  366. (setq org-download-path-last-file filename)
  367. (when (image-type-from-file-name filename)
  368. (org-download--image link filename)
  369. (when (eq major-mode 'org-mode)
  370. (when (eq org-download-method 'attach)
  371. (org-attach-attach filename nil 'none))
  372. (org-download-insert-link link filename))
  373. (when (and (eq org-download-delete-image-after-download t)
  374. (not (url-handler-file-remote-p (current-kill 0))))
  375. (delete-file link delete-by-moving-to-trash))))))
  376. (defun org-download-rename-at-point ()
  377. "Rename image at point."
  378. (interactive)
  379. (let* ((dir-path (org-download--dir))
  380. (current-name (file-name-nondirectory
  381. (org-element-property :path (org-element-context))))
  382. (current-path (concat dir-path "/" current-name))
  383. (ext (file-name-extension current-name))
  384. (new-name (read-string "Rename file at point to: " (file-name-sans-extension current-name)))
  385. (new-path (concat dir-path "/" new-name "." ext)))
  386. (rename-file current-path new-path)
  387. (message "File successfully renamed...")
  388. (org-download-replace-all current-name (concat new-name "." ext))))
  389. (defun org-download-rename-last-file ()
  390. "Rename the last downloaded file saved in your computer."
  391. (interactive)
  392. (let* ((dir-path (org-download--dir))
  393. (newname (read-string "Rename last file to: " (file-name-base org-download-path-last-file)))
  394. (ext (file-name-extension org-download-path-last-file))
  395. (newpath (concat dir-path "/" newname "." ext)))
  396. (when org-download-path-last-file
  397. (rename-file org-download-path-last-file newpath 1)
  398. (org-download-replace-all
  399. (file-name-nondirectory org-download-path-last-file)
  400. (concat newname "." ext))
  401. (setq org-download-path-last-file newpath)
  402. (org-download--display-inline-images))))
  403. (defun org-download-replace-all (oldpath newpath)
  404. "Function to search for the OLDPATH inside the buffer and replace it by the NEWPATH."
  405. (save-excursion
  406. (goto-char (point-min))
  407. (while (re-search-forward oldpath nil t)
  408. (replace-match newpath))))
  409. (defcustom org-download-abbreviate-filename-function #'file-relative-name
  410. "Function that takes FILENAME and returns an abbreviated file name."
  411. :type '(choice
  412. (const :tag "relative" file-relative-name)
  413. (const :tag "absolute" expand-file-name)))
  414. (defun org-download-insert-link (link filename)
  415. (let* ((beg (point))
  416. (line-beg (line-beginning-position))
  417. (indent (- beg line-beg))
  418. (in-item-p (org-in-item-p))
  419. str)
  420. (if (looking-back "^[ \t]+" line-beg)
  421. (delete-region (match-beginning 0) (match-end 0))
  422. (newline))
  423. (insert (funcall org-download-annotate-function link))
  424. (dolist (attr org-download-image-attr-list)
  425. (insert attr "\n"))
  426. (insert (if (= org-download-image-html-width 0)
  427. ""
  428. (format "#+attr_html: :width %dpx\n" org-download-image-html-width)))
  429. (insert (if (= org-download-image-latex-width 0)
  430. ""
  431. (format "#+attr_latex: :width %dcm\n" org-download-image-latex-width)))
  432. (insert (if (= org-download-image-org-width 0)
  433. ""
  434. (format "#+attr_org: :width %dpx\n" org-download-image-org-width)))
  435. (insert
  436. (format org-download-link-format
  437. (org-link-escape
  438. (funcall org-download-abbreviate-filename-function filename))))
  439. (org-download--display-inline-images)
  440. (setq str (buffer-substring-no-properties line-beg (point)))
  441. (when in-item-p
  442. (indent-region line-beg (point) indent))
  443. str))
  444. (defun org-download--at-comment-p ()
  445. "Check if current line begins with #+DOWLOADED:."
  446. (save-excursion
  447. (move-beginning-of-line nil)
  448. (looking-at "#\\+DOWNLOADED:")))
  449. (defun org-download-delete ()
  450. "Delete inline image link on current line, and the file that it points to."
  451. (interactive)
  452. (cond ((org-download--at-comment-p)
  453. (delete-region (line-beginning-position)
  454. (line-end-position))
  455. (org-download--delete (line-beginning-position)
  456. nil
  457. 1))
  458. ((region-active-p)
  459. (org-download--delete (region-beginning)
  460. (region-end))
  461. (delete-region (region-beginning)
  462. (region-end)))
  463. ((looking-at org-any-link-re)
  464. (let ((fname (org-link-unescape
  465. (match-string-no-properties 2))))
  466. (when (file-exists-p fname)
  467. (delete-file fname)
  468. (delete-region (match-beginning 0)
  469. (match-end 0))
  470. (when (eolp)
  471. (delete-char 1)))))
  472. (t (org-download--delete (line-beginning-position)
  473. (line-end-position))))
  474. (when (eq org-download-method 'attach)
  475. (org-attach-sync)))
  476. (defcustom org-download-edit-cmd "gimp %s"
  477. "Command for editing an image link."
  478. :type 'string)
  479. (defun org-download-edit ()
  480. "Open the image at point for editing."
  481. (interactive)
  482. (let ((context (org-element-context)))
  483. (if (not (eq (car-safe context) 'link))
  484. (user-error "not on a link")
  485. (start-process-shell-command
  486. "org-download-edit"
  487. "org-download-edit"
  488. (format org-download-edit-cmd
  489. (shell-quote-wildcard-pattern
  490. (url-unhex-string (plist-get (cadr context) :path))))))))
  491. (defun org-download--delete (beg end &optional times)
  492. "Delete inline image links and the files they point to between BEG and END.
  493. When TIMES isn't nil, delete only TIMES links."
  494. (unless times
  495. (setq times most-positive-fixnum))
  496. (save-excursion
  497. (goto-char beg)
  498. (while (and (>= (decf times) 0)
  499. (re-search-forward "\\[\\[file:\\([^]]*\\)\\]\\]" end t))
  500. (let ((str (match-string-no-properties 1)))
  501. (delete-region beg
  502. (match-end 0))
  503. (when (file-exists-p str)
  504. (delete-file str))))))
  505. (defun org-download-dnd-fallback (uri action)
  506. (let ((dnd-protocol-alist
  507. (rassq-delete-all
  508. 'org-download-dnd
  509. (copy-alist dnd-protocol-alist))))
  510. (dnd-handle-one-url nil action uri)))
  511. (defun org-download-dnd (uri action)
  512. "When in `org-mode' and URI points to image, download it.
  513. Otherwise, pass URI and ACTION back to dnd dispatch."
  514. (cond ((eq major-mode 'org-mode)
  515. (condition-case nil
  516. (org-download-image uri)
  517. (error
  518. (org-download-dnd-fallback uri action))))
  519. ((eq major-mode 'dired-mode)
  520. (org-download-dired uri))
  521. ;; redirect to someone else
  522. (t
  523. (org-download-dnd-fallback uri action))))
  524. (defun org-download-dired (uri)
  525. "Download URI to current directory."
  526. (raise-frame)
  527. (org-download-image uri))
  528. (defun org-download-dnd-base64 (uri _action)
  529. (when (eq major-mode 'org-mode)
  530. (when (string-match "^data:image/png;base64," uri)
  531. (let* ((me (match-end 0))
  532. (filename (org-download--fullname
  533. (substring-no-properties uri me (+ me 10))
  534. "png")))
  535. (with-temp-buffer
  536. (insert (base64-decode-string (substring uri me)))
  537. (write-file filename))
  538. (org-download-insert-link filename filename)))))
  539. ;;;###autoload
  540. (defun org-download-enable ()
  541. "Enable org-download."
  542. (unless (eq (cdr (assoc "^\\(https?\\|ftp\\|file\\|nfs\\):" dnd-protocol-alist))
  543. 'org-download-dnd)
  544. (setq dnd-protocol-alist
  545. `(("^\\(https?\\|ftp\\|file\\|nfs\\):" . org-download-dnd)
  546. ("^data:" . org-download-dnd-base64)
  547. ,@dnd-protocol-alist))))
  548. (defun org-download-disable ()
  549. "Disable org-download."
  550. (rassq-delete-all 'org-download-dnd dnd-protocol-alist))
  551. (org-download-enable)
  552. (provide 'org-download)
  553. ;;; org-download.el ends here