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