emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
@ 2023-09-22 14:52 ` Visuwesh
  2023-09-22 16:51   ` Max Nikulin
                     ` (3 more replies)
  0 siblings, 4 replies; 49+ messages in thread
From: Visuwesh @ 2023-09-22 14:52 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 858 bytes --]

Attached patch adds yank-media and DND handler to attach files in the
clipboard and dropped onto an Emacs frame respectively.

The yank-media handler for images is well tested, I use it frequently
however, rest of the stuff aren't really tested since I don't use a GUI
file manager.  I tested enough to make sure the logic is right so I
don't know if they are ergonomic enough.  As noted in the comments,
files copied/cut to clipboard doesn't seem to have a solid spec and most
of the code was written with pcmanfm as the file manager, testing with
other file managers are highly welcome.

Better names for the newly added defcustoms are highly welcome since I
don't find them to be clear enough from their name.

The patch compiles cleanly without warnings but I haven't tested _this_
patch yet, I have these functions in my init.el and have tested those.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-support-for-yank-media-and-DND.patch --]
[-- Type: text/x-diff, Size: 9705 bytes --]

From b6f1315cbdc331f2f54e1801b03272915d344cfd Mon Sep 17 00:00:00 2001
From: Visuwesh <visuweshm@gmail.com>
Date: Fri, 22 Sep 2023 20:11:41 +0530
Subject: [PATCH] Add support for yank-media and DND

* lisp/org.el (org-mode): Call the setup function for yank-media and
DND.
(org-setup-yank-dnd-handlers): Register yank-media-handler and DND
handler.
(org-media-image-save-type, org-dnd-default-attach-method): New
defcustoms.
(org--image-yank-media-handler, org--copied-files-yank-media-handler)
(org--dnd-local-file-handler): Add yank-media and DND handlers.

* etc/ORG-NEWS: Advertise the new features.
---
 etc/ORG-NEWS |  16 ++++++
 lisp/org.el  | 153 +++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 164 insertions(+), 5 deletions(-)

diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 252c5a9..f193c54 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -596,6 +596,22 @@ return a matplotlib Figure object to plot. For output results, the
 current figure (as returned by =pyplot.gcf()=) is cleared before
 evaluation, and then plotted afterwards.
 
+*** Images and files in clipboard can be attached
+
+Org can now attach images in clipboard and files copied/cut to the
+clipboard from file managers using the ~yank-media~ command which also
+inserts a link to the attached file.
+
+Images can be saved to a separate directory instead of being attached,
+customize ~org-media-image-save-type~.
+
+*** Files and images can be attached by dropping onto Emacs
+
+Attachment method other than ~org-attach-method~ for dropped files can
+be specified using ~org-dnd-default-attach-method~.
+
+Images dropped also respect the value of ~org-media-image-save-type~.
+
 ** New functions and changes in function arguments
 *** =TYPES= argument in ~org-element-lineage~ can now be a symbol
 
diff --git a/lisp/org.el b/lisp/org.el
index d0b2355..5c66f04 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -4999,7 +4999,10 @@ The following commands are available:
   (org--set-faces-extend '(org-block-begin-line org-block-end-line)
                          org-fontify-whole-block-delimiter-line)
   (org--set-faces-extend org-level-faces org-fontify-whole-heading-line)
-  (setq-local org-mode-loading nil))
+  (setq-local org-mode-loading nil)
+
+  ;; `yank-media' handler and DND support.
+  (org-setup-yank-dnd-handlers))
 
 ;; Update `customize-package-emacs-version-alist'
 (add-to-list 'customize-package-emacs-version-alist
@@ -15125,20 +15128,20 @@ INCREMENT-STEP divisor."
 	(setq hour (mod hour 24))
 	(setq pos-match-group 1
               new (format "-%02d:%02d" hour minute)))
-       
+
        ((org-pos-in-match-range pos 6) ;; POS on "dmwy" repeater char.
 	(setq pos-match-group 6
               new (car (rassoc (+ nincrements (cdr (assoc (match-string 6 ts-string) idx))) idx))))
-       
+
        ((org-pos-in-match-range pos 5) ;; POS on X in "Xd" repeater.
 	(setq pos-match-group 5
               ;; Never drop below X=1.
               new (format "%d" (max 1 (+ nincrements (string-to-number (match-string 5 ts-string)))))))
-       
+
        ((org-pos-in-match-range pos 9) ;; POS on "dmwy" repeater in warning interval.
 	(setq pos-match-group 9
               new (car (rassoc (+ nincrements (cdr (assoc (match-string 9 ts-string) idx))) idx))))
-       
+
        ((org-pos-in-match-range pos 8) ;; POS on X in "Xd" in warning interval.
 	(setq pos-match-group 8
               ;; Never drop below X=0.
@@ -20254,6 +20257,146 @@ it has a `diary' type."
 		    (org-format-timestamp timestamp fmt t))
 	  (org-format-timestamp timestamp fmt (eq boundary 'end)))))))
 
+;;; Yank media handler and DND
+(defun org-setup-yank-dnd-handlers ()
+  "Setup the `yank-media' and DND handlers for buffer."
+  (setq-local dnd-protocol-alist
+              (cons '("^file:///" . org--dnd-local-file-handler)
+                    dnd-protocol-alist))
+  (yank-media-handler "image/.*" #'org--image-yank-media-handler)
+  ;; Looks like different DEs go for different handler names,
+  ;; https://larsee.com/blog/2019/05/clipboard-files/.
+  (yank-media-handler "x/special-\\(?:gnome\|KDE\|mate\\)-files"
+                      #'org--copied-files-yank-media-handler))
+
+(defcustom org-media-image-save-type 'attach
+  "Method to save images yanked from clipboard and dropped to Emacs.
+It can be the symbol `attach' to add it as an attachment, or a
+directory name to copy/cut the image to that directory."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Add it as attachment" attach)
+                 (directory :tag "Save it in directory"))
+  :safe (lambda (x) (or (stringp x) (eq x 'attach))))
+
+(declare-function org-attach-attach "org-attach" (file &optional visit-dir method))
+
+(defun org--image-yank-media-handler (mimetype data)
+  "Save image DATA of mime-type MIMETYPE and insert link at point.
+It is saved as per `org-media-image-save-type'.  The name for the
+image is prompted and the extension is automatically added to the
+end."
+  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
+         (iname (read-string "Insert filename for image: "))
+         (filename (file-name-with-extension iname ext))
+         (absname (expand-file-name
+                   filename
+                   (if (eq org-media-image-save-type 'attach)
+                       temporary-file-directory
+                     org-media-image-save-type)))
+         link)
+    (when (and (not (eq org-media-image-save-type 'attach))
+               (not (file-directory-p org-media-image-save-type)))
+      (make-directory org-media-image-save-type t))
+    (with-temp-file absname
+      (insert data))
+    (if (null (eq org-media-image-save-type 'attach))
+        (setq link (org-link-make-string
+                    (concat "file:" (file-relative-name absname))
+                    filename))
+      (require 'org-attach)
+      (org-attach-attach absname nil 'mv)
+      (setq link (org-link-make-string
+                  (concat "attachment:" filename)
+                  filename)))
+    (insert link)))
+
+;; I cannot find a spec for this but
+;; https://indigo.re/posts/2021-12-21-clipboard-data.html and pcmanfm
+;; suggests that this is the format.
+(defun org--copied-files-yank-media-handler (_mimetype data)
+  "Attach copied or cut files from file manager.
+If the files were cut from the file manager, then the `mv' attach
+method is used; `cp' otherwise.
+
+DATA is a string where the first line is the operation to
+perform: copy or cut.  Rest of the lines are file: links to the
+concerned files."
+  (require 'org-attach)
+  (let* ((data (split-string data "[\0\n\r]" t "^file://"))
+         (files (cdr data))
+         (method (if (equal (car data) "cut")
+                     'mv
+                   'cp)))
+    (dolist (f files)
+      (setq f (url-unhex-string f))
+      (if (file-readable-p f)
+          (org-attach-attach f nil method)
+        (message "File `%s' is not readable, skipping" f)))))
+
+(defcustom org-dnd-default-attach-method nil
+  "Default attach method to use when DND action is unspecified.
+This attach method is used when the DND action is `private'.
+This is also used when `org-media-image-save-type' is nil.
+When nil, use `org-attach-method'."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Default attach method" nil)
+                 (const :tag "Copy" cp)
+                 (const :tag "Move" mv)
+                 (const :tag "Hard link" ln)
+                 (const :tag "Symbol link" lns)))
+
+(declare-function mailcap-file-name-to-mime-type "mailcap" (file-name))
+(defvar org-attach-method)
+
+(defun org--dnd-local-file-handler (url action)
+  "Attach filename given by URL using method pertaining to ACTION.
+If ACTION is `move', use `mv' attach method.
+If `copy', use `cp' attach method.
+If `ask', ask the user.
+If `private', use the method denoted in `vz/org-dnd-default-attach-action'.
+The action `private' is always returned."
+  (require 'mailcap)
+  (let* ((filename (dnd-get-local-file-name url))
+         (mimetype (mailcap-file-name-to-mime-type filename))
+         (separatep (and (string-prefix-p "image/" mimetype)
+                         (not (eq 'attach org-media-image-save-type))))
+         (method (pcase action
+                   ('copy 'cp)
+                   ('move 'mv)
+                   ('ask (caddr (read-multiple-choice
+                                 "Attach using method"
+                                 '((?c "cp" cp)
+                                   (?m "mv" mv)
+                                   (?l "ln hard link" ln)
+                                   (?s "symbolic link" lns)))))
+                   ('private (or org-dnd-default-attach-method
+                                 org-attach-method)))))
+    (if separatep
+        (funcall
+         (pcase method
+           ('cp #'copy-file)
+           ('mv #'rename-file)
+           ('ln #'add-name-to-file)
+           ('lns #'make-symbolic-link))
+         filename
+         (expand-file-name (file-name-nondirectory filename)
+                           org-media-image-save-type))
+      (org-attach-attach filename nil method))
+    (insert
+     (org-link-make-string
+      (concat (if separatep
+                  "file:"
+                "attachment:")
+              (if separatep
+                  (expand-file-name (file-name-nondirectory filename)
+                                    org-media-image-save-type)
+                (file-name-nondirectory filename)))
+      (file-name-nondirectory filename))
+     "\n")
+    'private))
+
 ;;; Other stuff
 
 (defvar reftex-docstruct-symbol)
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-22 14:52 ` [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)] Visuwesh
@ 2023-09-22 16:51   ` Max Nikulin
  2023-09-22 17:29     ` Visuwesh
  2023-09-23 10:28   ` Ihor Radchenko
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 49+ messages in thread
From: Max Nikulin @ 2023-09-22 16:51 UTC (permalink / raw)
  To: Visuwesh, emacs-orgmode

On 22/09/2023 21:52, Visuwesh wrote:
> Attached patch adds yank-media and DND handler to attach files in the
> clipboard and dropped onto an Emacs frame respectively.

Thank you for your work in this area.

I am not an active DND user, I am just curious what cases are supported:

- X11
- Wayland
- Sandboxed applications communicating with desktop environment through 
XDG desktop portal (flatpack/snap/AppImage or just running in bubblewrap)
- Emacs running on a remote host (ssh + X11 forwarding)
- Windows

When a file is dropped into an Org buffer I would consider inserting a 
link or storing it in addition to attachment options.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-22 16:51   ` Max Nikulin
@ 2023-09-22 17:29     ` Visuwesh
  2023-09-24  8:06       ` Max Nikulin
  0 siblings, 1 reply; 49+ messages in thread
From: Visuwesh @ 2023-09-22 17:29 UTC (permalink / raw)
  To: emacs-orgmode

[வெள்ளி செப்டம்பர் 22, 2023] Max Nikulin wrote:

> On 22/09/2023 21:52, Visuwesh wrote:
>> Attached patch adds yank-media and DND handler to attach files in the
>> clipboard and dropped onto an Emacs frame respectively.
>
> Thank you for your work in this area.
>
> I am not an active DND user, I am just curious what cases are supported:
>
> - X11

Yes.

> - Wayland

I don't know nor am I willing to setup a test environment for it.

> - Sandboxed applications communicating with desktop environment
>   through XDG desktop portal (flatpack/snap/AppImage or just running
>  in bubblewrap)

Same as above.

> - Emacs running on a remote host (ssh + X11 forwarding)

If the DND links are of the form file://HOST/ then the current DND
handler won't run.  I don't have a way to test it myself so I can't
write a handler for remote files.  I am not sure if we can even reuse
the existing the org-attach code for remote files.

> - Windows

I don't have access to a Windows machine so can't test it unfortunately.

> When a file is dropped into an Org buffer I would consider inserting a
> link or storing it in addition to attachment options.

The current code already inserts a link to the dropped files.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-22 14:52 ` [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)] Visuwesh
  2023-09-22 16:51   ` Max Nikulin
@ 2023-09-23 10:28   ` Ihor Radchenko
  2023-09-23 16:55     ` Visuwesh
  2023-09-24 14:58     ` Max Nikulin
  2023-09-24 14:49   ` Max Nikulin
  2023-10-06  7:34   ` Po Lu
  3 siblings, 2 replies; 49+ messages in thread
From: Ihor Radchenko @ 2023-09-23 10:28 UTC (permalink / raw)
  To: Visuwesh; +Cc: emacs-orgmode

Visuwesh <visuweshm@gmail.com> writes:

> The patch compiles cleanly without warnings but I haven't tested _this_
> patch yet, I have these functions in my init.el and have tested those.

Thanks for the patch!
See my comments below.

> +*** Images and files in clipboard can be attached
> +
> +Org can now attach images in clipboard and files copied/cut to the
> +clipboard from file managers using the ~yank-media~ command which also
> +inserts a link to the attached file.
> +
> +Images can be saved to a separate directory instead of being attached,
> +customize ~org-media-image-save-type~.

This requires Emacs 29, which should be documented.

>  ;; Update `customize-package-emacs-version-alist'
>  (add-to-list 'customize-package-emacs-version-alist
> @@ -15125,20 +15128,20 @@ INCREMENT-STEP divisor."
>  	(setq hour (mod hour 24))
>  	(setq pos-match-group 1
>                new (format "-%02d:%02d" hour minute)))
> -       
> +

These whitespace changes are not relevant.

> +;;; Yank media handler and DND
> +(defun org-setup-yank-dnd-handlers ()
> +  "Setup the `yank-media' and DND handlers for buffer."
> +  (setq-local dnd-protocol-alist
> +              (cons '("^file:///" . org--dnd-local-file-handler)
> +                    dnd-protocol-alist))
> +  (yank-media-handler "image/.*" #'org--image-yank-media-handler)

This function is not yet available in Emacs <29. Need to protect with `fboundp'.

> +(defcustom org-media-image-save-type 'attach

Maybe org-yank-image-save-type?

> +(defun org--image-yank-media-handler (mimetype data)
> +  "Save image DATA of mime-type MIMETYPE and insert link at point.
> +It is saved as per `org-media-image-save-type'.  The name for the
> +image is prompted and the extension is automatically added to the
> +end."
> +  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
> +         (iname (read-string "Insert filename for image: "))

It would be nice if we auto-generate the file name here by default. It
is what I would expect from yanking an image at least.

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-23 10:28   ` Ihor Radchenko
@ 2023-09-23 16:55     ` Visuwesh
  2023-09-25 13:14       ` Visuwesh
  2023-09-24 14:58     ` Max Nikulin
  1 sibling, 1 reply; 49+ messages in thread
From: Visuwesh @ 2023-09-23 16:55 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: emacs-orgmode

[சனி செப்டம்பர் 23, 2023] Ihor Radchenko wrote:

>> +*** Images and files in clipboard can be attached
>> +
>> +Org can now attach images in clipboard and files copied/cut to the
>> +clipboard from file managers using the ~yank-media~ command which also
>> +inserts a link to the attached file.
>> +
>> +Images can be saved to a separate directory instead of being attached,
>> +customize ~org-media-image-save-type~.
>
> This requires Emacs 29, which should be documented.

Yes, will do.

>>  ;; Update `customize-package-emacs-version-alist'
>>  (add-to-list 'customize-package-emacs-version-alist
>> @@ -15125,20 +15128,20 @@ INCREMENT-STEP divisor."
>>  	(setq hour (mod hour 24))
>>  	(setq pos-match-group 1
>>                new (format "-%02d:%02d" hour minute)))
>> -       
>> +
>
> These whitespace changes are not relevant.

Sorry, I missed these sneaky changes when I made the commit.

>> +;;; Yank media handler and DND
>> +(defun org-setup-yank-dnd-handlers ()
>> +  "Setup the `yank-media' and DND handlers for buffer."
>> +  (setq-local dnd-protocol-alist
>> +              (cons '("^file:///" . org--dnd-local-file-handler)
>> +                    dnd-protocol-alist))
>> +  (yank-media-handler "image/.*" #'org--image-yank-media-handler)
>
> This function is not yet available in Emacs <29. Need to protect with `fboundp'.

Of course.

>> +(defcustom org-media-image-save-type 'attach
>
> Maybe org-yank-image-save-type?

That is a better name but still doesn't inform the user that it is
respected by DND too.

>> +(defun org--image-yank-media-handler (mimetype data)
>> +  "Save image DATA of mime-type MIMETYPE and insert link at point.
>> +It is saved as per `org-media-image-save-type'.  The name for the
>> +image is prompted and the extension is automatically added to the
>> +end."
>> +  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
>> +         (iname (read-string "Insert filename for image: "))
>
> It would be nice if we auto-generate the file name here by default. It
> is what I would expect from yanking an image at least.

Hmm, I think I prefer having a descriptive name for the file.  It would
aid in searching for the attachment later (in the org document, and
using find & friends).  We can add a user option but how should the name
be auto-generated? Timestamp?  I have no ideas in this regard.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-22 17:29     ` Visuwesh
@ 2023-09-24  8:06       ` Max Nikulin
  0 siblings, 0 replies; 49+ messages in thread
From: Max Nikulin @ 2023-09-24  8:06 UTC (permalink / raw)
  To: emacs-orgmode

On 23/09/2023 00:29, Visuwesh wrote:
> [வெள்ளி செப்டம்பர் 22, 2023] Max Nikulin wrote:
> 
>> When a file is dropped into an Org buffer I would consider inserting a
>> link or storing it in addition to attachment options.
> 
> The current code already inserts a link to the dropped files.

I mean just insert a "file:" link to the *original* file location 
instead of attaching it or moving it to the preconfigured directory.



^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-22 14:52 ` [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)] Visuwesh
  2023-09-22 16:51   ` Max Nikulin
  2023-09-23 10:28   ` Ihor Radchenko
@ 2023-09-24 14:49   ` Max Nikulin
  2023-10-06  7:34   ` Po Lu
  3 siblings, 0 replies; 49+ messages in thread
From: Max Nikulin @ 2023-09-24 14:49 UTC (permalink / raw)
  To: emacs-orgmode

On 22/09/2023 21:52, Visuwesh wrote:
> Attached patch adds yank-media and DND handler to attach files in the
> clipboard and dropped onto an Emacs frame respectively.

Please, use `make-temp-file' to create files in the temporary directory 
that may be world writable. Predictable file names there are undesired 
from security point of view.

Other notes are no more than opinion. I may easily miss something and 
verbose reply may be wasting of time. Do not hesitate to ask more 
details if you do not agree.

At first, I expected more common with the following project
https://github.com/abo-abo/org-download/
however even the approach to fetch images from clipboard is different.

> +++ b/lisp/org.el

I am in doubts if the following code is more suitable for org.el or for 
org-attach.el

> @@ -20254,6 +20257,146 @@ it has a `diary' type."
>  		    (org-format-timestamp timestamp fmt t))
>  	  (org-format-timestamp timestamp fmt (eq boundary 'end)))))))
>  
> +;;; Yank media handler and DND
> +(defun org-setup-yank-dnd-handlers ()
> +  "Setup the `yank-media' and DND handlers for buffer."
> +  (setq-local dnd-protocol-alist
> +              (cons '("^file:///" . org--dnd-local-file-handler)
> +                    dnd-protocol-alist))
> +  (yank-media-handler "image/.*" #'org--image-yank-media-handler)
> +  ;; Looks like different DEs go for different handler names,
> +  ;; https://larsee.com/blog/2019/05/clipboard-files/.
> +  (yank-media-handler "x/special-\\(?:gnome\|KDE\|mate\\)-files"
> +                      #'org--copied-files-yank-media-handler))
> +
> +(defcustom org-media-image-save-type 'attach

Could it be extended to any mime type? If so I would avoid "image" in 
its name. `org-yank-media-save-dir'?

> +  "Method to save images yanked from clipboard and dropped to Emacs.
> +It can be the symbol `attach' to add it as an attachment, or a
> +directory name to copy/cut the image to that directory."
> +  :group 'org
> +  :package-version '(Org . "9.7")
> +  :type '(choice (const :tag "Add it as attachment" attach)
> +                 (directory :tag "Save it in directory"))
> +  :safe (lambda (x) (or (stringp x) (eq x 'attach))))

Unsure if every directory may be considered as safe (e.g. ~/.ssh/)

> +
> +(declare-function org-attach-attach "org-attach" (file &optional visit-dir method))
> +
> +(defun org--image-yank-media-handler (mimetype data)
> +  "Save image DATA of mime-type MIMETYPE and insert link at point.
> +It is saved as per `org-media-image-save-type'.  The name for the
> +image is prompted and the extension is automatically added to the
> +end."
> +  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
> +         (iname (read-string "Insert filename for image: "))

Unless 'attach is used, `read-file-name' would allow saving to arbitrary 
directory with path completion.

> +         (filename (file-name-with-extension iname ext))
> +         (absname (expand-file-name
> +                   filename
> +                   (if (eq org-media-image-save-type 'attach)
> +                       temporary-file-directory

`make-temp-file' should be used instead of `temporary-file-directory', 
however it requires changes in code around.

> +                     org-media-image-save-type)))
> +         link)
> +    (when (and (not (eq org-media-image-save-type 'attach))
> +               (not (file-directory-p org-media-image-save-type)))
> +      (make-directory org-media-image-save-type t))
> +    (with-temp-file absname
> +      (insert data))
> +    (if (null (eq org-media-image-save-type 'attach))
> +        (setq link (org-link-make-string
> +                    (concat "file:" (file-relative-name absname))
> +                    filename))

Is there a chance to request for link description? Unsure if 
`org-insert-link' may be easily reused.

> +      (require 'org-attach)
> +      (org-attach-attach absname nil 'mv)
> +      (setq link (org-link-make-string
> +                  (concat "attachment:" filename)
> +                  filename)))
> +    (insert link)))
> +
> +;; I cannot find a spec for this but
> +;; https://indigo.re/posts/2021-12-21-clipboard-data.html and pcmanfm
> +;; suggests that this is the format.
> +(defun org--copied-files-yank-media-handler (_mimetype data)
> +  "Attach copied or cut files from file manager.
> +If the files were cut from the file manager, then the `mv' attach
> +method is used; `cp' otherwise.
> +
> +DATA is a string where the first line is the operation to
> +perform: copy or cut.  Rest of the lines are file: links to the
> +concerned files."
> +  (require 'org-attach)
> +  (let* ((data (split-string data "[\0\n\r]" t "^file://"))
> +         (files (cdr data))
> +         (method (if (equal (car data) "cut")
> +                     'mv
> +                   'cp)))
> +    (dolist (f files)
> +      (setq f (url-unhex-string f))
> +      (if (file-readable-p f)
> +          (org-attach-attach f nil method)
> +        (message "File `%s' is not readable, skipping" f)))))
> +
> +(defcustom org-dnd-default-attach-method nil
> +  "Default attach method to use when DND action is unspecified.
> +This attach method is used when the DND action is `private'.
> +This is also used when `org-media-image-save-type' is nil.
> +When nil, use `org-attach-method'."
> +  :group 'org
> +  :package-version '(Org . "9.7")
> +  :type '(choice (const :tag "Default attach method" nil)
> +                 (const :tag "Copy" cp)
> +                 (const :tag "Move" mv)
> +                 (const :tag "Hard link" ln)
> +                 (const :tag "Symbol link" lns)))

Labels in the menu below are a bit different. "Symbolic"?

> +
> +(declare-function mailcap-file-name-to-mime-type "mailcap" (file-name))
> +(defvar org-attach-method)
> +
> +(defun org--dnd-local-file-handler (url action)
> +  "Attach filename given by URL using method pertaining to ACTION.
> +If ACTION is `move', use `mv' attach method.
> +If `copy', use `cp' attach method.
> +If `ask', ask the user.
> +If `private', use the method denoted in `vz/org-dnd-default-attach-action'.

"vz/" likely should be dropped

> +The action `private' is always returned."
> +  (require 'mailcap)
> +  (let* ((filename (dnd-get-local-file-name url))
> +         (mimetype (mailcap-file-name-to-mime-type filename))
> +         (separatep (and (string-prefix-p "image/" mimetype)
> +                         (not (eq 'attach org-media-image-save-type))))
> +         (method (pcase action
> +                   ('copy 'cp)
> +                   ('move 'mv)
> +                   ('ask (caddr (read-multiple-choice

`org-mks' has some issues, but it is the current way to present a kind 
of menu in Org.

> +                                 "Attach using method"
> +                                 '((?c "cp" cp)
> +                                   (?m "mv" mv)
> +                                   (?l "ln hard link" ln)

"ln" in the label perhaps should be dropped. Should it be hidden if stat 
reports different file systems for source and target or fallback to copy 
is implicitly used?

> +                                   (?s "symbolic link" lns)))))
> +                   ('private (or org-dnd-default-attach-method
> +                                 org-attach-method)))))
> +    (if separatep
> +        (funcall
> +         (pcase method
> +           ('cp #'copy-file)
> +           ('mv #'rename-file)
> +           ('ln #'add-name-to-file)
> +           ('lns #'make-symbolic-link))
> +         filename
> +         (expand-file-name (file-name-nondirectory filename)
> +                           org-media-image-save-type))
> +      (org-attach-attach filename nil method))
> +    (insert
> +     (org-link-make-string
> +      (concat (if separatep

I would consider swapping of `concat' and `if' for the sake of readability.

> +                  "file:"
> +                "attachment:")
> +              (if separatep
> +                  (expand-file-name (file-name-nondirectory filename)
> +                                    org-media-image-save-type)
> +                (file-name-nondirectory filename)))
> +      (file-name-nondirectory filename))
> +     "\n")
> +    'private))
> +
>  ;;; Other stuff
>  
>  (defvar reftex-docstruct-symbol)
> -- 

I do not know if it can be easily implemented, but it may be useful to 
control attachment/generic directory at the moment when a particular 
image is yanked/dropped instead of purely relying on predefined user option.




^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-23 10:28   ` Ihor Radchenko
  2023-09-23 16:55     ` Visuwesh
@ 2023-09-24 14:58     ` Max Nikulin
  2023-09-25 14:15       ` Visuwesh
  1 sibling, 1 reply; 49+ messages in thread
From: Max Nikulin @ 2023-09-24 14:58 UTC (permalink / raw)
  To: emacs-orgmode

On 23/09/2023 17:28, Ihor Radchenko wrote:
> Visuwesh writes:
> 
>> +  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
>> +         (iname (read-string "Insert filename for image: "))
> 
> It would be nice if we auto-generate the file name here by default. It
> is what I would expect from yanking an image at least.

Certainly it would be great to provide some default value allowing user 
to override it. However it may be not so trivial. Clipboard content may 
be just copy region from some graphics editor or a screenshot tool, so 
not associated with a file yet. In X11 xprop may be used to get 
clipboard owner application and its title. Wayland may be less permissive.

I have tried to copy an image in Firefox. There is a chance to find file 
name (it may be image.php?id=1324 though) in text/html clipboard 
content, but it requires parsing of HTML

xclip -selection clipboard -o -t text/html



^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-23 16:55     ` Visuwesh
@ 2023-09-25 13:14       ` Visuwesh
  2023-09-26 16:25         ` Max Nikulin
  2023-10-07 12:07         ` Ihor Radchenko
  0 siblings, 2 replies; 49+ messages in thread
From: Visuwesh @ 2023-09-25 13:14 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 1105 bytes --]

[சனி செப்டம்பர் 23, 2023] Visuwesh wrote:

>>> +(defun org--image-yank-media-handler (mimetype data)
>>> +  "Save image DATA of mime-type MIMETYPE and insert link at point.
>>> +It is saved as per `org-media-image-save-type'.  The name for the
>>> +image is prompted and the extension is automatically added to the
>>> +end."
>>> +  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
>>> +         (iname (read-string "Insert filename for image: "))
>>
>> It would be nice if we auto-generate the file name here by default. It
>> is what I would expect from yanking an image at least.
>
> Hmm, I think I prefer having a descriptive name for the file.  It would
> aid in searching for the attachment later (in the org document, and
> using find & friends).  We can add a user option but how should the name
> be auto-generated? Timestamp?  I have no ideas in this regard.

Attached patch considers your review and also another defcustom to tell
how to generate the filename which by default autogenerates a filename
based on current time.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-support-for-yank-media-and-DND.patch --]
[-- Type: text/x-diff, Size: 9984 bytes --]

From c9f149cde047a60b6c2a256d96616864f5f66352 Mon Sep 17 00:00:00 2001
From: Visuwesh <visuweshm@gmail.com>
Date: Fri, 22 Sep 2023 20:11:41 +0530
Subject: [PATCH] Add support for yank-media and DND

* lisp/org.el (org-mode): Call the setup function for yank-media and
DND.
(org-setup-yank-dnd-handlers): Register yank-media-handler and DND
handler.
(org-yank-image-save-type, org-dnd-default-attach-method)
(org-yank-image-file-name-function): New defcustoms.
(org--image-yank-media-handler, org--copied-files-yank-media-handler)
(org--dnd-local-file-handler): Add yank-media and DND handlers.

* etc/ORG-NEWS: Advertise the new features.
---
 etc/ORG-NEWS |  20 ++++++
 lisp/org.el  | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 188 insertions(+), 1 deletion(-)

diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 252c5a9f9..c4a58dd4d 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -596,6 +596,26 @@ return a matplotlib Figure object to plot. For output results, the
 current figure (as returned by =pyplot.gcf()=) is cleared before
 evaluation, and then plotted afterwards.
 
+*** Images and files in clipboard can be attached
+
+Org can now attach images in clipboard and files copied/cut to the
+clipboard from file managers using the ~yank-media~ command which also
+inserts a link to the attached file.  This command was added in Emacs 29.
+
+Images can be saved to a separate directory instead of being attached,
+customize ~org-yank-image-save-type~.
+
+Image filename chosen can be customized by setting
+~org-yank-image-file-name-function~ which by default autogenerates a
+filename based on the current time.
+
+*** Files and images can be attached by dropping onto Emacs
+
+Attachment method other than ~org-attach-method~ for dropped files can
+be specified using ~org-dnd-default-attach-method~.
+
+Images dropped also respect the value of ~org-yank-image-save-type~.
+
 ** New functions and changes in function arguments
 *** =TYPES= argument in ~org-element-lineage~ can now be a symbol
 
diff --git a/lisp/org.el b/lisp/org.el
index d0b2355ea..d69f9ec70 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -4999,7 +4999,10 @@ The following commands are available:
   (org--set-faces-extend '(org-block-begin-line org-block-end-line)
                          org-fontify-whole-block-delimiter-line)
   (org--set-faces-extend org-level-faces org-fontify-whole-heading-line)
-  (setq-local org-mode-loading nil))
+  (setq-local org-mode-loading nil)
+
+  ;; `yank-media' handler and DND support.
+  (org-setup-yank-dnd-handlers))
 
 ;; Update `customize-package-emacs-version-alist'
 (add-to-list 'customize-package-emacs-version-alist
@@ -20254,6 +20257,170 @@ it has a `diary' type."
 		    (org-format-timestamp timestamp fmt t))
 	  (org-format-timestamp timestamp fmt (eq boundary 'end)))))))
 
+;;; Yank media handler and DND
+(defun org-setup-yank-dnd-handlers ()
+  "Setup the `yank-media' and DND handlers for buffer."
+  (setq-local dnd-protocol-alist
+              (cons '("^file:///" . org--dnd-local-file-handler)
+                    dnd-protocol-alist))
+  (when (fboundp 'yank-media-handler)
+    (yank-media-handler "image/.*" #'org--image-yank-media-handler)
+    ;; Looks like different DEs go for different handler names,
+    ;; https://larsee.com/blog/2019/05/clipboard-files/.
+    (yank-media-handler "x/special-\\(?:gnome\|KDE\|mate\\)-files"
+                        #'org--copied-files-yank-media-handler)))
+
+(defcustom org-yank-image-save-type 'attach
+  "Method to save images yanked from clipboard and dropped to Emacs.
+It can be the symbol `attach' to add it as an attachment, or a
+directory name to copy/cut the image to that directory."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Add it as attachment" attach)
+                 (directory :tag "Save it in directory"))
+  :safe (lambda (x) (or (stringp x) (eq x 'attach))))
+
+(defcustom org-yank-image-file-name-function #'org-yank-image-autogen-filename 
+  "Function to generate filename for image yanked from clipboard.
+By default, this autogenerates a filename based on the current
+time.
+It is called with no arguments and should return a string without
+any extension which is used as the filename."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(radio (function-item :doc "Autogenerate filename"
+                               org-yank-image-autogen-filename)
+                (function-item :doc "Ask for filename"
+                               org-yank-image-read-filename)
+                function))
+
+(defun org-yank-image-autogen-filename ()
+  "Autogenerate filename for image in clipboard."
+  (format-time-string "clipboard-%Y-%m-%d-%H:%M"))
+
+(defun org-yank-image-read-filename ()
+  "Read filename for image in clipboard."
+  (read-string "Filename for image without extension: "))
+
+(declare-function org-attach-attach "org-attach" (file &optional visit-dir method))
+
+(defun org--image-yank-media-handler (mimetype data)
+  "Save image DATA of mime-type MIMETYPE and insert link at point.
+It is saved as per `org-yank-image-save-type'.  The name for the
+image is prompted and the extension is automatically added to the
+end."
+  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
+         (iname (funcall org-yank-image-file-name-function))
+         (filename (file-name-with-extension iname ext))
+         (absname (expand-file-name
+                   filename
+                   (if (eq org-yank-image-save-type 'attach)
+                       temporary-file-directory
+                     org-yank-image-save-type)))
+         link)
+    (when (and (not (eq org-yank-image-save-type 'attach))
+               (not (file-directory-p org-yank-image-save-type)))
+      (make-directory org-yank-image-save-type t))
+    (with-temp-file absname
+      (insert data))
+    (if (null (eq org-yank-image-save-type 'attach))
+        (setq link (org-link-make-string
+                    (concat "file:" (file-relative-name absname))
+                    filename))
+      (require 'org-attach)
+      (org-attach-attach absname nil 'mv)
+      (setq link (org-link-make-string
+                  (concat "attachment:" filename)
+                  filename)))
+    (insert link)))
+
+;; I cannot find a spec for this but
+;; https://indigo.re/posts/2021-12-21-clipboard-data.html and pcmanfm
+;; suggests that this is the format.
+(defun org--copied-files-yank-media-handler (_mimetype data)
+  "Attach copied or cut files from file manager.
+If the files were cut from the file manager, then the `mv' attach
+method is used; `cp' otherwise.
+
+DATA is a string where the first line is the operation to
+perform: copy or cut.  Rest of the lines are file: links to the
+concerned files."
+  (require 'org-attach)
+  ;; pcmanfm adds a null byte at the end for some reason.
+  (let* ((data (split-string data "[\0\n\r]" t "^file://"))
+         (files (cdr data))
+         (method (if (equal (car data) "cut")
+                     'mv
+                   'cp)))
+    (dolist (f files)
+      (setq f (url-unhex-string f))
+      (if (file-readable-p f)
+          (org-attach-attach f nil method)
+        (message "File `%s' is not readable, skipping" f)))))
+
+(defcustom org-dnd-default-attach-method nil
+  "Default attach method to use when DND action is unspecified.
+This attach method is used when the DND action is `private'.
+This is also used when `org-yank-image-save-type' is nil.
+When nil, use `org-attach-method'."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Default attach method" nil)
+                 (const :tag "Copy" cp)
+                 (const :tag "Move" mv)
+                 (const :tag "Hard link" ln)
+                 (const :tag "Symbol link" lns)))
+
+(declare-function mailcap-file-name-to-mime-type "mailcap" (file-name))
+(defvar org-attach-method)
+
+(defun org--dnd-local-file-handler (url action)
+  "Attach filename given by URL using method pertaining to ACTION.
+If ACTION is `move', use `mv' attach method.
+If `copy', use `cp' attach method.
+If `ask', ask the user.
+If `private', use the method denoted in `vz/org-dnd-default-attach-action'.
+The action `private' is always returned."
+  (require 'mailcap)
+  (let* ((filename (dnd-get-local-file-name url))
+         (mimetype (mailcap-file-name-to-mime-type filename))
+         (separatep (and (string-prefix-p "image/" mimetype)
+                         (not (eq 'attach org-yank-image-save-type))))
+         (method (pcase action
+                   ('copy 'cp)
+                   ('move 'mv)
+                   ('ask (caddr (read-multiple-choice
+                                 "Attach using method"
+                                 '((?c "cp" cp)
+                                   (?m "mv" mv)
+                                   (?l "ln hard link" ln)
+                                   (?s "symbolic link" lns)))))
+                   ('private (or org-dnd-default-attach-method
+                                 org-attach-method)))))
+    (if separatep
+        (funcall
+         (pcase method
+           ('cp #'copy-file)
+           ('mv #'rename-file)
+           ('ln #'add-name-to-file)
+           ('lns #'make-symbolic-link))
+         filename
+         (expand-file-name (file-name-nondirectory filename)
+                           org-yank-image-save-type))
+      (org-attach-attach filename nil method))
+    (insert
+     (org-link-make-string
+      (concat (if separatep
+                  "file:"
+                "attachment:")
+              (if separatep
+                  (expand-file-name (file-name-nondirectory filename)
+                                    org-yank-image-save-type)
+                (file-name-nondirectory filename)))
+      (file-name-nondirectory filename))
+     "\n")
+    'private))
+
 ;;; Other stuff
 
 (defvar reftex-docstruct-symbol)
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-24 14:58     ` Max Nikulin
@ 2023-09-25 14:15       ` Visuwesh
  2023-09-26 10:24         ` Ihor Radchenko
  0 siblings, 1 reply; 49+ messages in thread
From: Visuwesh @ 2023-09-25 14:15 UTC (permalink / raw)
  To: Max Nikulin; +Cc: emacs-orgmode

[ Please keep me in the CC as I don't follow the list.  ]

[ஞாயிறு செப்டம்பர் 24, 2023] Max Nikulin wrote:

> On 23/09/2023 17:28, Ihor Radchenko wrote:
>> Visuwesh writes:
>> 
>>> +  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
>>> +         (iname (read-string "Insert filename for image: "))
>> It would be nice if we auto-generate the file name here by
>> default. It
>> is what I would expect from yanking an image at least.
>
> Certainly it would be great to provide some default value allowing
> user to override it. However it may be not so trivial. Clipboard
> content may be just copy region from some graphics editor or a
> screenshot tool, so not associated with a file yet. In X11 xprop may
> be used to get clipboard owner application and its title. Wayland may
> be less permissive.
>
> I have tried to copy an image in Firefox. There is a chance to find
> file name (it may be image.php?id=1324 though) in text/html clipboard
> content, but it requires parsing of HTML
>
> xclip -selection clipboard -o -t text/html

I would rather not go down this rabbit hole since there is no way to
cover all the possible cases.

[ஞாயிறு செப்டம்பர் 24, 2023] Max Nikulin wrote:

> On 22/09/2023 21:52, Visuwesh wrote:
>> Attached patch adds yank-media and DND handler to attach files in the
>> clipboard and dropped onto an Emacs frame respectively.
>
> Please, use `make-temp-file' to create files in the temporary
> directory that may be world writable. Predictable file names there are
> undesired from security point of view.

What harm does it cause?

> Other notes are no more than opinion. I may easily miss something and
> verbose reply may be wasting of time. Do not hesitate to ask more
> details if you do not agree.
>
> At first, I expected more common with the following project
> https://github.com/abo-abo/org-download/
> however even the approach to fetch images from clipboard is different.

I have never used that package, this is what I wrote in a discussion
with Ihor in the Matrix room which we iteratively improved.  It was
mostly written with my workflow in mind.  Without knowledge of how
others work, I cannot improve the implementation.  I also don't
understand what the linked package from the Commentary section (which is
why I never used it).

>> +++ b/lisp/org.el
>
> I am in doubts if the following code is more suitable for org.el or
> for org-attach.el

I have the same doubts.

>> @@ -20254,6 +20257,146 @@ it has a `diary' type."
>>  		    (org-format-timestamp timestamp fmt t))
>>  	  (org-format-timestamp timestamp fmt (eq boundary 'end)))))))
>>  +;;; Yank media handler and DND
>> +(defun org-setup-yank-dnd-handlers ()
>> +  "Setup the `yank-media' and DND handlers for buffer."
>> +  (setq-local dnd-protocol-alist
>> +              (cons '("^file:///" . org--dnd-local-file-handler)
>> +                    dnd-protocol-alist))
>> +  (yank-media-handler "image/.*" #'org--image-yank-media-handler)
>> +  ;; Looks like different DEs go for different handler names,
>> +  ;; https://larsee.com/blog/2019/05/clipboard-files/.
>> +  (yank-media-handler "x/special-\\(?:gnome\|KDE\|mate\\)-files"
>> +                      #'org--copied-files-yank-media-handler))
>> +
>> +(defcustom org-media-image-save-type 'attach
>
> Could it be extended to any mime type? If so I would avoid "image" in
> its name. `org-yank-media-save-dir'?

It should be easy enough to do it but do we want to go there?

>> +  "Method to save images yanked from clipboard and dropped to Emacs.
>> +It can be the symbol `attach' to add it as an attachment, or a
>> +directory name to copy/cut the image to that directory."
>> +  :group 'org
>> +  :package-version '(Org . "9.7")
>> +  :type '(choice (const :tag "Add it as attachment" attach)
>> +                 (directory :tag "Save it in directory"))
>> +  :safe (lambda (x) (or (stringp x) (eq x 'attach))))
>
> Unsure if every directory may be considered as safe (e.g. ~/.ssh/)

Is there a reliable way to know which directory is "safe"?  If not, then
let the users shoot themselves in the foot.

>> +
>> +(declare-function org-attach-attach "org-attach" (file &optional visit-dir method))
>> +
>> +(defun org--image-yank-media-handler (mimetype data)
>> +  "Save image DATA of mime-type MIMETYPE and insert link at point.
>> +It is saved as per `org-media-image-save-type'.  The name for the
>> +image is prompted and the extension is automatically added to the
>> +end."
>> +  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
>> +         (iname (read-string "Insert filename for image: "))
>
> Unless 'attach is used, `read-file-name' would allow saving to
> arbitrary directory with path completion.

Sorry, I don't understand what you mean here.  I am not really familiar
with attachment facilities of org-mode.

>> +         (filename (file-name-with-extension iname ext))
>> +         (absname (expand-file-name
>> +                   filename
>> +                   (if (eq org-media-image-save-type 'attach)
>> +                       temporary-file-directory
>
> `make-temp-file' should be used instead of `temporary-file-directory',
> however it requires changes in code around.
>
>> +                     org-media-image-save-type)))
>> +         link)
>> +    (when (and (not (eq org-media-image-save-type 'attach))
>> +               (not (file-directory-p org-media-image-save-type)))
>> +      (make-directory org-media-image-save-type t))
>> +    (with-temp-file absname
>> +      (insert data))
>> +    (if (null (eq org-media-image-save-type 'attach))
>> +        (setq link (org-link-make-string
>> +                    (concat "file:" (file-relative-name absname))
>> +                    filename))
>
> Is there a chance to request for link description? Unsure if
> `org-insert-link' may be easily reused.

This would include too many prompts and make the whole process annoying
again.

>> [...]
>> +(defcustom org-dnd-default-attach-method nil
>> +  "Default attach method to use when DND action is unspecified.
>> +This attach method is used when the DND action is `private'.
>> +This is also used when `org-media-image-save-type' is nil.
>> +When nil, use `org-attach-method'."
>> +  :group 'org
>> +  :package-version '(Org . "9.7")
>> +  :type '(choice (const :tag "Default attach method" nil)
>> +                 (const :tag "Copy" cp)
>> +                 (const :tag "Move" mv)
>> +                 (const :tag "Hard link" ln)
>> +                 (const :tag "Symbol link" lns)))
>
> Labels in the menu below are a bit different. "Symbolic"?

Okay, will make them both follow what org-attach uses.

>> +
>> +(declare-function mailcap-file-name-to-mime-type "mailcap" (file-name))
>> +(defvar org-attach-method)
>> +
>> +(defun org--dnd-local-file-handler (url action)
>> +  "Attach filename given by URL using method pertaining to ACTION.
>> +If ACTION is `move', use `mv' attach method.
>> +If `copy', use `cp' attach method.
>> +If `ask', ask the user.
>> +If `private', use the method denoted in `vz/org-dnd-default-attach-action'.
>
> "vz/" likely should be dropped

Yes, thanks for the catch.

>> +The action `private' is always returned."
>> +  (require 'mailcap)
>> +  (let* ((filename (dnd-get-local-file-name url))
>> +         (mimetype (mailcap-file-name-to-mime-type filename))
>> +         (separatep (and (string-prefix-p "image/" mimetype)
>> +                         (not (eq 'attach org-media-image-save-type))))
>> +         (method (pcase action
>> +                   ('copy 'cp)
>> +                   ('move 'mv)
>> +                   ('ask (caddr (read-multiple-choice
>
> `org-mks' has some issues, but it is the current way to present a kind
> of menu in Org.

Okay.

>> +                                 "Attach using method"
>> +                                 '((?c "cp" cp)
>> +                                   (?m "mv" mv)
>> +                                   (?l "ln hard link" ln)
>
> "ln" in the label perhaps should be dropped.

I added ln to make the first letter bolded in rmc prompt, but with
org-mks this shouldn't be necessary anymore.

> Should it be hidden if stat reports different file systems for source
> and target or fallback to copy is implicitly used?

It should be handled appropriately by Emacs already.  See (info "(emacs)
Copying and Naming").  I also don't find org-attach doing anything
special.

>> +                                   (?s "symbolic link" lns)))))
>> +                   ('private (or org-dnd-default-attach-method
>> +                                 org-attach-method)))))
>> +    (if separatep
>> +        (funcall
>> +         (pcase method
>> +           ('cp #'copy-file)
>> +           ('mv #'rename-file)
>> +           ('ln #'add-name-to-file)
>> +           ('lns #'make-symbolic-link))
>> +         filename
>> +         (expand-file-name (file-name-nondirectory filename)
>> +                           org-media-image-save-type))
>> +      (org-attach-attach filename nil method))
>> +    (insert
>> +     (org-link-make-string
>> +      (concat (if separatep
>
> I would consider swapping of `concat' and `if' for the sake of readability.

That can be done.

>> +                  "file:"
>> +                "attachment:")
>> +              (if separatep
>> +                  (expand-file-name (file-name-nondirectory filename)
>> +                                    org-media-image-save-type)
>> +                (file-name-nondirectory filename)))
>> +      (file-name-nondirectory filename))
>> +     "\n")
>> +    'private))
>> +
>>  ;;; Other stuff
>>   (defvar reftex-docstruct-symbol)
>> -- 
>
> I do not know if it can be easily implemented, but it may be useful to
> control attachment/generic directory at the moment when a particular
> image is yanked/dropped instead of purely relying on predefined user
> option.

Sorry, I don't understand what you mean here.  Note that my usage of
attachments is sparse.  "Particular image" is hard to determine since
the only reliable data at hand is its mimetype.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-25 14:15       ` Visuwesh
@ 2023-09-26 10:24         ` Ihor Radchenko
  2023-09-27  8:29           ` Visuwesh
  0 siblings, 1 reply; 49+ messages in thread
From: Ihor Radchenko @ 2023-09-26 10:24 UTC (permalink / raw)
  To: Visuwesh; +Cc: Max Nikulin, emacs-orgmode

Visuwesh <visuweshm@gmail.com> writes:

>> Please, use `make-temp-file' to create files in the temporary
>> directory that may be world writable. Predictable file names there are
>> undesired from security point of view.
>
> What harm does it cause?

/tmp directory can be written by any program and the file, while kept
there, might be modified by malicious code.

Not that I know a concrete example what harm can be done in this
particular case, but it is generally a good practice to make file names
in /tmp random.

>> At first, I expected more common with the following project
>> https://github.com/abo-abo/org-download/
>> however even the approach to fetch images from clipboard is different.
>
> I have never used that package, this is what I wrote in a discussion
> with Ihor in the Matrix room which we iteratively improved.  It was
> mostly written with my workflow in mind.  Without knowledge of how
> others work, I cannot improve the implementation.  I also don't
> understand what the linked package from the Commentary section (which is
> why I never used it).

The package does similar things, except that it focuses on downloading
images from URL stored in clipboard or by making a screenshot. Dnd is
also supported there, but just for a single URL/file path.

Max, unless you see something particular that is implemented better in
org-download, let's just ignore that library. I eyeballed the code and I
do not see anything that we need to account for, except some more
specialized features (like attaching screenshots), which we can always
add in future, if necessary.

>> I am in doubts if the following code is more suitable for org.el or
>> for org-attach.el
>
> I have the same doubts.

The patch implements dnd and media handlers, which constitute Org mode
integration with the rest of Emacs. So, they are a part of major mode
definition. If we want to keep things clean, we may create org-dnd.el
and org-yank-media.el and put the relevant functions there, only leaving
the major mode setup in org.el

I do not think that org-attach is the right place for this new
functionality.

>> Could it be extended to any mime type? If so I would avoid "image" in
>> its name. `org-yank-media-save-dir'?
>
> It should be easy enough to do it but do we want to go there?

Isn't the patch handling non-images as well?
AFAIU, the only case when images are considered separately is when image
data is in clipboard.
In theory, we might handle, for example, html markup in clipboard
specially, but I am not sure if that's what people expect.

>>> +  "Method to save images yanked from clipboard and dropped to Emacs.
>>> +It can be the symbol `attach' to add it as an attachment, or a
>>> +directory name to copy/cut the image to that directory."
>>> +  :group 'org
>>> +  :package-version '(Org . "9.7")
>>> +  :type '(choice (const :tag "Add it as attachment" attach)
>>> +                 (directory :tag "Save it in directory"))
>>> +  :safe (lambda (x) (or (stringp x) (eq x 'attach))))
>>
>> Unsure if every directory may be considered as safe (e.g. ~/.ssh/)
>
> Is there a reliable way to know which directory is "safe"?  If not, then
> let the users shoot themselves in the foot.

We can just say :safe nil (omit the keyword). Then, users will be
prompted and can decide which directories are truly safe for them.

>>> +(declare-function org-attach-attach "org-attach" (file &optional visit-dir method))
>>> +
>>> +(defun org--image-yank-media-handler (mimetype data)
>>> +  "Save image DATA of mime-type MIMETYPE and insert link at point.
>>> +It is saved as per `org-media-image-save-type'.  The name for the
>>> +image is prompted and the extension is automatically added to the
>>> +end."
>>> +  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
>>> +         (iname (read-string "Insert filename for image: "))
>>
>> Unless 'attach is used, `read-file-name' would allow saving to
>> arbitrary directory with path completion.
>
> Sorry, I don't understand what you mean here.  I am not really familiar
> with attachment facilities of org-mode.

Imagine that user enters something like
"/tmp/non-existing-directory/image-file" as an answer to
"Insert filename for image: " prompt.

>> Is there a chance to request for link description? Unsure if
>> `org-insert-link' may be easily reused.
>
> This would include too many prompts and make the whole process annoying
> again.

+1. If users want to have a link description, they can easily follow the
prompt with C-c C-l to edit the inserted link.

>> I do not know if it can be easily implemented, but it may be useful to
>> control attachment/generic directory at the moment when a particular
>> image is yanked/dropped instead of purely relying on predefined user
>> option.
>
> Sorry, I don't understand what you mean here.  Note that my usage of
> attachments is sparse.  "Particular image" is hard to determine since
> the only reliable data at hand is its mimetype.

He meant that users might want to alternate between saving to attach
dir and saving to `org-yank-image-save-type' set to custom directory.
... which might theoretically be needed, but I see it as unnecessary
over-engineering. Let's keep things simple and consider implementing
more complex behavior if we get actual feature requests.

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-25 13:14       ` Visuwesh
@ 2023-09-26 16:25         ` Max Nikulin
  2023-09-27  8:33           ` Visuwesh
  2023-10-07 11:56           ` Ihor Radchenko
  2023-10-07 12:07         ` Ihor Radchenko
  1 sibling, 2 replies; 49+ messages in thread
From: Max Nikulin @ 2023-09-26 16:25 UTC (permalink / raw)
  To: Visuwesh; +Cc: emacs-orgmode

On 25/09/2023 20:14, Visuwesh wrote:
> +  (setq-local dnd-protocol-alist
> +              (cons '("^file:///" . org--dnd-local-file-handler)
> +                    dnd-protocol-alist))

Does it mean that `org--dnd-local-file-handler' is unconditionally 
called for Org buffers? Current action is to open text files in the 
widow under cursor and it is what users may expect. They may be 
surprised if the file is attached instead.

Some applications may highlight some area and display a hint describing 
effect of drop. Unsure if it is possible in Emacs.

I have tried to drop files from Dolphin KDE file manager onto an 
Emacs-28 frame. ACTION is always 'private. In some application 
Shift/Ctrl pressed during drop means move/copy. Mouse cursor changes 
accordingly. Is it possible to achieve the same effect in Emacs?


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-26 10:24         ` Ihor Radchenko
@ 2023-09-27  8:29           ` Visuwesh
  2023-09-28 12:01             ` Max Nikulin
  0 siblings, 1 reply; 49+ messages in thread
From: Visuwesh @ 2023-09-27  8:29 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: Max Nikulin, emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 4415 bytes --]

[செவ்வாய் செப்டம்பர் 26, 2023] Ihor Radchenko wrote:

> Visuwesh <visuweshm@gmail.com> writes:
>
>>> Please, use `make-temp-file' to create files in the temporary
>>> directory that may be world writable. Predictable file names there are
>>> undesired from security point of view.
>>
>> What harm does it cause?
>
> /tmp directory can be written by any program and the file, while kept
> there, might be modified by malicious code.
>
> Not that I know a concrete example what harm can be done in this
> particular case, but it is generally a good practice to make file names
> in /tmp random.

I don't see a way in org-attach-attach's mv method to change the
basename of the file post attachment so we have to live with this harm.

>>> I am in doubts if the following code is more suitable for org.el or
>>> for org-attach.el
>>
>> I have the same doubts.
>
> The patch implements dnd and media handlers, which constitute Org mode
> integration with the rest of Emacs. So, they are a part of major mode
> definition. If we want to keep things clean, we may create org-dnd.el
> and org-yank-media.el and put the relevant functions there, only leaving
> the major mode setup in org.el

I think it would be better to keep the registration part in org-mode's
definition in that case since if a user wants to override this
functionality, they can easily do so in org-mode-hook.

> I do not think that org-attach is the right place for this new
> functionality.
>
>>> Could it be extended to any mime type? If so I would avoid "image" in
>>> its name. `org-yank-media-save-dir'?
>>
>> It should be easy enough to do it but do we want to go there?
>
> Isn't the patch handling non-images as well?
> AFAIU, the only case when images are considered separately is when image
> data is in clipboard.

The patch handles non-images in the sense that they are simply attached
using cp/mv method when they are copy/cut to the clipboard from a file
manager.

But if your clipboard data contains video/mpeg for example, then the
patch does nothing.  yank-media would complain about no registered
handlers for video/mpeg.

BTW, before I forget again for the Nth time: there's an issue with how
yank-media works so cut files will not be handled properly unless
bug#65892 gets its clearance.  I hope the description in the linked bug
is clear enough.

>>>> +  "Method to save images yanked from clipboard and dropped to Emacs.
>>>> +It can be the symbol `attach' to add it as an attachment, or a
>>>> +directory name to copy/cut the image to that directory."
>>>> +  :group 'org
>>>> +  :package-version '(Org . "9.7")
>>>> +  :type '(choice (const :tag "Add it as attachment" attach)
>>>> +                 (directory :tag "Save it in directory"))
>>>> +  :safe (lambda (x) (or (stringp x) (eq x 'attach))))
>>>
>>> Unsure if every directory may be considered as safe (e.g. ~/.ssh/)
>>
>> Is there a reliable way to know which directory is "safe"?  If not, then
>> let the users shoot themselves in the foot.
>
> We can just say :safe nil (omit the keyword). Then, users will be
> prompted and can decide which directories are truly safe for them.

That would be quite annoying IMO.  I say we let the user shoot
themselves in the foot.

>>>> +(declare-function org-attach-attach "org-attach" (file &optional visit-dir method))
>>>> +
>>>> +(defun org--image-yank-media-handler (mimetype data)
>>>> +  "Save image DATA of mime-type MIMETYPE and insert link at point.
>>>> +It is saved as per `org-media-image-save-type'.  The name for the
>>>> +image is prompted and the extension is automatically added to the
>>>> +end."
>>>> +  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
>>>> +         (iname (read-string "Insert filename for image: "))
>>>
>>> Unless 'attach is used, `read-file-name' would allow saving to
>>> arbitrary directory with path completion.
>>
>> Sorry, I don't understand what you mean here.  I am not really familiar
>> with attachment facilities of org-mode.
>
> Imagine that user enters something like
> "/tmp/non-existing-directory/image-file" as an answer to
> "Insert filename for image: " prompt.

How about the following prompt instead?

    Basename for image file without extension:

Attached patch has several of the reviews considered.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-support-for-yank-media-and-DND.patch --]
[-- Type: text/x-diff, Size: 10030 bytes --]

From a11658f82ce71850b52b853a4b44055b8f917486 Mon Sep 17 00:00:00 2001
From: Visuwesh <visuweshm@gmail.com>
Date: Fri, 22 Sep 2023 20:11:41 +0530
Subject: [PATCH] Add support for yank-media and DND

* lisp/org.el (org-mode): Call the setup function for yank-media and
DND.
(org-setup-yank-dnd-handlers): Register yank-media-handler and DND
handler.
(org-yank-image-save-type, org-dnd-default-attach-method)
(org-yank-image-file-name-function): New defcustoms.
(org--image-yank-media-handler, org--copied-files-yank-media-handler)
(org--dnd-local-file-handler): Add yank-media and DND handlers.

* etc/ORG-NEWS: Advertise the new features.
---
 etc/ORG-NEWS |  20 ++++++
 lisp/org.el  | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 189 insertions(+), 1 deletion(-)

diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 252c5a9f9..c4a58dd4d 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -596,6 +596,26 @@ return a matplotlib Figure object to plot. For output results, the
 current figure (as returned by =pyplot.gcf()=) is cleared before
 evaluation, and then plotted afterwards.
 
+*** Images and files in clipboard can be attached
+
+Org can now attach images in clipboard and files copied/cut to the
+clipboard from file managers using the ~yank-media~ command which also
+inserts a link to the attached file.  This command was added in Emacs 29.
+
+Images can be saved to a separate directory instead of being attached,
+customize ~org-yank-image-save-type~.
+
+Image filename chosen can be customized by setting
+~org-yank-image-file-name-function~ which by default autogenerates a
+filename based on the current time.
+
+*** Files and images can be attached by dropping onto Emacs
+
+Attachment method other than ~org-attach-method~ for dropped files can
+be specified using ~org-dnd-default-attach-method~.
+
+Images dropped also respect the value of ~org-yank-image-save-type~.
+
 ** New functions and changes in function arguments
 *** =TYPES= argument in ~org-element-lineage~ can now be a symbol
 
diff --git a/lisp/org.el b/lisp/org.el
index d0b2355ea..bd36ec4e8 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -4999,7 +4999,10 @@ The following commands are available:
   (org--set-faces-extend '(org-block-begin-line org-block-end-line)
                          org-fontify-whole-block-delimiter-line)
   (org--set-faces-extend org-level-faces org-fontify-whole-heading-line)
-  (setq-local org-mode-loading nil))
+  (setq-local org-mode-loading nil)
+
+  ;; `yank-media' handler and DND support.
+  (org-setup-yank-dnd-handlers))
 
 ;; Update `customize-package-emacs-version-alist'
 (add-to-list 'customize-package-emacs-version-alist
@@ -20254,6 +20257,171 @@ it has a `diary' type."
 		    (org-format-timestamp timestamp fmt t))
 	  (org-format-timestamp timestamp fmt (eq boundary 'end)))))))
 
+;;; Yank media handler and DND
+(defun org-setup-yank-dnd-handlers ()
+  "Setup the `yank-media' and DND handlers for buffer."
+  (setq-local dnd-protocol-alist
+              (cons '("^file:///" . org--dnd-local-file-handler)
+                    dnd-protocol-alist))
+  (when (fboundp 'yank-media-handler)
+    (yank-media-handler "image/.*" #'org--image-yank-media-handler)
+    ;; Looks like different DEs go for different handler names,
+    ;; https://larsee.com/blog/2019/05/clipboard-files/.
+    (yank-media-handler "x/special-\\(?:gnome\|KDE\|mate\\)-files"
+                        #'org--copied-files-yank-media-handler)))
+
+(defcustom org-yank-image-save-type 'attach
+  "Method to save images yanked from clipboard and dropped to Emacs.
+It can be the symbol `attach' to add it as an attachment, or a
+directory name to copy/cut the image to that directory."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Add it as attachment" attach)
+                 (directory :tag "Save it in directory"))
+  :safe (lambda (x) (or (stringp x) (eq x 'attach))))
+
+(defcustom org-yank-image-file-name-function #'org-yank-image-autogen-filename
+  "Function to generate filename for image yanked from clipboard.
+By default, this autogenerates a filename based on the current
+time.
+It is called with no arguments and should return a string without
+any extension which is used as the filename."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(radio (function-item :doc "Autogenerate filename"
+                               org-yank-image-autogen-filename)
+                (function-item :doc "Ask for filename"
+                               org-yank-image-read-filename)
+                function))
+
+(defun org-yank-image-autogen-filename ()
+  "Autogenerate filename for image in clipboard."
+  (format-time-string "clipboard-%Y-%m-%d-%H:%M"))
+
+(defun org-yank-image-read-filename ()
+  "Read filename for image in clipboard."
+  (read-string "Basename for image file without extension: "))
+
+(declare-function org-attach-attach "org-attach" (file &optional visit-dir method))
+
+(defun org--image-yank-media-handler (mimetype data)
+  "Save image DATA of mime-type MIMETYPE and insert link at point.
+It is saved as per `org-yank-image-save-type'.  The name for the
+image is prompted and the extension is automatically added to the
+end."
+  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
+         (iname (funcall org-yank-image-file-name-function))
+         (filename (file-name-with-extension iname ext))
+         (absname (expand-file-name
+                   filename
+                   (if (eq org-yank-image-save-type 'attach)
+                       temporary-file-directory
+                     org-yank-image-save-type)))
+         link)
+    (when (and (not (eq org-yank-image-save-type 'attach))
+               (not (file-directory-p org-yank-image-save-type)))
+      (make-directory org-yank-image-save-type t))
+    (with-temp-file absname
+      (insert data))
+    (if (null (eq org-yank-image-save-type 'attach))
+        (setq link (org-link-make-string
+                    (concat "file:" (file-relative-name absname))
+                    filename))
+      (require 'org-attach)
+      (org-attach-attach absname nil 'mv)
+      (setq link (org-link-make-string
+                  (concat "attachment:" filename)
+                  filename)))
+    (insert link)))
+
+;; I cannot find a spec for this but
+;; https://indigo.re/posts/2021-12-21-clipboard-data.html and pcmanfm
+;; suggests that this is the format.
+(defun org--copied-files-yank-media-handler (_mimetype data)
+  "Attach copied or cut files from file manager.
+If the files were cut from the file manager, then the `mv' attach
+method is used; `cp' otherwise.
+
+DATA is a string where the first line is the operation to
+perform: copy or cut.  Rest of the lines are file: links to the
+concerned files."
+  (require 'org-attach)
+  ;; pcmanfm adds a null byte at the end for some reason.
+  (let* ((data (split-string data "[\0\n\r]" t "^file://"))
+         (files (cdr data))
+         (method (if (equal (car data) "cut")
+                     'mv
+                   'cp)))
+    (dolist (f files)
+      (setq f (url-unhex-string f))
+      (if (file-readable-p f)
+          (org-attach-attach f nil method)
+        (message "File `%s' is not readable, skipping" f)))))
+
+(defcustom org-dnd-default-attach-method nil
+  "Default attach method to use when DND action is unspecified.
+This attach method is used when the DND action is `private'.
+This is also used when `org-yank-image-save-type' is nil.
+When nil, use `org-attach-method'."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Default attach method" nil)
+                 (const :tag "Copy" cp)
+                 (const :tag "Move" mv)
+                 (const :tag "Hard link" ln)
+                 (const :tag "Symbolic link" lns)))
+
+(declare-function mailcap-file-name-to-mime-type "mailcap" (file-name))
+(defvar org-attach-method)
+
+(defun org--dnd-local-file-handler (url action)
+  "Attach filename given by URL using method pertaining to ACTION.
+If ACTION is `move', use `mv' attach method.
+If `copy', use `cp' attach method.
+If `ask', ask the user.
+If `private', use the method denoted in `org-dnd-default-attach-action'.
+The action `private' is always returned."
+  (require 'mailcap)
+  (let* ((filename (dnd-get-local-file-name url))
+         (mimetype (mailcap-file-name-to-mime-type filename))
+         (separatep (and (string-prefix-p "image/" mimetype)
+                         (not (eq 'attach org-yank-image-save-type))))
+         (method (pcase action
+                   ('copy 'cp)
+                   ('move 'mv)
+                   ('ask (caddr (org-mks
+                                 '(("c" "Copy" cp)
+                                   ("m" "Move" mv)
+                                   ("l" "Hard link" ln)
+                                   ("s" "Symbolic link" lns))
+                                 "How to attach?"
+                                 "Attach using method")))
+                   ('private (or org-dnd-default-attach-method
+                                 org-attach-method)))))
+    (if separatep
+        (funcall
+         (pcase method
+           ('cp #'copy-file)
+           ('mv #'rename-file)
+           ('ln #'add-name-to-file)
+           ('lns #'make-symbolic-link))
+         filename
+         (expand-file-name (file-name-nondirectory filename)
+                           org-yank-image-save-type))
+      (org-attach-attach filename nil method))
+    (insert
+     (org-link-make-string
+      (concat (if separatep
+                  "file:"
+                "attachment:")
+              (if separatep
+                  (expand-file-name (file-name-nondirectory filename)
+                                    org-yank-image-save-type)
+                (file-name-nondirectory filename)))
+      (file-name-nondirectory filename))
+     "\n")
+    'private))
+
 ;;; Other stuff
 
 (defvar reftex-docstruct-symbol)
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-26 16:25         ` Max Nikulin
@ 2023-09-27  8:33           ` Visuwesh
  2023-10-07 11:56           ` Ihor Radchenko
  1 sibling, 0 replies; 49+ messages in thread
From: Visuwesh @ 2023-09-27  8:33 UTC (permalink / raw)
  To: emacs-orgmode

[செவ்வாய் செப்டம்பர் 26, 2023] Max Nikulin wrote:

> On 25/09/2023 20:14, Visuwesh wrote:
>> +  (setq-local dnd-protocol-alist
>> +              (cons '("^file:///" . org--dnd-local-file-handler)
>> +                    dnd-protocol-alist))
>
> Does it mean that `org--dnd-local-file-handler' is unconditionally
> called for Org buffers? Current action is to open text files in the
> widow under cursor and it is what users may expect. They may be
> surprised if the file is attached instead.

The common request I see when a file is dropped is to associate it
somehow with the org-mode buffer.  org-attach is the most natural way to
do it.  If the users like the old behaviour, they can remove the entry
from the alist.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-27  8:29           ` Visuwesh
@ 2023-09-28 12:01             ` Max Nikulin
  0 siblings, 0 replies; 49+ messages in thread
From: Max Nikulin @ 2023-09-28 12:01 UTC (permalink / raw)
  To: emacs-orgmode; +Cc: Visuwesh

On 27/09/2023 15:29, Visuwesh wrote:
> Ihor Radchenko wrote:
>> We can just say :safe nil (omit the keyword). Then, users will be >> prompted and can decide which directories are truly safe for them.> 
That would be quite annoying IMO.  I say we let the user shoot> 
themselves in the foot.
The patch implements 2 features:
- attach to an Org file,
- save to arbitrary directory.

Second option is not specific to Org, so I would consider it in the 
context of Emacs core or an independent ELPA package. I am unsure if 
this is a bright idea, but it eliminates most of questions.

Concerning security issues, there is demand like

> I wish to open Org files by using EWW.
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=58774#34

So a file from net may have arbitrary local variables.

On 27/09/2023 15:33, Visuwesh wrote:
> Max Nikulin wrote:
>>
>> Does it mean that `org--dnd-local-file-handler' is unconditionally
>> called for Org buffers? Current action is to open text files in the
>> widow under cursor and it is what users may expect. They may be
>> surprised if the file is attached instead.
> 
> The common request I see when a file is dropped is to associate it
> somehow with the org-mode buffer.  org-attach is the most natural way to
> do it.  If the users like the old behaviour, they can remove the entry
> from the alist.

I am unsure concerning opt-in vs. opt-out choice. I do not follow 
discussions e.g. on emacs help and on reddit, so I can not estimate if 
voice of users happy with opening file and discovered changed defaults 
would be louder.



^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
@ 2023-09-29  8:20 Liu Hui
  2023-10-01 14:28 ` Visuwesh
  0 siblings, 1 reply; 49+ messages in thread
From: Liu Hui @ 2023-09-29  8:20 UTC (permalink / raw)
  To: Visuwesh, Ihor Radchenko; +Cc: Max Nikulin, emacs-orgmode

Hi,

在 2023/9/27 16:29, Visuwesh 写道:

> +*** Files and images can be attached by dropping onto Emacs
> +
> +Attachment method other than ~org-attach-method~ for dropped files can
> +be specified using ~org-dnd-default-attach-method~.

> +(defcustom org-dnd-default-attach-method nil
> +  "Default attach method to use when DND action is unspecified.
> +This attach method is used when the DND action is `private'.
> +This is also used when `org-yank-image-save-type' is nil.
> +When nil, use `org-attach-method'."

I think the dnd feature should not be restricted to org-attach. I have
often used it for opening file and inserting file link. How about
supporting them and adding a new variable, such as
org-dnd-default-method?

> +                   ('ask (caddr (org-mks
> +                                 '(("c" "Copy" cp)
> +                                   ("m" "Move" mv)
> +                                   ("l" "Hard link" ln)
> +                                   ("s" "Symbolic link" lns))
> +                                 "How to attach?"
> +                                 "Attach using method")))

It is better to pop up a menu that allows users to proceed with the
mouse, e.g. that in `dired-dnd-do-ask-action'. Options like
'open'/'file link' could be added too.

Thanks for your work.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-29  8:20 Liu Hui
@ 2023-10-01 14:28 ` Visuwesh
  2023-10-02  0:28   ` Liu Hui
  0 siblings, 1 reply; 49+ messages in thread
From: Visuwesh @ 2023-10-01 14:28 UTC (permalink / raw)
  To: Liu Hui; +Cc: Ihor Radchenko, Max Nikulin, emacs-orgmode

[வெள்ளி செப்டம்பர் 29, 2023] Liu Hui wrote:

> Hi,
>
> 在 2023/9/27 16:29, Visuwesh 写道:
>
>> +*** Files and images can be attached by dropping onto Emacs
>> +
>> +Attachment method other than ~org-attach-method~ for dropped files can
>> +be specified using ~org-dnd-default-attach-method~.
>
>> +(defcustom org-dnd-default-attach-method nil
>> +  "Default attach method to use when DND action is unspecified.
>> +This attach method is used when the DND action is `private'.
>> +This is also used when `org-yank-image-save-type' is nil.
>> +When nil, use `org-attach-method'."
>
> I think the dnd feature should not be restricted to org-attach. I have
> often used it for opening file and inserting file link. How about
> supporting them and adding a new variable, such as
> org-dnd-default-method?

Thanks for your feedback.  Do you think asking during the time of drop
would be a viable option too?  I'm thinking of adding the defcustom
org-dnd-default-method with options such as

    . attach -- as implemented here
    . open -- open file
    . file-link -- insert file links
    . ask -- ask what to do after drop

I think the `ask' option would be nice to have too.  Perhaps, even as
the default?

>> +                   ('ask (caddr (org-mks
>> +                                 '(("c" "Copy" cp)
>> +                                   ("m" "Move" mv)
>> +                                   ("l" "Hard link" ln)
>> +                                   ("s" "Symbolic link" lns))
>> +                                 "How to attach?"
>> +                                 "Attach using method")))
>
> It is better to pop up a menu that allows users to proceed with the
> mouse, e.g. that in `dired-dnd-do-ask-action'. Options like
> 'open'/'file link' could be added too.

Thanks, I will go back to using rmc then.  It shows a nice dialog box
when you use the mouse.

> Thanks for your work.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-01 14:28 ` Visuwesh
@ 2023-10-02  0:28   ` Liu Hui
  0 siblings, 0 replies; 49+ messages in thread
From: Liu Hui @ 2023-10-02  0:28 UTC (permalink / raw)
  To: Visuwesh; +Cc: Ihor Radchenko, Max Nikulin, emacs-orgmode

Visuwesh <visuweshm@gmail.com> 于2023年10月1日周日 22:28写道:

>  Do you think asking during the time of drop
> would be a viable option too?  I'm thinking of adding the defcustom
> org-dnd-default-method with options such as
>
>     . attach -- as implemented here
>     . open -- open file
>     . file-link -- insert file links
>     . ask -- ask what to do after drop
>
> I think the `ask' option would be nice to have too.  Perhaps, even as
> the default?

Yes, I totally agree with asking as the default.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-22 14:52 ` [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)] Visuwesh
                     ` (2 preceding siblings ...)
  2023-09-24 14:49   ` Max Nikulin
@ 2023-10-06  7:34   ` Po Lu
  3 siblings, 0 replies; 49+ messages in thread
From: Po Lu @ 2023-10-06  7:34 UTC (permalink / raw)
  To: Visuwesh; +Cc: emacs-orgmode

Visuwesh <visuweshm@gmail.com> writes:


> Attached patch adds yank-media and DND handler to attach files in the
> clipboard and dropped onto an Emacs frame respectively.
>
> The yank-media handler for images is well tested, I use it frequently
> however, rest of the stuff aren't really tested since I don't use a GUI
> file manager.  I tested enough to make sure the logic is right so I
> don't know if they are ergonomic enough.  As noted in the comments,
> files copied/cut to clipboard doesn't seem to have a solid spec and most
> of the code was written with pcmanfm as the file manager, testing with
> other file managers are highly welcome.
>
> Better names for the newly added defcustoms are highly welcome since I
> don't find them to be clear enough from their name.
>
> The patch compiles cleanly without warnings but I haven't tested _this_
> patch yet, I have these functions in my init.el and have tested those.

Thanks.  You have only implemented handlers for one of the two protocols
employed by X programs to perform drag-and-drop.

Lisp code receiving dropped files must also implement an XDS handler,
absent which files dropped from web browsers such as Firefox will merely
be saved into the default-directory.  XDS is distinct from the regular
drag-and-drop system in that Emacs is enjoined by the drag source to
provide a file name under which the file should be saved, whereas
regular drag-and-drop transpires once it has already been saved by the
drag source.  Refer to (elisp)Drag and Drop.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-26 16:25         ` Max Nikulin
  2023-09-27  8:33           ` Visuwesh
@ 2023-10-07 11:56           ` Ihor Radchenko
  1 sibling, 0 replies; 49+ messages in thread
From: Ihor Radchenko @ 2023-10-07 11:56 UTC (permalink / raw)
  To: Max Nikulin; +Cc: Visuwesh, emacs-orgmode

Max Nikulin <manikulin@gmail.com> writes:

> ... Current action is to open text files in the 
> widow under cursor and it is what users may expect.

This is not always true. For example, dropping to dired buffers will
copy to the opened fired directory. Dropping to message buffers will add
an attachment to the email being written.

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-09-25 13:14       ` Visuwesh
  2023-09-26 16:25         ` Max Nikulin
@ 2023-10-07 12:07         ` Ihor Radchenko
  2023-10-07 12:27           ` Visuwesh
  1 sibling, 1 reply; 49+ messages in thread
From: Ihor Radchenko @ 2023-10-07 12:07 UTC (permalink / raw)
  To: Visuwesh; +Cc: emacs-orgmode

Visuwesh <visuweshm@gmail.com> writes:

> Attached patch considers your review and also another defcustom to tell
> how to generate the filename which by default autogenerates a filename
> based on current time.

Thanks!
I still disagree about :safe, but I can change it myself to something
more restrictive like :safe (lambda (x) (eq x 'attach)))

Also, it looks like we also need to define `x-dnd-direct-save-function',
as Po Lu pointed in https://list.orgmode.org/orgmode/87bkdccihf.fsf@yahoo.com/ 

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-07 12:07         ` Ihor Radchenko
@ 2023-10-07 12:27           ` Visuwesh
  2023-10-07 12:36             ` Ihor Radchenko
  2023-10-07 14:03             ` Visuwesh
  0 siblings, 2 replies; 49+ messages in thread
From: Visuwesh @ 2023-10-07 12:27 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: emacs-orgmode

[சனி அக்டோபர் 07, 2023] Ihor Radchenko wrote:

> Visuwesh <visuweshm@gmail.com> writes:
>
>> Attached patch considers your review and also another defcustom to tell
>> how to generate the filename which by default autogenerates a filename
>> based on current time.
>
> Thanks!
> I still disagree about :safe, but I can change it myself to something
> more restrictive like :safe (lambda (x) (eq x 'attach)))

OK, I will change it on my end then.  I can always override the :safe
function on my end.

> Also, it looks like we also need to define `x-dnd-direct-save-function',
> as Po Lu pointed in https://list.orgmode.org/orgmode/87bkdccihf.fsf@yahoo.com/ 

I will get to it, hopefully by Sunday.

What do you think about adding a defcustom as requested by Liu Hui in
https://yhetil.org/orgmode/CAOQTW-P6_w3HeepjFtKCFey2RPfGJFOUhWMSEOE2AjNPoi+N+Q@mail.gmail.com/


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-07 12:27           ` Visuwesh
@ 2023-10-07 12:36             ` Ihor Radchenko
  2023-10-07 14:03             ` Visuwesh
  1 sibling, 0 replies; 49+ messages in thread
From: Ihor Radchenko @ 2023-10-07 12:36 UTC (permalink / raw)
  To: Visuwesh; +Cc: emacs-orgmode

Visuwesh <visuweshm@gmail.com> writes:

> What do you think about adding a defcustom as requested by Liu Hui in
> https://yhetil.org/orgmode/CAOQTW-P6_w3HeepjFtKCFey2RPfGJFOUhWMSEOE2AjNPoi+N+Q@mail.gmail.com/

Looks reasonable. I have no objections.

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-07 12:27           ` Visuwesh
  2023-10-07 12:36             ` Ihor Radchenko
@ 2023-10-07 14:03             ` Visuwesh
  2023-10-08  9:30               ` Ihor Radchenko
  1 sibling, 1 reply; 49+ messages in thread
From: Visuwesh @ 2023-10-07 14:03 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 894 bytes --]

[சனி அக்டோபர் 07, 2023] Visuwesh wrote:

> [சனி அக்டோபர் 07, 2023] Ihor Radchenko wrote:
>
>> Visuwesh <visuweshm@gmail.com> writes:
>>
>>> Attached patch considers your review and also another defcustom to tell
>>> how to generate the filename which by default autogenerates a filename
>>> based on current time.
>>
>> Thanks!
>> I still disagree about :safe, but I can change it myself to something
>> more restrictive like :safe (lambda (x) (eq x 'attach)))
>
> OK, I will change it on my end then.  I can always override the :safe
> function on my end.
>
>> Also, it looks like we also need to define `x-dnd-direct-save-function',
>> as Po Lu pointed in https://list.orgmode.org/orgmode/87bkdccihf.fsf@yahoo.com/ 
>
> I will get to it, hopefully by Sunday.

This was far easier than I initially thought.  Patch attached.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-support-for-yank-media-and-DND.patch --]
[-- Type: text/x-diff, Size: 13104 bytes --]

From 7bdd892c0cdb248341e3284e9aeee341f073d38d Mon Sep 17 00:00:00 2001
From: Visuwesh <visuweshm@gmail.com>
Date: Fri, 22 Sep 2023 20:11:41 +0530
Subject: [PATCH] Add support for yank-media and DND

* lisp/org.el (org-mode): Call the setup function for yank-media and
DND.
(org-setup-yank-dnd-handlers): Register yank-media-handler and DND
handler.
(org-yank-image-save-type, org-yank-image-file-name-function)
(org-dnd-default-attach-method, org-dnd-method): New defcustoms.
(org--image-yank-media-handler, org--copied-files-yank-media-handler)
(org--dnd-attach-file, org--dnd-local-file-handler, org--dnd-xds-method)
(org--dnd-xds-function): Add yank-media and DND handlers.

* etc/ORG-NEWS: Advertise the new features.
---
 etc/ORG-NEWS |  20 +++++
 lisp/org.el  | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 253 insertions(+), 1 deletion(-)

diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 252c5a9f9..c4a58dd4d 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -596,6 +596,26 @@ return a matplotlib Figure object to plot. For output results, the
 current figure (as returned by =pyplot.gcf()=) is cleared before
 evaluation, and then plotted afterwards.
 
+*** Images and files in clipboard can be attached
+
+Org can now attach images in clipboard and files copied/cut to the
+clipboard from file managers using the ~yank-media~ command which also
+inserts a link to the attached file.  This command was added in Emacs 29.
+
+Images can be saved to a separate directory instead of being attached,
+customize ~org-yank-image-save-type~.
+
+Image filename chosen can be customized by setting
+~org-yank-image-file-name-function~ which by default autogenerates a
+filename based on the current time.
+
+*** Files and images can be attached by dropping onto Emacs
+
+Attachment method other than ~org-attach-method~ for dropped files can
+be specified using ~org-dnd-default-attach-method~.
+
+Images dropped also respect the value of ~org-yank-image-save-type~.
+
 ** New functions and changes in function arguments
 *** =TYPES= argument in ~org-element-lineage~ can now be a symbol
 
diff --git a/lisp/org.el b/lisp/org.el
index d0b2355ea..cfb314e23 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -4999,7 +4999,10 @@ The following commands are available:
   (org--set-faces-extend '(org-block-begin-line org-block-end-line)
                          org-fontify-whole-block-delimiter-line)
   (org--set-faces-extend org-level-faces org-fontify-whole-heading-line)
-  (setq-local org-mode-loading nil))
+  (setq-local org-mode-loading nil)
+
+  ;; `yank-media' handler and DND support.
+  (org-setup-yank-dnd-handlers))
 
 ;; Update `customize-package-emacs-version-alist'
 (add-to-list 'customize-package-emacs-version-alist
@@ -20254,6 +20257,235 @@ it has a `diary' type."
 		    (org-format-timestamp timestamp fmt t))
 	  (org-format-timestamp timestamp fmt (eq boundary 'end)))))))
 
+;;; Yank media handler and DND
+(defun org-setup-yank-dnd-handlers ()
+  "Setup the `yank-media' and DND handlers for buffer."
+  (setq-local dnd-protocol-alist
+              (cons '("^file:///" . org--dnd-local-file-handler)
+                    dnd-protocol-alist))
+  (when (fboundp 'yank-media-handler)
+    (yank-media-handler "image/.*" #'org--image-yank-media-handler)
+    ;; Looks like different DEs go for different handler names,
+    ;; https://larsee.com/blog/2019/05/clipboard-files/.
+    (yank-media-handler "x/special-\\(?:gnome\|KDE\|mate\\)-files"
+                        #'org--copied-files-yank-media-handler))
+  (when (boundp 'x-dnd-direct-save-function)
+    (setq-local x-dnd-direct-save-function #'org--dnd-xds-function)))
+
+(defcustom org-yank-image-save-type 'attach
+  "Method to save images yanked from clipboard and dropped to Emacs.
+It can be the symbol `attach' to add it as an attachment, or a
+directory name to copy/cut the image to that directory."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Add it as attachment" attach)
+                 (directory :tag "Save it in directory"))
+  :safe (lambda (x) (eq x 'attach)))
+
+(defcustom org-yank-image-file-name-function #'org-yank-image-autogen-filename
+  "Function to generate filename for image yanked from clipboard.
+By default, this autogenerates a filename based on the current
+time.
+It is called with no arguments and should return a string without
+any extension which is used as the filename."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(radio (function-item :doc "Autogenerate filename"
+                               org-yank-image-autogen-filename)
+                (function-item :doc "Ask for filename"
+                               org-yank-image-read-filename)
+                function))
+
+(defun org-yank-image-autogen-filename ()
+  "Autogenerate filename for image in clipboard."
+  (format-time-string "clipboard-%Y-%m-%d-%H:%M"))
+
+(defun org-yank-image-read-filename ()
+  "Read filename for image in clipboard."
+  (read-string "Basename for image file without extension: "))
+
+(declare-function org-attach-attach "org-attach" (file &optional visit-dir method))
+
+(defun org--image-yank-media-handler (mimetype data)
+  "Save image DATA of mime-type MIMETYPE and insert link at point.
+It is saved as per `org-yank-image-save-type'.  The name for the
+image is prompted and the extension is automatically added to the
+end."
+  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
+         (iname (funcall org-yank-image-file-name-function))
+         (filename (file-name-with-extension iname ext))
+         (absname (expand-file-name
+                   filename
+                   (if (eq org-yank-image-save-type 'attach)
+                       temporary-file-directory
+                     org-yank-image-save-type)))
+         link)
+    (when (and (not (eq org-yank-image-save-type 'attach))
+               (not (file-directory-p org-yank-image-save-type)))
+      (make-directory org-yank-image-save-type t))
+    (with-temp-file absname
+      (insert data))
+    (if (null (eq org-yank-image-save-type 'attach))
+        (setq link (org-link-make-string
+                    (concat "file:" (file-relative-name absname))
+                    filename))
+      (require 'org-attach)
+      (org-attach-attach absname nil 'mv)
+      (setq link (org-link-make-string
+                  (concat "attachment:" filename)
+                  filename)))
+    (insert link)))
+
+;; I cannot find a spec for this but
+;; https://indigo.re/posts/2021-12-21-clipboard-data.html and pcmanfm
+;; suggests that this is the format.
+(defun org--copied-files-yank-media-handler (_mimetype data)
+  "Attach copied or cut files from file manager.
+If the files were cut from the file manager, then the `mv' attach
+method is used; `cp' otherwise.
+
+DATA is a string where the first line is the operation to
+perform: copy or cut.  Rest of the lines are file: links to the
+concerned files."
+  (require 'org-attach)
+  ;; pcmanfm adds a null byte at the end for some reason.
+  (let* ((data (split-string data "[\0\n\r]" t "^file://"))
+         (files (cdr data))
+         (method (if (equal (car data) "cut")
+                     'mv
+                   'cp)))
+    (dolist (f files)
+      (setq f (url-unhex-string f))
+      (if (file-readable-p f)
+          (org-attach-attach f nil method)
+        (message "File `%s' is not readable, skipping" f)))))
+
+(defcustom org-dnd-method 'ask
+  "Action to perform on the dropped file.
+When the value is the symbol,
+  . `attach' -- attach the dropped file
+  . `open' -- visit/open the dropped file in Emacs
+  . `file-link' -- insert file: link to the dropped file
+  . `ask' -- ask what to do out of the above."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Attach" attach)
+                 (const :tag "Open/Visit file" open)
+                 (const :tag "Insert file: link" file-link)
+                 (const :tag "Ask what to do" ask)))
+
+(defcustom org-dnd-default-attach-method nil
+  "Default attach method to use when DND action is unspecified.
+This attach method is used when the DND action is `private'.
+This is also used when `org-yank-image-save-type' is nil.
+When nil, use `org-attach-method'."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Default attach method" nil)
+                 (const :tag "Copy" cp)
+                 (const :tag "Move" mv)
+                 (const :tag "Hard link" ln)
+                 (const :tag "Symbolic link" lns)))
+
+(declare-function mailcap-file-name-to-mime-type "mailcap" (file-name))
+(defvar org-attach-method)
+
+(defun org--dnd-local-file-handler (url action)
+  (let ((method (if (eq org-dnd-method 'ask)
+                    (caddr (read-multiple-choice
+                            "What to do with dropped file?"
+                            '((?a "attach" attach)
+                              (?o "open" open)
+                              (?f "insert file: link" file-link))))
+                  org-dnd-method)))
+    (pcase method
+      (`attach (org--dnd-attach-file url action))
+      (`open (dnd-open-local-file url action))
+      (`file-link
+       (let ((filename (dnd-get-local-file-name url)))
+         (insert (org-make-link-string (concat "file:" filename)
+                                       (file-name-nondirectory filename))))))))
+
+(defun org--dnd-attach-file (url action)
+  "Attach filename given by URL using method pertaining to ACTION.
+If ACTION is `move', use `mv' attach method.
+If `copy', use `cp' attach method.
+If `ask', ask the user.
+If `private', use the method denoted in `org-dnd-default-attach-action'.
+The action `private' is always returned."
+  (require 'mailcap)
+  (let* ((filename (dnd-get-local-file-name url))
+         (mimetype (mailcap-file-name-to-mime-type filename))
+         (separatep (and (string-prefix-p "image/" mimetype)
+                         (not (eq 'attach org-yank-image-save-type))))
+         (method (pcase action
+                   ('copy 'cp)
+                   ('move 'mv)
+                   ('ask (caddr (read-multiple-choice
+                                 "Attach using method"
+                                 '((?c "copy" cp)
+                                   (?m "move" mv)
+                                   (?l "hard link" ln)
+                                   (?s "symbolic link" lns)))))
+                   ('private (or org-dnd-default-attach-method
+                                 org-attach-method)))))
+    (if separatep
+        (funcall
+         (pcase method
+           ('cp #'copy-file)
+           ('mv #'rename-file)
+           ('ln #'add-name-to-file)
+           ('lns #'make-symbolic-link))
+         filename
+         (expand-file-name (file-name-nondirectory filename)
+                           org-yank-image-save-type))
+      (org-attach-attach filename nil method))
+    (insert
+     (org-link-make-string
+      (concat (if separatep
+                  "file:"
+                "attachment:")
+              (if separatep
+                  (expand-file-name (file-name-nondirectory filename)
+                                    org-yank-image-save-type)
+                (file-name-nondirectory filename)))
+      (file-name-nondirectory filename))
+     "\n")
+    'private))
+
+(defvar-local org--dnd-xds-method nil
+  "The method to use for dropped file.")
+(defun org--dnd-xds-function (need-name filename)
+  "Handle file with FILENAME dropped via XDS protocol.
+When NEED-NAME is t, FILNAME is the base name of the file to be
+saved.
+When NEED-NAME is nil, the drop is complete."
+  (message "%S %S" need-name filename)
+  (if need-name
+      (let ((method (if (eq org-dnd-method 'ask)
+                        (caddr (read-multiple-choice
+                                "What to do with dropped file?"
+                                '((?a "attach" attach)
+                                  (?o "open" open)
+                                  (?f "insert file: link" file-link))))
+                      org-dnd-method)))
+        (setq-local org--dnd-xds-method method)
+        (pcase method
+          (`attach (expand-file-name filename (org-attach-dir 'create)))
+          (`open (expand-file-name (make-temp-name "emacs.") temporary-file-directory))
+          (`file-link (read-file-name "Write file to: " nil nil nil filename))))
+    (pcase org--dnd-xds-method
+      (`attach (let ((base (file-name-nondirectory filename)))
+                 (insert (org-make-link-string
+                          (concat "attachment:" base)
+                          base)
+                         "\n")))
+      (`file-link (insert (org-make-link-string (concat "file:" filename)
+                                                (file-name-nondirectory filename))
+                          "\n"))
+      (`open (find-file filename)))
+    (setq-local org--dnd-xds-method nil)))
+
 ;;; Other stuff
 
 (defvar reftex-docstruct-symbol)
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-07 14:03             ` Visuwesh
@ 2023-10-08  9:30               ` Ihor Radchenko
  2023-10-08 11:21                 ` Visuwesh
  0 siblings, 1 reply; 49+ messages in thread
From: Ihor Radchenko @ 2023-10-08  9:30 UTC (permalink / raw)
  To: Visuwesh; +Cc: emacs-orgmode

Visuwesh <visuweshm@gmail.com> writes:

> This was far easier than I initially thought.  Patch attached.

Thanks!
I tried to install the patch and I have two more comments.

> +(defun org-yank-image-autogen-filename ()
> +  "Autogenerate filename for image in clipboard."
> +  (format-time-string "clipboard-%Y-%m-%d-%H:%M"))

This is too coarse - no more than a single unique image name per minute.
What about the default value of `org-id-ts-format' - "%Y%m%dT%H%M%S.%6N"?
This is guaranteed to be unique.

> +(defun org--image-yank-media-handler (mimetype data)
> +  "Save image DATA of mime-type MIMETYPE and insert link at point.
> +It is saved as per `org-yank-image-save-type'.  The name for the
> +image is prompted and the extension is automatically added to the
> +end."
> ...
> +    (if (null (eq org-yank-image-save-type 'attach))
> +        (setq link (org-link-make-string
> +                    (concat "file:" (file-relative-name absname))
> +                    filename))

I do not like that the inserted image link is with description. Images
with description cannot be previewed by default. I think that no
description would be more handy as one could then M-x yank-media
followed by M-x org-display-inline-images to see the inserted image
immediately.

> +     (org-link-make-string
> +      (concat (if separatep
> +                  "file:"
> +                "attachment:")
> +              (if separatep
> +                  (expand-file-name (file-name-nondirectory filename)
> +                                    org-yank-image-save-type)
> +                (file-name-nondirectory filename)))

Same here.

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-08  9:30               ` Ihor Radchenko
@ 2023-10-08 11:21                 ` Visuwesh
  2023-10-09 11:12                   ` Ihor Radchenko
  0 siblings, 1 reply; 49+ messages in thread
From: Visuwesh @ 2023-10-08 11:21 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 1509 bytes --]

[ஞாயிறு அக்டோபர் 08, 2023] Ihor Radchenko wrote:

> Visuwesh <visuweshm@gmail.com> writes:
>
>> This was far easier than I initially thought.  Patch attached.
>
> Thanks!
> I tried to install the patch and I have two more comments.
>
>> +(defun org-yank-image-autogen-filename ()
>> +  "Autogenerate filename for image in clipboard."
>> +  (format-time-string "clipboard-%Y-%m-%d-%H:%M"))
>
> This is too coarse - no more than a single unique image name per minute.
> What about the default value of `org-id-ts-format' - "%Y%m%dT%H%M%S.%6N"?
> This is guaranteed to be unique.

OK, now done.

>> +(defun org--image-yank-media-handler (mimetype data)
>> +  "Save image DATA of mime-type MIMETYPE and insert link at point.
>> +It is saved as per `org-yank-image-save-type'.  The name for the
>> +image is prompted and the extension is automatically added to the
>> +end."
>> ...
>> +    (if (null (eq org-yank-image-save-type 'attach))
>> +        (setq link (org-link-make-string
>> +                    (concat "file:" (file-relative-name absname))
>> +                    filename))
>
> I do not like that the inserted image link is with description. Images
> with description cannot be previewed by default. I think that no
> description would be more handy as one could then M-x yank-media
> followed by M-x org-display-inline-images to see the inserted image
> immediately.

I didn't know about this.  Now adjusted in the attached patch.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-support-for-yank-media-and-DND.patch --]
[-- Type: text/x-diff, Size: 12697 bytes --]

From f2d13548b970ebde90ea0a04be7951ef92d220a9 Mon Sep 17 00:00:00 2001
From: Visuwesh <visuweshm@gmail.com>
Date: Fri, 22 Sep 2023 20:11:41 +0530
Subject: [PATCH] Add support for yank-media and DND

* lisp/org.el (org-mode): Call the setup function for yank-media and
DND.
(org-setup-yank-dnd-handlers): Register yank-media-handler and DND
handler.
(org-yank-image-save-type, org-yank-image-file-name-function)
(org-dnd-default-attach-method, org-dnd-method): New defcustoms.
(org--image-yank-media-handler, org--copied-files-yank-media-handler)
(org--dnd-attach-file, org--dnd-local-file-handler, org--dnd-xds-method)
(org--dnd-xds-function): Add yank-media and DND handlers.

* etc/ORG-NEWS: Advertise the new features.
---
 etc/ORG-NEWS |  20 +++++
 lisp/org.el  | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 243 insertions(+), 1 deletion(-)

diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 252c5a9f9..c4a58dd4d 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -596,6 +596,26 @@ return a matplotlib Figure object to plot. For output results, the
 current figure (as returned by =pyplot.gcf()=) is cleared before
 evaluation, and then plotted afterwards.
 
+*** Images and files in clipboard can be attached
+
+Org can now attach images in clipboard and files copied/cut to the
+clipboard from file managers using the ~yank-media~ command which also
+inserts a link to the attached file.  This command was added in Emacs 29.
+
+Images can be saved to a separate directory instead of being attached,
+customize ~org-yank-image-save-type~.
+
+Image filename chosen can be customized by setting
+~org-yank-image-file-name-function~ which by default autogenerates a
+filename based on the current time.
+
+*** Files and images can be attached by dropping onto Emacs
+
+Attachment method other than ~org-attach-method~ for dropped files can
+be specified using ~org-dnd-default-attach-method~.
+
+Images dropped also respect the value of ~org-yank-image-save-type~.
+
 ** New functions and changes in function arguments
 *** =TYPES= argument in ~org-element-lineage~ can now be a symbol
 
diff --git a/lisp/org.el b/lisp/org.el
index d0b2355ea..a7ec90f08 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -4999,7 +4999,10 @@ The following commands are available:
   (org--set-faces-extend '(org-block-begin-line org-block-end-line)
                          org-fontify-whole-block-delimiter-line)
   (org--set-faces-extend org-level-faces org-fontify-whole-heading-line)
-  (setq-local org-mode-loading nil))
+  (setq-local org-mode-loading nil)
+
+  ;; `yank-media' handler and DND support.
+  (org-setup-yank-dnd-handlers))
 
 ;; Update `customize-package-emacs-version-alist'
 (add-to-list 'customize-package-emacs-version-alist
@@ -20254,6 +20257,225 @@ it has a `diary' type."
 		    (org-format-timestamp timestamp fmt t))
 	  (org-format-timestamp timestamp fmt (eq boundary 'end)))))))
 
+;;; Yank media handler and DND
+(defun org-setup-yank-dnd-handlers ()
+  "Setup the `yank-media' and DND handlers for buffer."
+  (setq-local dnd-protocol-alist
+              (cons '("^file:///" . org--dnd-local-file-handler)
+                    dnd-protocol-alist))
+  (when (fboundp 'yank-media-handler)
+    (yank-media-handler "image/.*" #'org--image-yank-media-handler)
+    ;; Looks like different DEs go for different handler names,
+    ;; https://larsee.com/blog/2019/05/clipboard-files/.
+    (yank-media-handler "x/special-\\(?:gnome\|KDE\|mate\\)-files"
+                        #'org--copied-files-yank-media-handler))
+  (when (boundp 'x-dnd-direct-save-function)
+    (setq-local x-dnd-direct-save-function #'org--dnd-xds-function)))
+
+(defcustom org-yank-image-save-type 'attach
+  "Method to save images yanked from clipboard and dropped to Emacs.
+It can be the symbol `attach' to add it as an attachment, or a
+directory name to copy/cut the image to that directory."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Add it as attachment" attach)
+                 (directory :tag "Save it in directory"))
+  :safe (lambda (x) (eq x 'attach)))
+
+(defcustom org-yank-image-file-name-function #'org-yank-image-autogen-filename
+  "Function to generate filename for image yanked from clipboard.
+By default, this autogenerates a filename based on the current
+time.
+It is called with no arguments and should return a string without
+any extension which is used as the filename."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(radio (function-item :doc "Autogenerate filename"
+                               org-yank-image-autogen-filename)
+                (function-item :doc "Ask for filename"
+                               org-yank-image-read-filename)
+                function))
+
+(defun org-yank-image-autogen-filename ()
+  "Autogenerate filename for image in clipboard."
+  (format-time-string "clipboard-%Y%m%dT%H%M%S.%6N"))
+
+(defun org-yank-image-read-filename ()
+  "Read filename for image in clipboard."
+  (read-string "Basename for image file without extension: "))
+
+(declare-function org-attach-attach "org-attach" (file &optional visit-dir method))
+
+(defun org--image-yank-media-handler (mimetype data)
+  "Save image DATA of mime-type MIMETYPE and insert link at point.
+It is saved as per `org-yank-image-save-type'.  The name for the
+image is prompted and the extension is automatically added to the
+end."
+  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
+         (iname (funcall org-yank-image-file-name-function))
+         (filename (file-name-with-extension iname ext))
+         (absname (expand-file-name
+                   filename
+                   (if (eq org-yank-image-save-type 'attach)
+                       temporary-file-directory
+                     org-yank-image-save-type)))
+         link)
+    (when (and (not (eq org-yank-image-save-type 'attach))
+               (not (file-directory-p org-yank-image-save-type)))
+      (make-directory org-yank-image-save-type t))
+    (with-temp-file absname
+      (insert data))
+    (if (null (eq org-yank-image-save-type 'attach))
+        (setq link (org-link-make-string (concat "file:" (file-relative-name absname))))
+      (require 'org-attach)
+      (org-attach-attach absname nil 'mv)
+      (setq link (org-link-make-string (concat "attachment:" filename))))
+    (insert link)))
+
+;; I cannot find a spec for this but
+;; https://indigo.re/posts/2021-12-21-clipboard-data.html and pcmanfm
+;; suggests that this is the format.
+(defun org--copied-files-yank-media-handler (_mimetype data)
+  "Attach copied or cut files from file manager.
+If the files were cut from the file manager, then the `mv' attach
+method is used; `cp' otherwise.
+
+DATA is a string where the first line is the operation to
+perform: copy or cut.  Rest of the lines are file: links to the
+concerned files."
+  (require 'org-attach)
+  ;; pcmanfm adds a null byte at the end for some reason.
+  (let* ((data (split-string data "[\0\n\r]" t "^file://"))
+         (files (cdr data))
+         (method (if (equal (car data) "cut")
+                     'mv
+                   'cp)))
+    (dolist (f files)
+      (setq f (url-unhex-string f))
+      (if (file-readable-p f)
+          (org-attach-attach f nil method)
+        (message "File `%s' is not readable, skipping" f)))))
+
+(defcustom org-dnd-method 'ask
+  "Action to perform on the dropped file.
+When the value is the symbol,
+  . `attach' -- attach the dropped file
+  . `open' -- visit/open the dropped file in Emacs
+  . `file-link' -- insert file: link to the dropped file
+  . `ask' -- ask what to do out of the above."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Attach" attach)
+                 (const :tag "Open/Visit file" open)
+                 (const :tag "Insert file: link" file-link)
+                 (const :tag "Ask what to do" ask)))
+
+(defcustom org-dnd-default-attach-method nil
+  "Default attach method to use when DND action is unspecified.
+This attach method is used when the DND action is `private'.
+This is also used when `org-yank-image-save-type' is nil.
+When nil, use `org-attach-method'."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Default attach method" nil)
+                 (const :tag "Copy" cp)
+                 (const :tag "Move" mv)
+                 (const :tag "Hard link" ln)
+                 (const :tag "Symbolic link" lns)))
+
+(declare-function mailcap-file-name-to-mime-type "mailcap" (file-name))
+(defvar org-attach-method)
+
+(defun org--dnd-local-file-handler (url action)
+  (let ((method (if (eq org-dnd-method 'ask)
+                    (caddr (read-multiple-choice
+                            "What to do with dropped file?"
+                            '((?a "attach" attach)
+                              (?o "open" open)
+                              (?f "insert file: link" file-link))))
+                  org-dnd-method)))
+    (pcase method
+      (`attach (org--dnd-attach-file url action))
+      (`open (dnd-open-local-file url action))
+      (`file-link
+       (let ((filename (dnd-get-local-file-name url)))
+         (insert (org-link-make-string (concat "file:" filename))))))))
+
+(defun org--dnd-attach-file (url action)
+  "Attach filename given by URL using method pertaining to ACTION.
+If ACTION is `move', use `mv' attach method.
+If `copy', use `cp' attach method.
+If `ask', ask the user.
+If `private', use the method denoted in `org-dnd-default-attach-action'.
+The action `private' is always returned."
+  (require 'mailcap)
+  (let* ((filename (dnd-get-local-file-name url))
+         (mimetype (mailcap-file-name-to-mime-type filename))
+         (separatep (and (string-prefix-p "image/" mimetype)
+                         (not (eq 'attach org-yank-image-save-type))))
+         (method (pcase action
+                   ('copy 'cp)
+                   ('move 'mv)
+                   ('ask (caddr (read-multiple-choice
+                                 "Attach using method"
+                                 '((?c "copy" cp)
+                                   (?m "move" mv)
+                                   (?l "hard link" ln)
+                                   (?s "symbolic link" lns)))))
+                   ('private (or org-dnd-default-attach-method
+                                 org-attach-method)))))
+    (if separatep
+        (funcall
+         (pcase method
+           ('cp #'copy-file)
+           ('mv #'rename-file)
+           ('ln #'add-name-to-file)
+           ('lns #'make-symbolic-link))
+         filename
+         (expand-file-name (file-name-nondirectory filename)
+                           org-yank-image-save-type))
+      (org-attach-attach filename nil method))
+    (insert
+     (org-link-make-string
+      (concat (if separatep
+                  "file:"
+                "attachment:")
+              (if separatep
+                  (expand-file-name (file-name-nondirectory filename)
+                                    org-yank-image-save-type)
+                (file-name-nondirectory filename))))
+     "\n")
+    'private))
+
+(defvar-local org--dnd-xds-method nil
+  "The method to use for dropped file.")
+(defun org--dnd-xds-function (need-name filename)
+  "Handle file with FILENAME dropped via XDS protocol.
+When NEED-NAME is t, FILNAME is the base name of the file to be
+saved.
+When NEED-NAME is nil, the drop is complete."
+  (if need-name
+      (let ((method (if (eq org-dnd-method 'ask)
+                        (caddr (read-multiple-choice
+                                "What to do with dropped file?"
+                                '((?a "attach" attach)
+                                  (?o "open" open)
+                                  (?f "insert file: link" file-link))))
+                      org-dnd-method)))
+        (setq-local org--dnd-xds-method method)
+        (pcase method
+          (`attach (expand-file-name filename (org-attach-dir 'create)))
+          (`open (expand-file-name (make-temp-name "emacs.") temporary-file-directory))
+          (`file-link (read-file-name "Write file to: " nil nil nil filename))))
+    (pcase org--dnd-xds-method
+      (`attach (insert (org-link-make-string
+                        (concat "attachment:" (file-name-nondirectory filename)))
+                       "\n"))
+      (`file-link (insert (org-link-make-string (concat "file:" filename))
+                          "\n"))
+      (`open (find-file filename)))
+    (setq-local org--dnd-xds-method nil)))
+
 ;;; Other stuff
 
 (defvar reftex-docstruct-symbol)
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-08 11:21                 ` Visuwesh
@ 2023-10-09 11:12                   ` Ihor Radchenko
  2023-10-09 12:17                     ` Visuwesh
  2023-10-22  6:19                     ` Visuwesh
  0 siblings, 2 replies; 49+ messages in thread
From: Ihor Radchenko @ 2023-10-09 11:12 UTC (permalink / raw)
  To: Visuwesh; +Cc: emacs-orgmode

Thanks!

> +(defun org--dnd-attach-file (url action)
> ...
> +    (insert
> +     (org-link-make-string
> +      (concat (if separatep
> +                  "file:"
> +                "attachment:")
> +              (if separatep
> +                  (expand-file-name (file-name-nondirectory filename)
> +                                    org-yank-image-save-type)
> +                (file-name-nondirectory filename))))
> +     "\n")
> +    'private))

> +    (pcase org--dnd-xds-method
> +      (`attach (insert (org-link-make-string
> +                        (concat "attachment:" (file-name-nondirectory filename)))

> +      (`file-link (insert (org-link-make-string (concat "file:" filename))
> +                          "\n"))

Is there any particular reason why you insert a newline after the image
link?

Also, we might want to add a subsection describing the new customization to
17 Miscellaneous section of the manual. Otherwise, users might have
difficulties discovering relevant settings to customize dnd and yank
behavior.

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-09 11:12                   ` Ihor Radchenko
@ 2023-10-09 12:17                     ` Visuwesh
  2023-10-19  7:34                       ` Visuwesh
  2023-10-22  6:19                     ` Visuwesh
  1 sibling, 1 reply; 49+ messages in thread
From: Visuwesh @ 2023-10-09 12:17 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: emacs-orgmode

[திங்கள் அக்டோபர் 09, 2023] Ihor Radchenko wrote:

> Thanks!
>
>> +(defun org--dnd-attach-file (url action)
>> ...
>> +    (insert
>> +     (org-link-make-string
>> +      (concat (if separatep
>> +                  "file:"
>> +                "attachment:")
>> +              (if separatep
>> +                  (expand-file-name (file-name-nondirectory filename)
>> +                                    org-yank-image-save-type)
>> +                (file-name-nondirectory filename))))
>> +     "\n")
>> +    'private))
>
>> +    (pcase org--dnd-xds-method
>> +      (`attach (insert (org-link-make-string
>> +                        (concat "attachment:" (file-name-nondirectory filename)))
>
>> +      (`file-link (insert (org-link-make-string (concat "file:" filename))
>> +                          "\n"))
>
> Is there any particular reason why you insert a newline after the image
> link?

It is for when you drop multiple files onto a frame.  AFAIU/AFAICT
there's no way to know beforehand if multiple files will be dropped or
not.  I will look into it in bit more detail in the coming days and drop
the newline if only one file is dropped.

> Also, we might want to add a subsection describing the new customization to
> 17 Miscellaneous section of the manual. Otherwise, users might have
> difficulties discovering relevant settings to customize dnd and yank
> behavior.

OK, I will do so.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
@ 2023-10-11 14:24 Liu Hui
  2023-10-11 15:36 ` Visuwesh
  0 siblings, 1 reply; 49+ messages in thread
From: Liu Hui @ 2023-10-11 14:24 UTC (permalink / raw)
  To: Visuwesh; +Cc: emacs-orgmode, Ihor Radchenko

Hi,

Thanks for your work. I have two minor suggestions about the patch.

> + (`file-link
> + (let ((filename (dnd-get-local-file-name url)))
> + (insert (org-link-make-string (concat "file:" filename))))))))

I think it is better to abbreviate the file name for file-link, so it
would be consistent with org-insert-link.

> + (caddr (read-multiple-choice
> + "What to do with dropped file?"
> + '((?a "attach" attach)
> + (?o "open" open)
> + (?f "insert file: link" file-link))))

The dialog box is shown in the center of frame and I find it a little
inconvenient compared with a menu popped up just at the mouse
location. How about using x-popup-menu?


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-11 14:24 Liu Hui
@ 2023-10-11 15:36 ` Visuwesh
  2023-10-12  5:12   ` Liu Hui
  0 siblings, 1 reply; 49+ messages in thread
From: Visuwesh @ 2023-10-11 15:36 UTC (permalink / raw)
  To: Liu Hui; +Cc: emacs-orgmode, Ihor Radchenko

[புதன் அக்டோபர் 11, 2023] Liu Hui wrote:

> Hi,
>
> Thanks for your work. I have two minor suggestions about the patch.
>
>> + (`file-link
>> + (let ((filename (dnd-get-local-file-name url)))
>> + (insert (org-link-make-string (concat "file:" filename))))))))
>
> I think it is better to abbreviate the file name for file-link, so it
> would be consistent with org-insert-link.

OK, will do.

>> + (caddr (read-multiple-choice
>> + "What to do with dropped file?"
>> + '((?a "attach" attach)
>> + (?o "open" open)
>> + (?f "insert file: link" file-link))))
>
> The dialog box is shown in the center of frame and I find it a little
> inconvenient compared with a menu popped up just at the mouse
> location. How about using x-popup-menu?

But isn't that for menus?  I don't know how convenient is a menu
compared to a dialog box.  But if the consensus is that a menu should be
popped, then I will adjust the query to use rmc or menu as per
use-dialog-box.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-11 15:36 ` Visuwesh
@ 2023-10-12  5:12   ` Liu Hui
  0 siblings, 0 replies; 49+ messages in thread
From: Liu Hui @ 2023-10-12  5:12 UTC (permalink / raw)
  To: Visuwesh; +Cc: emacs-orgmode, Ihor Radchenko

[-- Attachment #1: Type: text/plain, Size: 1385 bytes --]

Visuwesh <visuweshm@gmail.com> 于2023年10月11日周三 23:36写道:

> >> + (caddr (read-multiple-choice
> >> + "What to do with dropped file?"
> >> + '((?a "attach" attach)
> >> + (?o "open" open)
> >> + (?f "insert file: link" file-link))))
> >
> > The dialog box is shown in the center of frame and I find it a little
> > inconvenient compared with a menu popped up just at the mouse
> > location. How about using x-popup-menu?
>
> But isn't that for menus?  I don't know how convenient is a menu
> compared to a dialog box.  But if the consensus is that a menu should be
> popped, then I will adjust the query to use rmc or menu as per
> use-dialog-box.

Thanks for consideration. I don't mean menu keymap but a pop-up menu
that simply returns a candidate, like

(x-popup-menu t
              (list "What to do with dropped file?"
                (cons ""
                      '(("Attach" . attach)
                    ("Open" . open)
                            ("Insert file: link" . file-link)))))

The menu is popped up at the location where the file is dropped, so
users only need to move the mouse slightly to select the desired
action, rather than always moving the mouse to the center of frame.

An example in dired mode is attached, and I feel it is a little more
convenient and efficient from the perspective of user interaction ;)

[-- Attachment #2: dired.gif --]
[-- Type: image/gif, Size: 96822 bytes --]

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-09 12:17                     ` Visuwesh
@ 2023-10-19  7:34                       ` Visuwesh
  2023-10-19  9:44                         ` Ihor Radchenko
  0 siblings, 1 reply; 49+ messages in thread
From: Visuwesh @ 2023-10-19  7:34 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: emacs-orgmode

[திங்கள் அக்டோபர் 09, 2023] Visuwesh wrote:

> [திங்கள் அக்டோபர் 09, 2023] Ihor Radchenko wrote:
>
>> Thanks!
>>
>>> +(defun org--dnd-attach-file (url action)
>>> ...
>>> +    (insert
>>> +     (org-link-make-string
>>> +      (concat (if separatep
>>> +                  "file:"
>>> +                "attachment:")
>>> +              (if separatep
>>> +                  (expand-file-name (file-name-nondirectory filename)
>>> +                                    org-yank-image-save-type)
>>> +                (file-name-nondirectory filename))))
>>> +     "\n")
>>> +    'private))
>>
>>> +    (pcase org--dnd-xds-method
>>> +      (`attach (insert (org-link-make-string
>>> +                        (concat "attachment:" (file-name-nondirectory filename)))
>>
>>> +      (`file-link (insert (org-link-make-string (concat "file:" filename))
>>> +                          "\n"))
>>
>> Is there any particular reason why you insert a newline after the image
>> link?
>
> It is for when you drop multiple files onto a frame.  AFAIU/AFAICT
> there's no way to know beforehand if multiple files will be dropped or
> not.  I will look into it in bit more detail in the coming days and drop
> the newline if only one file is dropped.

OK, seems like the actual handling is done by x-dnd-handle-uri-list
where the URI list is split and each URI is handled separately.  We can
override this function by our own by changing the entry in
x-dnd-types-alist but we need to do the same for pgtk-dnd-types-alist
and from what I can tell there are no such facilities for MS Windows and
NS?  I think we need to accept that we will have sub-optimal interface
for multi file drops.  WDYT?

Also, can I know what you think about using a popup-menu instead of a
dialog box for prompting as Liu Hui suggests in
https://yhetil.org/orgmode/CAOQTW-MAS6B2Uh9Z52eJN7eBwni+suhq9dWe0FkY_RzVHoxW-A@mail.gmail.com
?  If you're okay with it, I will write a function to use rmc or
popup-menu for prompting as per what use-dialog-box-p returns.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-19  7:34                       ` Visuwesh
@ 2023-10-19  9:44                         ` Ihor Radchenko
  2023-10-20  1:52                           ` Po Lu
  0 siblings, 1 reply; 49+ messages in thread
From: Ihor Radchenko @ 2023-10-19  9:44 UTC (permalink / raw)
  To: Visuwesh; +Cc: emacs-orgmode

Visuwesh <visuweshm@gmail.com> writes:

>>> Is there any particular reason why you insert a newline after the image
>>> link?
>>
>> It is for when you drop multiple files onto a frame.  AFAIU/AFAICT
>> there's no way to know beforehand if multiple files will be dropped or
>> not.  I will look into it in bit more detail in the coming days and drop
>> the newline if only one file is dropped.
>
> OK, seems like the actual handling is done by x-dnd-handle-uri-list
> where the URI list is split and each URI is handled separately.  We can
> override this function by our own by changing the entry in
> x-dnd-types-alist but we need to do the same for pgtk-dnd-types-alist
> and from what I can tell there are no such facilities for MS Windows and
> NS?  I think we need to accept that we will have sub-optimal interface
> for multi file drops.  WDYT?

Ideally, we should ask Emacs devs to provide OS-independent interface
for uri-list handling. I am not a big fan of adding special OS handling
to Org mode, unless absolutely necessary.

And the discussed _minor_ problem is not so big deal that we should
complicate the code and, more importantly, maintenance so much.

> Also, can I know what you think about using a popup-menu instead of a
> dialog box for prompting as Liu Hui suggests in
> https://yhetil.org/orgmode/CAOQTW-MAS6B2Uh9Z52eJN7eBwni+suhq9dWe0FkY_RzVHoxW-A@mail.gmail.com
> ?  If you're okay with it, I will write a function to use rmc or
> popup-menu for prompting as per what use-dialog-box-p returns.

I have no objections.

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-19  9:44                         ` Ihor Radchenko
@ 2023-10-20  1:52                           ` Po Lu
  2023-10-20  7:29                             ` Ihor Radchenko
  0 siblings, 1 reply; 49+ messages in thread
From: Po Lu @ 2023-10-20  1:52 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: Visuwesh, emacs-orgmode

Ihor Radchenko <yantar92@posteo.net> writes:

> Ideally, we should ask Emacs devs to provide OS-independent interface
> for uri-list handling. I am not a big fan of adding special OS handling
> to Org mode, unless absolutely necessary.

Inasmuch as I had previously interposed myself into this conversation,
it should have been clear that its subject is of interest to me.  So
herein lies the question: why was I removed from its carbon copy list,
from nothing less than a follow-up to my reply itself?  It is common
courtesy for some advance notice to be given to a participant who is
about to be subject to such treatment; and was I not, I would have be
made aware of your plight earlier -- but I digress.

How about enabling programs to receive drops of multiple files instead?
Since not all window systems supply drops of multiple files in the form
of text/uri-lists, I account that a more robust and versatile interface.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-20  1:52                           ` Po Lu
@ 2023-10-20  7:29                             ` Ihor Radchenko
  2023-10-20  7:46                               ` Po Lu
  0 siblings, 1 reply; 49+ messages in thread
From: Ihor Radchenko @ 2023-10-20  7:29 UTC (permalink / raw)
  To: Po Lu; +Cc: Visuwesh, emacs-orgmode

Po Lu <luangruo@yahoo.com> writes:

> Ihor Radchenko <yantar92@posteo.net> writes:
>
>> Ideally, we should ask Emacs devs to provide OS-independent interface
>> for uri-list handling. I am not a big fan of adding special OS handling
>> to Org mode, unless absolutely necessary.
>
> Inasmuch as I had previously interposed myself into this conversation,
> it should have been clear that its subject is of interest to me.  So
> herein lies the question: why was I removed from its carbon copy list,
> from nothing less than a follow-up to my reply itself?  It is common
> courtesy for some advance notice to be given to a participant who is
> about to be subject to such treatment; and was I not, I would have be
> made aware of your plight earlier -- but I digress.

Because nobody added you manually to the CC in this branch of the
discussion. You would only be automatically in the CC in the replies to
your message (87bkdccihf.fsf@yahoo.com), but not the earlier branches.

> How about enabling programs to receive drops of multiple files instead?
> Since not all window systems supply drops of multiple files in the form
> of text/uri-lists, I account that a more robust and versatile interface.

Do you mean something like a standardized entry in `dnd-protocol-alist'
that is independent on OS? Instead of (REGEXP . FUNCTION),
(SYMBOL . FUNCTION) with SYMBOL = 'file-list or so.

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-20  7:29                             ` Ihor Radchenko
@ 2023-10-20  7:46                               ` Po Lu
  2023-10-20  7:57                                 ` Ihor Radchenko
  0 siblings, 1 reply; 49+ messages in thread
From: Po Lu @ 2023-10-20  7:46 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: Visuwesh, emacs-orgmode

Ihor Radchenko <yantar92@posteo.net> writes:

> Because nobody added you manually to the CC in this branch of the
> discussion. You would only be automatically in the CC in the replies to
> your message (87bkdccihf.fsf@yahoo.com), but not the earlier branches.

The follow-up I mentioned lists 87bkdccihf.fsf@yahoo.com within
In-Reply-To.

> Do you mean something like a standardized entry in `dnd-protocol-alist'
> that is independent on OS? Instead of (REGEXP . FUNCTION),
> (SYMBOL . FUNCTION) with SYMBOL = 'file-list or so.

I'm sorry, but I don't understand what you're proposing.  Would you
please couch it differently?


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-20  7:46                               ` Po Lu
@ 2023-10-20  7:57                                 ` Ihor Radchenko
  2023-10-20  8:29                                   ` Po Lu
  2023-10-20 10:17                                   ` Visuwesh
  0 siblings, 2 replies; 49+ messages in thread
From: Ihor Radchenko @ 2023-10-20  7:57 UTC (permalink / raw)
  To: Po Lu; +Cc: Visuwesh, emacs-orgmode

Po Lu <luangruo@yahoo.com> writes:

>> Because nobody added you manually to the CC in this branch of the
>> discussion. You would only be automatically in the CC in the replies to
>> your message (87bkdccihf.fsf@yahoo.com), but not the earlier branches.
>
> The follow-up I mentioned lists 87bkdccihf.fsf@yahoo.com within
> In-Reply-To.

I see. I use wide reply by default, personally. I did not drop you or
anyone from CC in this thread on purpose.

>> Do you mean something like a standardized entry in `dnd-protocol-alist'
>> that is independent on OS? Instead of (REGEXP . FUNCTION),
>> (SYMBOL . FUNCTION) with SYMBOL = 'file-list or so.
>
> I'm sorry, but I don't understand what you're proposing.  Would you
> please couch it differently?

Instead of passing dnd data as is from the OS, Emacs can convert it into
a standardized format, independent of the OS. Then,
`dnd-protocol-alist' can allow handlers for such standardized dnd type.

In our scenario, the dnd data will be dropped file list. Emacs should
internally detect such file lists for GNU/Linux / Windows / other
platforms with dnd support and then convert them into the same format.
Then, dnd users can add (file-list . FUNCTION) into `dnd-protocol-alist'
and the FUNCTION will be passed with the converted data that will always
be the same for all the OSes.

Does it make sense?

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-20  7:57                                 ` Ihor Radchenko
@ 2023-10-20  8:29                                   ` Po Lu
  2023-10-20 10:17                                   ` Visuwesh
  1 sibling, 0 replies; 49+ messages in thread
From: Po Lu @ 2023-10-20  8:29 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: Visuwesh, emacs-orgmode

Ihor Radchenko <yantar92@posteo.net> writes:

> Instead of passing dnd data as is from the OS, Emacs can convert it into
> a standardized format, independent of the OS. Then,
> `dnd-protocol-alist' can allow handlers for such standardized dnd type.
>
> In our scenario, the dnd data will be dropped file list. Emacs should
> internally detect such file lists for GNU/Linux / Windows / other
> platforms with dnd support and then convert them into the same format.
> Then, dnd users can add (file-list . FUNCTION) into `dnd-protocol-alist'
> and the FUNCTION will be passed with the converted data that will always
> be the same for all the OSes.
>
> Does it make sense?

Yes, that is scrutable.  I will write this.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-20  7:57                                 ` Ihor Radchenko
  2023-10-20  8:29                                   ` Po Lu
@ 2023-10-20 10:17                                   ` Visuwesh
  1 sibling, 0 replies; 49+ messages in thread
From: Visuwesh @ 2023-10-20 10:17 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: Po Lu, emacs-orgmode

[வெள்ளி அக்டோபர் 20, 2023] Ihor Radchenko wrote:

> Po Lu <luangruo@yahoo.com> writes:
>
>>> Because nobody added you manually to the CC in this branch of the
>>> discussion. You would only be automatically in the CC in the replies to
>>> your message (87bkdccihf.fsf@yahoo.com), but not the earlier branches.
>>
>> The follow-up I mentioned lists 87bkdccihf.fsf@yahoo.com within
>> In-Reply-To.
>
> I see. I use wide reply by default, personally. I did not drop you or
> anyone from CC in this thread on purpose.

I also use wide-reply but the problem was that I neglected to include Po
Lu in the CCs when I replied to a message in another sub-thread that
referred to Po Lu's message.  So the issue raised by Po Lu was addressed
but he was effectively dropped from the discussion due to my negligence.

>>> Do you mean something like a standardized entry in `dnd-protocol-alist'
>>> that is independent on OS? Instead of (REGEXP . FUNCTION),
>>> (SYMBOL . FUNCTION) with SYMBOL = 'file-list or so.
>>
>> I'm sorry, but I don't understand what you're proposing.  Would you
>> please couch it differently?
>
> Instead of passing dnd data as is from the OS, Emacs can convert it into
> a standardized format, independent of the OS. Then,
> `dnd-protocol-alist' can allow handlers for such standardized dnd type.
>
> In our scenario, the dnd data will be dropped file list. Emacs should
> internally detect such file lists for GNU/Linux / Windows / other
> platforms with dnd support and then convert them into the same format.
> Then, dnd users can add (file-list . FUNCTION) into `dnd-protocol-alist'
> and the FUNCTION will be passed with the converted data that will always
> be the same for all the OSes.
>
> Does it make sense?

BTW, I would also like to have something like dnd-protocol-alist but for
dropped _text_ instead.  Currently, every dropped text is handled by
dnd-insert-text but I would like to have a handler so that e.g., I can
add the relevant bibtex entry for the DOI _text_ (10.BLAH/BLAH no fancy
link) onto an Emacs window visiting a bibtex file.  I currently do this
via an advice but it would be nice to have something less hacky.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-09 11:12                   ` Ihor Radchenko
  2023-10-09 12:17                     ` Visuwesh
@ 2023-10-22  6:19                     ` Visuwesh
  2023-10-23  8:58                       ` Ihor Radchenko
  1 sibling, 1 reply; 49+ messages in thread
From: Visuwesh @ 2023-10-22  6:19 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: emacs-orgmode, Po Lu

[-- Attachment #1: Type: text/plain, Size: 1486 bytes --]

[திங்கள் அக்டோபர் 09, 2023] Ihor Radchenko wrote:

> Thanks!
>
>> +(defun org--dnd-attach-file (url action)
>> ...
>> +    (insert
>> +     (org-link-make-string
>> +      (concat (if separatep
>> +                  "file:"
>> +                "attachment:")
>> +              (if separatep
>> +                  (expand-file-name (file-name-nondirectory filename)
>> +                                    org-yank-image-save-type)
>> +                (file-name-nondirectory filename))))
>> +     "\n")
>> +    'private))
>
>> +    (pcase org--dnd-xds-method
>> +      (`attach (insert (org-link-make-string
>> +                        (concat "attachment:" (file-name-nondirectory filename)))
>
>> +      (`file-link (insert (org-link-make-string (concat "file:" filename))
>> +                          "\n"))
>
> Is there any particular reason why you insert a newline after the image
> link?

I think this can stay here until Po Lu writes the platform-agnostic code
that you requested further down this thread?

> Also, we might want to add a subsection describing the new customization to
> 17 Miscellaneous section of the manual. Otherwise, users might have
> difficulties discovering relevant settings to customize dnd and yank
> behavior.

I have now added these to the manual with a few relevant concept
indices.  Please check if they are understandable as I am terrible at
writing documentation.  Thanks.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-support-for-yank-media-and-DND.patch --]
[-- Type: text/x-diff, Size: 16271 bytes --]

From e8b142db0d91e305cbefbb2c945fee7c40ed03f3 Mon Sep 17 00:00:00 2001
From: Visuwesh <visuweshm@gmail.com>
Date: Fri, 22 Sep 2023 20:11:41 +0530
Subject: [PATCH] Add support for yank-media and DND

* lisp/org.el (org-mode): Call the setup function for yank-media and
DND.
(org-setup-yank-dnd-handlers): Register yank-media-handler and DND
handler.
(org-yank-image-save-type, org-yank-image-file-name-function)
(org-dnd-default-attach-method, org-dnd-method): New defcustoms.
(org--image-yank-media-handler, org--copied-files-yank-media-handler)
(org--dnd-attach-file, org--dnd-local-file-handler, org--dnd-xds-method)
(org--dnd-xds-function, org--dnd-rmc): Add yank-media and DND
handlers.

* doc/org-manual.org: (Drag and Drop & ~yank-media~): Describe the new
feature in the manual.

* etc/ORG-NEWS: Advertise the new features.
---
 doc/org-manual.org |  40 ++++++++
 etc/ORG-NEWS       |  20 ++++
 lisp/org.el        | 243 ++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 298 insertions(+), 5 deletions(-)

diff --git a/doc/org-manual.org b/doc/org-manual.org
index 3e9d42f55..dcdaa98fa 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -21095,6 +21095,46 @@ most recent since the mobile application searches files that were last
 pulled.  To get an updated agenda view with changes since the last
 pull, pull again.
 
+** Drag and Drop & ~yank-media~
+:PROPERTIES:
+:DESCRIPTION: Dropping and pasting files and images
+:END:
+
+#+cindex: dropping files
+#+vindex: org-dnd-method
+Org can attach, insert =file:= links, or open files dropped onto an
+Emacs frame.  By default, Org asks the user what must be done with the
+dropped file but this can be changed by customizing the user option
+~org-dnd-method~.  Changing the variable can make Org do any of the
+above mentioned actions without prompting.
+
+#+vindex: org-dnd-default-attach-method
+If Org cannot figure out which attachment method to use automatically,
+it defaults to using the attachment method mentioned in
+~org-dnd-default-attach-method~.  Org uses the attachment method
+mentioned in ~org-attach-method~ by default, so Org using the =cp=
+method to attach the dropped files.
+
+#+cindex: pasting files, images from clipboard
+From Emacs 29, Org can deal with images copied to the clipboard, and
+files copied from a file manager when pasted using the command
+~yank-media~.  Org deals with them differently.
+
+#+vindex: org-yank-image-save-type
+#+vindex: org-yank-image-file-name-function
+For images, Org attaches the image when pasting but this can be
+changed by customizing ~org-yank-image-save-type~ to save them under
+another directory instead.  The name given to these images are
+autogenerated by default but you can customize what name should be
+given to the pasted image by customizing
+~org-yank-image-file-name-function~.  This function takes no argument
+and should return the filename without extension to use as the image's
+filename.
+
+For files copied from a file manager, Org attaches them to the current
+heading using the =mv= or the =cp= method if the files were cut or
+copied in the file manager respectively.
+
 * Hacking
 :PROPERTIES:
 :DESCRIPTION: How to hack your way around.
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 252c5a9f9..c4a58dd4d 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -596,6 +596,26 @@ return a matplotlib Figure object to plot. For output results, the
 current figure (as returned by =pyplot.gcf()=) is cleared before
 evaluation, and then plotted afterwards.
 
+*** Images and files in clipboard can be attached
+
+Org can now attach images in clipboard and files copied/cut to the
+clipboard from file managers using the ~yank-media~ command which also
+inserts a link to the attached file.  This command was added in Emacs 29.
+
+Images can be saved to a separate directory instead of being attached,
+customize ~org-yank-image-save-type~.
+
+Image filename chosen can be customized by setting
+~org-yank-image-file-name-function~ which by default autogenerates a
+filename based on the current time.
+
+*** Files and images can be attached by dropping onto Emacs
+
+Attachment method other than ~org-attach-method~ for dropped files can
+be specified using ~org-dnd-default-attach-method~.
+
+Images dropped also respect the value of ~org-yank-image-save-type~.
+
 ** New functions and changes in function arguments
 *** =TYPES= argument in ~org-element-lineage~ can now be a symbol
 
diff --git a/lisp/org.el b/lisp/org.el
index d0b2355ea..855897e7d 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -4999,7 +4999,10 @@ The following commands are available:
   (org--set-faces-extend '(org-block-begin-line org-block-end-line)
                          org-fontify-whole-block-delimiter-line)
   (org--set-faces-extend org-level-faces org-fontify-whole-heading-line)
-  (setq-local org-mode-loading nil))
+  (setq-local org-mode-loading nil)
+
+  ;; `yank-media' handler and DND support.
+  (org-setup-yank-dnd-handlers))
 
 ;; Update `customize-package-emacs-version-alist'
 (add-to-list 'customize-package-emacs-version-alist
@@ -15125,20 +15128,20 @@ INCREMENT-STEP divisor."
 	(setq hour (mod hour 24))
 	(setq pos-match-group 1
               new (format "-%02d:%02d" hour minute)))
-       
+
        ((org-pos-in-match-range pos 6) ;; POS on "dmwy" repeater char.
 	(setq pos-match-group 6
               new (car (rassoc (+ nincrements (cdr (assoc (match-string 6 ts-string) idx))) idx))))
-       
+
        ((org-pos-in-match-range pos 5) ;; POS on X in "Xd" repeater.
 	(setq pos-match-group 5
               ;; Never drop below X=1.
               new (format "%d" (max 1 (+ nincrements (string-to-number (match-string 5 ts-string)))))))
-       
+
        ((org-pos-in-match-range pos 9) ;; POS on "dmwy" repeater in warning interval.
 	(setq pos-match-group 9
               new (car (rassoc (+ nincrements (cdr (assoc (match-string 9 ts-string) idx))) idx))))
-       
+
        ((org-pos-in-match-range pos 8) ;; POS on X in "Xd" in warning interval.
 	(setq pos-match-group 8
               ;; Never drop below X=0.
@@ -20254,6 +20257,236 @@ it has a `diary' type."
 		    (org-format-timestamp timestamp fmt t))
 	  (org-format-timestamp timestamp fmt (eq boundary 'end)))))))
 
+;;; Yank media handler and DND
+(defun org-setup-yank-dnd-handlers ()
+  "Setup the `yank-media' and DND handlers for buffer."
+  (setq-local dnd-protocol-alist
+              (cons '("^file:///" . org--dnd-local-file-handler)
+                    dnd-protocol-alist))
+  (when (fboundp 'yank-media-handler)
+    (yank-media-handler "image/.*" #'org--image-yank-media-handler)
+    ;; Looks like different DEs go for different handler names,
+    ;; https://larsee.com/blog/2019/05/clipboard-files/.
+    (yank-media-handler "x/special-\\(?:gnome\|KDE\|mate\\)-files"
+                        #'org--copied-files-yank-media-handler))
+  (when (boundp 'x-dnd-direct-save-function)
+    (setq-local x-dnd-direct-save-function #'org--dnd-xds-function)))
+
+(defcustom org-yank-image-save-type 'attach
+  "Method to save images yanked from clipboard and dropped to Emacs.
+It can be the symbol `attach' to add it as an attachment, or a
+directory name to copy/cut the image to that directory."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Add it as attachment" attach)
+                 (directory :tag "Save it in directory"))
+  :safe (lambda (x) (eq x 'attach)))
+
+(defcustom org-yank-image-file-name-function #'org-yank-image-autogen-filename
+  "Function to generate filename for image yanked from clipboard.
+By default, this autogenerates a filename based on the current
+time.
+It is called with no arguments and should return a string without
+any extension which is used as the filename."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(radio (function-item :doc "Autogenerate filename"
+                               org-yank-image-autogen-filename)
+                (function-item :doc "Ask for filename"
+                               org-yank-image-read-filename)
+                function))
+
+(defun org-yank-image-autogen-filename ()
+  "Autogenerate filename for image in clipboard."
+  (format-time-string "clipboard-%Y%m%dT%H%M%S.%6N"))
+
+(defun org-yank-image-read-filename ()
+  "Read filename for image in clipboard."
+  (read-string "Basename for image file without extension: "))
+
+(declare-function org-attach-attach "org-attach" (file &optional visit-dir method))
+
+(defun org--image-yank-media-handler (mimetype data)
+  "Save image DATA of mime-type MIMETYPE and insert link at point.
+It is saved as per `org-yank-image-save-type'.  The name for the
+image is prompted and the extension is automatically added to the
+end."
+  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
+         (iname (funcall org-yank-image-file-name-function))
+         (filename (file-name-with-extension iname ext))
+         (absname (expand-file-name
+                   filename
+                   (if (eq org-yank-image-save-type 'attach)
+                       temporary-file-directory
+                     org-yank-image-save-type)))
+         link)
+    (when (and (not (eq org-yank-image-save-type 'attach))
+               (not (file-directory-p org-yank-image-save-type)))
+      (make-directory org-yank-image-save-type t))
+    (with-temp-file absname
+      (insert data))
+    (if (null (eq org-yank-image-save-type 'attach))
+        (setq link (org-link-make-string (concat "file:" (file-relative-name absname))))
+      (require 'org-attach)
+      (org-attach-attach absname nil 'mv)
+      (setq link (org-link-make-string (concat "attachment:" filename))))
+    (insert link)))
+
+;; I cannot find a spec for this but
+;; https://indigo.re/posts/2021-12-21-clipboard-data.html and pcmanfm
+;; suggests that this is the format.
+(defun org--copied-files-yank-media-handler (_mimetype data)
+  "Attach copied or cut files from file manager.
+If the files were cut from the file manager, then the `mv' attach
+method is used; `cp' otherwise.
+
+DATA is a string where the first line is the operation to
+perform: copy or cut.  Rest of the lines are file: links to the
+concerned files."
+  (require 'org-attach)
+  ;; pcmanfm adds a null byte at the end for some reason.
+  (let* ((data (split-string data "[\0\n\r]" t "^file://"))
+         (files (cdr data))
+         (method (if (equal (car data) "cut")
+                     'mv
+                   'cp)))
+    (dolist (f files)
+      (setq f (url-unhex-string f))
+      (if (file-readable-p f)
+          (org-attach-attach f nil method)
+        (message "File `%s' is not readable, skipping" f)))))
+
+(defcustom org-dnd-method 'ask
+  "Action to perform on the dropped file.
+When the value is the symbol,
+  . `attach' -- attach the dropped file
+  . `open' -- visit/open the dropped file in Emacs
+  . `file-link' -- insert file: link to the dropped file
+  . `ask' -- ask what to do out of the above."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Attach" attach)
+                 (const :tag "Open/Visit file" open)
+                 (const :tag "Insert file: link" file-link)
+                 (const :tag "Ask what to do" ask)))
+
+(defcustom org-dnd-default-attach-method nil
+  "Default attach method to use when DND action is unspecified.
+This attach method is used when the DND action is `private'.
+This is also used when `org-yank-image-save-type' is nil.
+When nil, use `org-attach-method'."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Default attach method" nil)
+                 (const :tag "Copy" cp)
+                 (const :tag "Move" mv)
+                 (const :tag "Hard link" ln)
+                 (const :tag "Symbolic link" lns)))
+
+(declare-function mailcap-file-name-to-mime-type "mailcap" (file-name))
+(defvar org-attach-method)
+
+(defun org--dnd-rmc (prompt choices)
+  (if (null (use-dialog-box-p))
+      (caddr (read-multiple-choice prompt choices))
+    (setq choices
+          (mapcar
+           (pcase-lambda (`(_key ,message ,val))
+             (cons (capitalize message) val))
+           choices))
+    (x-popup-menu t (list prompt (cons "" choices)))))
+
+(defun org--dnd-local-file-handler (url action)
+  "Handle file URL as per ACTION."
+  (let ((method (if (eq org-dnd-method 'ask)
+                    (org--dnd-rmc
+                     "What to do with dropped file?"
+                     '((?a "attach" attach)
+                       (?o "open" open)
+                       (?f "insert file: link" file-link)))
+                  org-dnd-method)))
+    (pcase method
+      (`attach (org--dnd-attach-file url action))
+      (`open (dnd-open-local-file url action))
+      (`file-link
+       (let ((filename (dnd-get-local-file-name url)))
+         (insert (org-link-make-string (concat "file:" filename))))))))
+
+(defun org--dnd-attach-file (url action)
+  "Attach filename given by URL using method pertaining to ACTION.
+If ACTION is `move', use `mv' attach method.
+If `copy', use `cp' attach method.
+If `ask', ask the user.
+If `private', use the method denoted in `org-dnd-default-attach-action'.
+The action `private' is always returned."
+  (require 'mailcap)
+  (let* ((filename (dnd-get-local-file-name url))
+         (mimetype (mailcap-file-name-to-mime-type filename))
+         (separatep (and (string-prefix-p "image/" mimetype)
+                         (not (eq 'attach org-yank-image-save-type))))
+         (method (pcase action
+                   ('copy 'cp)
+                   ('move 'mv)
+                   ('ask (org--dnd-rmc
+                          "Attach using method"
+                          '((?c "copy" cp)
+                            (?m "move" mv)
+                            (?l "hard link" ln)
+                            (?s "symbolic link" lns))))
+                   ('private (or org-dnd-default-attach-method
+                                 org-attach-method)))))
+    (if separatep
+        (funcall
+         (pcase method
+           ('cp #'copy-file)
+           ('mv #'rename-file)
+           ('ln #'add-name-to-file)
+           ('lns #'make-symbolic-link))
+         filename
+         (expand-file-name (file-name-nondirectory filename)
+                           org-yank-image-save-type))
+      (org-attach-attach filename nil method))
+    (insert
+     (org-link-make-string
+      (concat (if separatep
+                  "file:"
+                "attachment:")
+              (if separatep
+                  (expand-file-name (file-name-nondirectory filename)
+                                    org-yank-image-save-type)
+                (file-name-nondirectory filename))))
+     "\n")
+    'private))
+
+(defvar-local org--dnd-xds-method nil
+  "The method to use for dropped file.")
+(defun org--dnd-xds-function (need-name filename)
+  "Handle file with FILENAME dropped via XDS protocol.
+When NEED-NAME is t, FILNAME is the base name of the file to be
+saved.
+When NEED-NAME is nil, the drop is complete."
+  (if need-name
+      (let ((method (if (eq org-dnd-method 'ask)
+                        (org--dnd-rmc
+                         "What to do with dropped file?"
+                         '((?a "attach" attach)
+                           (?o "open" open)
+                           (?f "insert file: link" file-link)))
+                      org-dnd-method)))
+        (setq-local org--dnd-xds-method method)
+        (pcase method
+          (`attach (expand-file-name filename (org-attach-dir 'create)))
+          (`open (expand-file-name (make-temp-name "emacs.") temporary-file-directory))
+          (`file-link (read-file-name "Write file to: " nil nil nil filename))))
+    (pcase org--dnd-xds-method
+      (`attach (insert (org-link-make-string
+                        (concat "attachment:" (file-name-nondirectory filename)))
+                       "\n"))
+      (`file-link (insert (org-link-make-string (concat "file:" filename))
+                          "\n"))
+      (`open (find-file filename)))
+    (setq-local org--dnd-xds-method nil)))
+
 ;;; Other stuff
 
 (defvar reftex-docstruct-symbol)
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-22  6:19                     ` Visuwesh
@ 2023-10-23  8:58                       ` Ihor Radchenko
  2023-10-23 10:12                         ` Visuwesh
  0 siblings, 1 reply; 49+ messages in thread
From: Ihor Radchenko @ 2023-10-23  8:58 UTC (permalink / raw)
  To: Visuwesh; +Cc: emacs-orgmode, Po Lu

[-- Attachment #1: Type: text/plain, Size: 3380 bytes --]

Visuwesh <visuweshm@gmail.com> writes:

>> Is there any particular reason why you insert a newline after the image
>> link?
>
> I think this can stay here until Po Lu writes the platform-agnostic code
> that you requested further down this thread?

Sure.

>> Also, we might want to add a subsection describing the new customization to
>> 17 Miscellaneous section of the manual. Otherwise, users might have
>> difficulties discovering relevant settings to customize dnd and yank
>> behavior.
>
> I have now added these to the manual with a few relevant concept
> indices.  Please check if they are understandable as I am terrible at
> writing documentation.  Thanks.

Thanks!
Upon reading the manual sections I have further comments on the code:

1. org-yank-image-save-type feels a bit inconsistent. What about
   org-yank-image-save-method? "Type" sounds more like extension (png,
   jpeg, etc)

2. org-dnd-method allows either attaching files/images or putting them
   into a dedicated directory. Same for yanked images. But it is not the
   case for yanked files - they are always attached, which is not
   consistent. May we also allow dedicated directory for yanked files?
   Or maybe simply merge the org-dnd-method customization for yank and
   dnd together.

I am attaching my further amendments on top of your patch.
For simpler review, I am also adding the manual section verbatim.

To other readers of this thread: Please help reviewing the manual
section - it should be as clear as possible for everyone.

------------------
** Drag and Drop & ~yank-media~
:PROPERTIES:
:DESCRIPTION: Dropping and pasting files and images
:END:

#+cindex: dropping files
#+cindex: dragging files
#+cindex: drag and drop
#+cindex: dnd
#+vindex: org-dnd-method
Org mode supports drag and drop (DnD) of files.  By default, Org asks
the user what must be done with the dropped file: attach it, insert
=file:= link, or open the file.  Customize ~org-dnd-method~ to set the
default DnD action.

When DnD method is "attach", Org mode first consults DnD metadata to
decide the attach method.  For example, when file/files are dragged
from a file manager, Org may attach by copying or by moving.

#+vindex: org-dnd-default-attach-method
If Org cannot figure out which attachment method to use from the
metadata, it defaults to ~org-dnd-default-attach-method~ [fn::By
default, ~org-dnd-default-attach-method~ is set to nil -- use the same
value as ~org-attach-method~ (~cp~ by default).]

#+cindex: pasting files, images from clipboard
Starting from Emacs 29, Org mode supports ~yank-media~ command to yank
images from the clipboard and files from a file manager.

#+vindex: org-yank-image-save-type
When yanking images from clipboard, Org saves the image on disk and
inserts the image link to Org buffer.  Images are either saved as
attachments to heading (default) or to a globally defined directory.
The save location is controlled by ~org-yank-image-save-type~.

#+vindex: org-yank-image-file-name-function
The yanked images are saved under automatically generated name.  You
can customize ~org-yank-image-file-name-function~ to make Org query
the image names or change the naming scheme.

When yanking files copied from a file manager, Org attaches them to
the current heading using the =mv= or the =cp= method if the files
were cut or copied in the file manager respectively.
-----------


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-doc-org-manual.org-Drag-and-Drop-yank-media-Rewrite.patch --]
[-- Type: text/x-patch, Size: 3909 bytes --]

From 23a01dd32ed21061b52557b427b2dc39a656aa8b Mon Sep 17 00:00:00 2001
Message-ID: <23a01dd32ed21061b52557b427b2dc39a656aa8b.1698051344.git.yantar92@posteo.net>
From: Ihor Radchenko <yantar92@posteo.net>
Date: Mon, 23 Oct 2023 11:50:56 +0300
Subject: [PATCH] * doc/org-manual.org (Drag and Drop & ~yank-media~): Rewrite

---
 doc/org-manual.org | 54 +++++++++++++++++++++++++---------------------
 1 file changed, 29 insertions(+), 25 deletions(-)

diff --git a/doc/org-manual.org b/doc/org-manual.org
index 5a17d1c1f..00eb6680b 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -21101,39 +21101,43 @@ ** Drag and Drop & ~yank-media~
 :END:
 
 #+cindex: dropping files
+#+cindex: dragging files
+#+cindex: drag and drop
+#+cindex: dnd
 #+vindex: org-dnd-method
-Org can attach, insert =file:= links, or open files dropped onto an
-Emacs frame.  By default, Org asks the user what must be done with the
-dropped file but this can be changed by customizing the user option
-~org-dnd-method~.  Changing the variable can make Org do any of the
-above mentioned actions without prompting.
+Org mode supports drag and drop (DnD) of files.  By default, Org asks
+the user what must be done with the dropped file: attach it, insert
+=file:= link, or open the file.  Customize ~org-dnd-method~ to set the
+default DnD action.
+
+When DnD method is "attach", Org mode first consults DnD metadata to
+decide the attach method.  For example, when file/files are dragged
+from a file manager, Org may attach by copying or by moving.
 
 #+vindex: org-dnd-default-attach-method
-If Org cannot figure out which attachment method to use automatically,
-it defaults to using the attachment method mentioned in
-~org-dnd-default-attach-method~.  Org uses the attachment method
-mentioned in ~org-attach-method~ by default, so Org using the =cp=
-method to attach the dropped files.
+If Org cannot figure out which attachment method to use from the
+metadata, it defaults to ~org-dnd-default-attach-method~ [fn::By
+default, ~org-dnd-default-attach-method~ is set to nil -- use the same
+value as ~org-attach-method~ (~cp~ by default).]
 
 #+cindex: pasting files, images from clipboard
-From Emacs 29, Org can deal with images copied to the clipboard, and
-files copied from a file manager when pasted using the command
-~yank-media~.  Org deals with them differently.
+Starting from Emacs 29, Org mode supports ~yank-media~ command to yank
+images from the clipboard and files from a file manager.
 
 #+vindex: org-yank-image-save-type
+When yanking images from clipboard, Org saves the image on disk and
+inserts the image link to Org buffer.  Images are either saved as
+attachments to heading (default) or to a globally defined directory.
+The save location is controlled by ~org-yank-image-save-type~.
+
 #+vindex: org-yank-image-file-name-function
-For images, Org attaches the image when pasting but this can be
-changed by customizing ~org-yank-image-save-type~ to save them under
-another directory instead.  The name given to these images are
-autogenerated by default but you can customize what name should be
-given to the pasted image by customizing
-~org-yank-image-file-name-function~.  This function takes no argument
-and should return the filename without extension to use as the image's
-filename.
-
-For files copied from a file manager, Org attaches them to the current
-heading using the =mv= or the =cp= method if the files were cut or
-copied in the file manager respectively.
+The yanked images are saved under automatically generated name.  You
+can customize ~org-yank-image-file-name-function~ to make Org query
+the image names or change the naming scheme.
+
+When yanking files copied from a file manager, Org attaches them to
+the current heading using the =mv= or the =cp= method if the files
+were cut or copied in the file manager respectively.
 
 * Hacking
 :PROPERTIES:
-- 
2.42.0


[-- Attachment #3: Type: text/plain, Size: 225 bytes --]



-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-23  8:58                       ` Ihor Radchenko
@ 2023-10-23 10:12                         ` Visuwesh
  2023-10-26 11:39                           ` Po Lu
  0 siblings, 1 reply; 49+ messages in thread
From: Visuwesh @ 2023-10-23 10:12 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: emacs-orgmode, Po Lu

[-- Attachment #1: Type: text/plain, Size: 1440 bytes --]

[திங்கள் அக்டோபர் 23, 2023] Ihor Radchenko wrote:

>>> Also, we might want to add a subsection describing the new customization to
>>> 17 Miscellaneous section of the manual. Otherwise, users might have
>>> difficulties discovering relevant settings to customize dnd and yank
>>> behavior.
>>
>> I have now added these to the manual with a few relevant concept
>> indices.  Please check if they are understandable as I am terrible at
>> writing documentation.  Thanks.
>
> Thanks!
> Upon reading the manual sections I have further comments on the code:
>
> 1. org-yank-image-save-type feels a bit inconsistent. What about
>    org-yank-image-save-method? "Type" sounds more like extension (png,
>    jpeg, etc)

You're right, I have now changed the name to say method instead.

> 2. org-dnd-method allows either attaching files/images or putting them
>    into a dedicated directory. Same for yanked images. But it is not the
>    case for yanked files - they are always attached, which is not
>    consistent. May we also allow dedicated directory for yanked files?
>    Or maybe simply merge the org-dnd-method customization for yank and
>    dnd together.

Hmm... yes, I now made yank-media also respect the value of
org-dnd-method.  I changed the name of org-dnd-method to
org-yank-dnd-method and similarly for the rest of the common user
options.

Here's the new patch:


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-support-for-yank-media-and-DND.patch --]
[-- Type: text/x-diff, Size: 15502 bytes --]

From c7492fc2adb72a45691124929527798bf9482c2b Mon Sep 17 00:00:00 2001
From: Visuwesh <visuweshm@gmail.com>
Date: Fri, 22 Sep 2023 20:11:41 +0530
Subject: [PATCH] Add support for yank-media and DND

* lisp/org.el (org-mode): Call the setup function for yank-media and
DND.
(org-setup-yank-dnd-handlers): Register yank-media-handler and DND
handler.
(org-yank-image-save-method, org-yank-image-file-name-function)
(org-yank-dnd-method, org-yank-dnd-default-attach-method): New
defcustoms.
(org--image-yank-media-handler, org--copied-files-yank-media-handler)
(org--dnd-rmc, org--dnd-attach-file, org--dnd-local-file-handler)
(org--dnd-xds-method, org--dnd-xds-function): Add yank-media and DND
handlers.

* doc/org-manual.org: (Drag and Drop & ~yank-media~): Describe the new
feature in the manual.

* etc/ORG-NEWS: Advertise the new features.
---
 doc/org-manual.org |  45 +++++++++
 etc/ORG-NEWS       |  24 +++++
 lisp/org.el        | 231 ++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 299 insertions(+), 1 deletion(-)

diff --git a/doc/org-manual.org b/doc/org-manual.org
index 3e9d42f55..7b14eb937 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -21095,6 +21095,51 @@ most recent since the mobile application searches files that were last
 pulled.  To get an updated agenda view with changes since the last
 pull, pull again.
 
+** Drag and Drop & ~yank-media~
+:PROPERTIES:
+:DESCRIPTION: Dropping and pasting files and images
+:END:
+
+#+cindex: dropping files
+#+cindex: dragging files
+#+cindex: drag and drop
+#+cindex: dnd
+#+vindex: org-yank-dnd-method
+Org mode supports drag and drop (DnD) of files.  By default, Org asks
+the user what must be done with the dropped file: attach it, insert
+=file:= link, or open the file.  Customize ~org-yank-dnd-method~ to
+set the default DnD action.
+
+When DnD method is "attach", Org mode first consults DnD metadata to
+decide the attach method.  For example, when file/files are dragged
+from a file manager, Org may attach by copying or by moving.
+
+#+vindex: org-yank-dnd-default-attach-method
+If Org cannot figure out which attachment method to use from the
+metadata, it defaults to ~org-yank-dnd-default-attach-method~ [fn::By
+default, ~org-yank-dnd-default-attach-method~ is set to nil -- use the same
+value as ~org-attach-method~ (~cp~ by default).]
+
+#+cindex: pasting files, images from clipboard
+Starting from Emacs 29, Org mode supports ~yank-media~ command to yank
+images from the clipboard and files from a file manager.
+
+#+vindex: org-yank-image-save-method
+When yanking images from clipboard, Org saves the image on disk and
+inserts the image link to Org buffer.  Images are either saved as
+attachments to heading (default) or to a globally defined directory.
+The save location is controlled by ~org-yank-image-save-method~.
+
+#+vindex: org-yank-image-file-name-function
+The yanked images are saved under automatically generated name.  You
+can customize ~org-yank-image-file-name-function~ to make Org query
+the image names or change the naming scheme.
+
+When yanking files copied from a file manager, Org respects the value
+of ~org-yank-dnd-method~.  Image files pasted this way also respect
+the value of ~org-yank-image-save-method~ when the action to perform
+is =attach=.
+
 * Hacking
 :PROPERTIES:
 :DESCRIPTION: How to hack your way around.
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 252c5a9f9..7c40ec0fe 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -596,6 +596,30 @@ return a matplotlib Figure object to plot. For output results, the
 current figure (as returned by =pyplot.gcf()=) is cleared before
 evaluation, and then plotted afterwards.
 
+*** Images and files in clipboard can be pasted
+
+Org asks the user what must be done when pasting images and files
+copied to the clipboard from a file manager using the ~yank-media~
+command.  The default action can be set by customizing
+~org-yank-dnd-method~.  The ~yank-media~ command was added in Emacs
+29.
+
+Images can be saved to a separate directory instead of being attached,
+customize ~org-yank-image-save-method~.
+
+Image filename chosen can be customized by setting
+~org-yank-image-file-name-function~ which by default autogenerates a
+filename based on the current time.
+
+*** Files and images can be attached by dropping onto Emacs
+
+By default, Org asks the user what to do with the dropped file like
+for pasted files.  The same user option ~org-yank-dnd-method~ is
+respected.
+
+Images dropped also respect the value of ~org-yank-image-save-method~
+when ~org-yank-dnd-method~ is =attach=.
+
 ** New functions and changes in function arguments
 *** =TYPES= argument in ~org-element-lineage~ can now be a symbol
 
diff --git a/lisp/org.el b/lisp/org.el
index d0b2355ea..317cd267f 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -4999,7 +4999,10 @@ The following commands are available:
   (org--set-faces-extend '(org-block-begin-line org-block-end-line)
                          org-fontify-whole-block-delimiter-line)
   (org--set-faces-extend org-level-faces org-fontify-whole-heading-line)
-  (setq-local org-mode-loading nil))
+  (setq-local org-mode-loading nil)
+
+  ;; `yank-media' handler and DND support.
+  (org-setup-yank-dnd-handlers))
 
 ;; Update `customize-package-emacs-version-alist'
 (add-to-list 'customize-package-emacs-version-alist
@@ -20254,6 +20257,232 @@ it has a `diary' type."
 		    (org-format-timestamp timestamp fmt t))
 	  (org-format-timestamp timestamp fmt (eq boundary 'end)))))))
 
+;;; Yank media handler and DND
+(defun org-setup-yank-dnd-handlers ()
+  "Setup the `yank-media' and DND handlers for buffer."
+  (setq-local dnd-protocol-alist
+              (cons '("^file:///" . org--dnd-local-file-handler)
+                    dnd-protocol-alist))
+  (when (fboundp 'yank-media-handler)
+    (yank-media-handler "image/.*" #'org--image-yank-media-handler)
+    ;; Looks like different DEs go for different handler names,
+    ;; https://larsee.com/blog/2019/05/clipboard-files/.
+    (yank-media-handler "x/special-\\(?:gnome\|KDE\|mate\\)-files"
+                        #'org--copied-files-yank-media-handler))
+  (when (boundp 'x-dnd-direct-save-function)
+    (setq-local x-dnd-direct-save-function #'org--dnd-xds-function)))
+
+(defcustom org-yank-image-save-method 'attach
+  "Method to save images yanked from clipboard and dropped to Emacs.
+It can be the symbol `attach' to add it as an attachment, or a
+directory name to copy/cut the image to that directory."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Add it as attachment" attach)
+                 (directory :tag "Save it in directory"))
+  :safe (lambda (x) (eq x 'attach)))
+
+(defcustom org-yank-image-file-name-function #'org-yank-image-autogen-filename
+  "Function to generate filename for image yanked from clipboard.
+By default, this autogenerates a filename based on the current
+time.
+It is called with no arguments and should return a string without
+any extension which is used as the filename."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(radio (function-item :doc "Autogenerate filename"
+                               org-yank-image-autogen-filename)
+                (function-item :doc "Ask for filename"
+                               org-yank-image-read-filename)
+                function))
+
+(defun org-yank-image-autogen-filename ()
+  "Autogenerate filename for image in clipboard."
+  (format-time-string "clipboard-%Y%m%dT%H%M%S.%6N"))
+
+(defun org-yank-image-read-filename ()
+  "Read filename for image in clipboard."
+  (read-string "Basename for image file without extension: "))
+
+(declare-function org-attach-attach "org-attach" (file &optional visit-dir method))
+
+(defun org--image-yank-media-handler (mimetype data)
+  "Save image DATA of mime-type MIMETYPE and insert link at point.
+It is saved as per `org-yank-image-save-method'.  The name for the
+image is prompted and the extension is automatically added to the
+end."
+  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
+         (iname (funcall org-yank-image-file-name-function))
+         (filename (file-name-with-extension iname ext))
+         (absname (expand-file-name
+                   filename
+                   (if (eq org-yank-image-save-method 'attach)
+                       temporary-file-directory
+                     org-yank-image-save-method)))
+         link)
+    (when (and (not (eq org-yank-image-save-method 'attach))
+               (not (file-directory-p org-yank-image-save-method)))
+      (make-directory org-yank-image-save-method t))
+    (with-temp-file absname
+      (insert data))
+    (if (null (eq org-yank-image-save-method 'attach))
+        (setq link (org-link-make-string (concat "file:" (file-relative-name absname))))
+      (require 'org-attach)
+      (org-attach-attach absname nil 'mv)
+      (setq link (org-link-make-string (concat "attachment:" filename))))
+    (insert link)))
+
+;; I cannot find a spec for this but
+;; https://indigo.re/posts/2021-12-21-clipboard-data.html and pcmanfm
+;; suggests that this is the format.
+(defun org--copied-files-yank-media-handler (_mimetype data)
+  "Handle copied or cut files from file manager.
+They are handled as per `org-yank-dnd-method'.
+DATA is a string where the first line is the operation to
+perform: copy or cut.  Rest of the lines are file: links to the
+concerned files."
+  ;; pcmanfm adds a null byte at the end for some reason.
+  (let* ((data (split-string data "[\0\n\r]" t))
+         (files (cdr data))
+         (action (if (equal (car data) "cut")
+                     'copy
+                   'move)))
+    (dolist (f files)
+      (if (file-readable-p f)
+          (org--dnd-local-file-handler f action)
+        (message "File `%s' is not readable, skipping" f)))))
+
+(defcustom org-yank-dnd-method 'ask
+  "Action to perform on the dropped and the pasted files.
+When the value is the symbol,
+  . `attach' -- attach dropped/pasted file
+  . `open' -- visit/open dropped/pasted file in Emacs
+  . `file-link' -- insert file: link to dropped/pasted file
+  . `ask' -- ask what to do out of the above."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Attach" attach)
+                 (const :tag "Open/Visit file" open)
+                 (const :tag "Insert file: link" file-link)
+                 (const :tag "Ask what to do" ask)))
+
+(defcustom org-yank-dnd-default-attach-method nil
+  "Default attach method to use when DND action is unspecified.
+This attach method is used when the DND action is `private'.
+This is also used when `org-yank-image-save-method' is nil.
+When nil, use `org-attach-method'."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Default attach method" nil)
+                 (const :tag "Copy" cp)
+                 (const :tag "Move" mv)
+                 (const :tag "Hard link" ln)
+                 (const :tag "Symbolic link" lns)))
+
+(declare-function mailcap-file-name-to-mime-type "mailcap" (file-name))
+(defvar org-attach-method)
+
+(defun org--dnd-rmc (prompt choices)
+  (if (null (use-dialog-box-p))
+      (caddr (read-multiple-choice prompt choices))
+    (setq choices
+          (mapcar
+           (pcase-lambda (`(_key ,message ,val))
+             (cons (capitalize message) val))
+           choices))
+    (x-popup-menu t (list prompt (cons "" choices)))))
+
+(defun org--dnd-local-file-handler (url action)
+  "Handle file URL as per ACTION."
+  (let ((method (if (eq org-yank-dnd-method 'ask)
+                    (org--dnd-rmc
+                     "What to do with file?"
+                     '((?a "attach" attach)
+                       (?o "open" open)
+                       (?f "insert file: link" file-link)))
+                  org-yank-dnd-method)))
+    (pcase method
+      (`attach (org--dnd-attach-file url action))
+      (`open (dnd-open-local-file url action))
+      (`file-link
+       (let ((filename (dnd-get-local-file-name url)))
+         (insert (org-link-make-string (concat "file:" filename))))))))
+
+(defun org--dnd-attach-file (url action)
+  "Attach filename given by URL using method pertaining to ACTION.
+If ACTION is `move', use `mv' attach method.
+If `copy', use `cp' attach method.
+If `ask', ask the user.
+If `private', use the method denoted in `org-yank-dnd-default-attach-method'.
+The action `private' is always returned."
+  (require 'mailcap)
+  (let* ((filename (dnd-get-local-file-name url))
+         (mimetype (mailcap-file-name-to-mime-type filename))
+         (separatep (and (string-prefix-p "image/" mimetype)
+                         (not (eq 'attach org-yank-image-save-method))))
+         (method (pcase action
+                   ('copy 'cp)
+                   ('move 'mv)
+                   ('ask (org--dnd-rmc
+                          "Attach using method"
+                          '((?c "copy" cp)
+                            (?m "move" mv)
+                            (?l "hard link" ln)
+                            (?s "symbolic link" lns))))
+                   ('private (or org-yank-dnd-default-attach-method
+                                 org-attach-method)))))
+    (if separatep
+        (funcall
+         (pcase method
+           ('cp #'copy-file)
+           ('mv #'rename-file)
+           ('ln #'add-name-to-file)
+           ('lns #'make-symbolic-link))
+         filename
+         (expand-file-name (file-name-nondirectory filename)
+                           org-yank-image-save-method))
+      (org-attach-attach filename nil method))
+    (insert
+     (org-link-make-string
+      (concat (if separatep
+                  "file:"
+                "attachment:")
+              (if separatep
+                  (expand-file-name (file-name-nondirectory filename)
+                                    org-yank-image-save-method)
+                (file-name-nondirectory filename))))
+     "\n")
+    'private))
+
+(defvar-local org--dnd-xds-method nil
+  "The method to use for dropped file.")
+(defun org--dnd-xds-function (need-name filename)
+  "Handle file with FILENAME dropped via XDS protocol.
+When NEED-NAME is t, FILNAME is the base name of the file to be
+saved.
+When NEED-NAME is nil, the drop is complete."
+  (if need-name
+      (let ((method (if (eq org-yank-dnd-method 'ask)
+                        (org--dnd-rmc
+                         "What to do with dropped file?"
+                         '((?a "attach" attach)
+                           (?o "open" open)
+                           (?f "insert file: link" file-link)))
+                      org-yank-dnd-method)))
+        (setq-local org--dnd-xds-method method)
+        (pcase method
+          (`attach (expand-file-name filename (org-attach-dir 'create)))
+          (`open (expand-file-name (make-temp-name "emacs.") temporary-file-directory))
+          (`file-link (read-file-name "Write file to: " nil nil nil filename))))
+    (pcase org--dnd-xds-method
+      (`attach (insert (org-link-make-string
+                        (concat "attachment:" (file-name-nondirectory filename)))
+                       "\n"))
+      (`file-link (insert (org-link-make-string (concat "file:" filename))
+                          "\n"))
+      (`open (find-file filename)))
+    (setq-local org--dnd-xds-method nil)))
+
 ;;; Other stuff
 
 (defvar reftex-docstruct-symbol)
-- 
2.42.0


[-- Attachment #3: Type: text/plain, Size: 2199 bytes --]


> I am attaching my further amendments on top of your patch.
> For simpler review, I am also adding the manual section verbatim.
>
> To other readers of this thread: Please help reviewing the manual
> section - it should be as clear as possible for everyone.

Thanks for your review.  The manual now says:

** Drag and Drop & ~yank-media~
:PROPERTIES:
:DESCRIPTION: Dropping and pasting files and images
:END:

#+cindex: dropping files
#+cindex: dragging files
#+cindex: drag and drop
#+cindex: dnd
#+vindex: org-yank-dnd-method
Org mode supports drag and drop (DnD) of files.  By default, Org asks
the user what must be done with the dropped file: attach it, insert
=file:= link, or open the file.  Customize ~org-yank-dnd-method~ to
set the default DnD action.

When DnD method is "attach", Org mode first consults DnD metadata to
decide the attach method.  For example, when file/files are dragged
from a file manager, Org may attach by copying or by moving.

#+vindex: org-yank-dnd-default-attach-method
If Org cannot figure out which attachment method to use from the
metadata, it defaults to ~org-yank-dnd-default-attach-method~ [fn::By
default, ~org-yank-dnd-default-attach-method~ is set to nil -- use the same
value as ~org-attach-method~ (~cp~ by default).]

#+cindex: pasting files, images from clipboard
Starting from Emacs 29, Org mode supports ~yank-media~ command to yank
images from the clipboard and files from a file manager.

#+vindex: org-yank-image-save-method
When yanking images from clipboard, Org saves the image on disk and
inserts the image link to Org buffer.  Images are either saved as
attachments to heading (default) or to a globally defined directory.
The save location is controlled by ~org-yank-image-save-method~.

#+vindex: org-yank-image-file-name-function
The yanked images are saved under automatically generated name.  You
can customize ~org-yank-image-file-name-function~ to make Org query
the image names or change the naming scheme.

When yanking files copied from a file manager, Org respects the value
of ~org-yank-dnd-method~.  Image files pasted this way also respect
the value of ~org-yank-image-save-method~ when the action to perform
is =attach=.

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-23 10:12                         ` Visuwesh
@ 2023-10-26 11:39                           ` Po Lu
  2023-11-05 12:02                             ` Ihor Radchenko
  0 siblings, 1 reply; 49+ messages in thread
From: Po Lu @ 2023-10-26 11:39 UTC (permalink / raw)
  To: Visuwesh; +Cc: Ihor Radchenko, emacs-orgmode

I have now installed an interface to this end into Emacs 30.  The
pertinent documentation is under the ``Drag and Drop'' node in the Lisp
reference manual.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-10-26 11:39                           ` Po Lu
@ 2023-11-05 12:02                             ` Ihor Radchenko
  2023-11-05 17:45                               ` Visuwesh
  2023-12-05 13:18                               ` Visuwesh
  0 siblings, 2 replies; 49+ messages in thread
From: Ihor Radchenko @ 2023-11-05 12:02 UTC (permalink / raw)
  To: Po Lu; +Cc: Visuwesh, emacs-orgmode

Po Lu <luangruo@yahoo.com> writes:

> I have now installed an interface to this end into Emacs 30.  The
> pertinent documentation is under the ``Drag and Drop'' node in the Lisp
> reference manual.

Thanks!

AFAIU, the relevant paragraph is

    @cindex dnd-multiple-handler, a symbol property
    If a handler function is a symbol whose @code{dnd-multiple-handler}
    property (@pxref{Symbol Properties}) is set, then upon a drop it is
    given a list of every URL that matches its regexp; absent this
    property, it is called once for each of those URLs.  Following this
    first argument is one of the symbols @code{copy}, @code{move},
    @code{link}, @code{private} or @code{ask} identifying the action to be
    taken.

Visuwesh, do you want to update your patch accordingly?

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-11-05 12:02                             ` Ihor Radchenko
@ 2023-11-05 17:45                               ` Visuwesh
  2023-12-05 13:18                               ` Visuwesh
  1 sibling, 0 replies; 49+ messages in thread
From: Visuwesh @ 2023-11-05 17:45 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: Po Lu, emacs-orgmode

[ஞாயிறு நவம்பர் 05, 2023] Ihor Radchenko wrote:

> Po Lu <luangruo@yahoo.com> writes:
>
>> I have now installed an interface to this end into Emacs 30.  The
>> pertinent documentation is under the ``Drag and Drop'' node in the Lisp
>> reference manual.
> [...]
> Visuwesh, do you want to update your patch accordingly?

Yes, I intend to.  But this might not happen until December since I am
busy with the end of my semester.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-11-05 12:02                             ` Ihor Radchenko
  2023-11-05 17:45                               ` Visuwesh
@ 2023-12-05 13:18                               ` Visuwesh
  2023-12-10 13:53                                 ` Ihor Radchenko
  1 sibling, 1 reply; 49+ messages in thread
From: Visuwesh @ 2023-12-05 13:18 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: Po Lu, emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 513 bytes --]

[ஞாயிறு நவம்பர் 05, 2023] Ihor Radchenko wrote:

> Po Lu <luangruo@yahoo.com> writes:
>
>> I have now installed an interface to this end into Emacs 30.  The
>> pertinent documentation is under the ``Drag and Drop'' node in the Lisp
>> reference manual.
>
> Thanks!
>
> [...]
>
> Visuwesh, do you want to update your patch accordingly?

I have now adjusted the patch to insert a space between links when the
Emacs version in <30, and when more than one file is dropped.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-support-for-yank-media-and-DND.patch --]
[-- Type: text/x-diff, Size: 16263 bytes --]

From 93db10f9f0c618be5512fbd67cd7b7676d6b1b0c Mon Sep 17 00:00:00 2001
From: Visuwesh <visuweshm@gmail.com>
Date: Fri, 22 Sep 2023 20:11:41 +0530
Subject: [PATCH] Add support for yank-media and DND

* lisp/org.el (org-mode): Call the setup function for yank-media and
DND.
(org-setup-yank-dnd-handlers): Register yank-media-handler and DND
handler.
(org-yank-image-save-method, org-yank-image-file-name-function)
(org-yank-dnd-method, org-yank-dnd-default-attach-method): New
defcustoms.
(org--image-yank-media-handler, org--copied-files-yank-media-handler)
(org--dnd-rmc, org--dnd-multi-local-file-handler)
(org--dnd-local-file-handler, org--dnd-attach-file, org--dnd-xds-method)
(org--dnd-xds-function): Add yank-media and DND handlers.

* doc/org-manual.org: (Drag and Drop & ~yank-media~): Describe the new
feature in the manual.

* etc/ORG-NEWS: Advertise the new features.
---
 doc/org-manual.org |  45 ++++++++
 etc/ORG-NEWS       |  24 +++++
 lisp/org.el        | 248 ++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 316 insertions(+), 1 deletion(-)

diff --git a/doc/org-manual.org b/doc/org-manual.org
index 3e9d42f55..7b14eb937 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -21095,6 +21095,51 @@ most recent since the mobile application searches files that were last
 pulled.  To get an updated agenda view with changes since the last
 pull, pull again.
 
+** Drag and Drop & ~yank-media~
+:PROPERTIES:
+:DESCRIPTION: Dropping and pasting files and images
+:END:
+
+#+cindex: dropping files
+#+cindex: dragging files
+#+cindex: drag and drop
+#+cindex: dnd
+#+vindex: org-yank-dnd-method
+Org mode supports drag and drop (DnD) of files.  By default, Org asks
+the user what must be done with the dropped file: attach it, insert
+=file:= link, or open the file.  Customize ~org-yank-dnd-method~ to
+set the default DnD action.
+
+When DnD method is "attach", Org mode first consults DnD metadata to
+decide the attach method.  For example, when file/files are dragged
+from a file manager, Org may attach by copying or by moving.
+
+#+vindex: org-yank-dnd-default-attach-method
+If Org cannot figure out which attachment method to use from the
+metadata, it defaults to ~org-yank-dnd-default-attach-method~ [fn::By
+default, ~org-yank-dnd-default-attach-method~ is set to nil -- use the same
+value as ~org-attach-method~ (~cp~ by default).]
+
+#+cindex: pasting files, images from clipboard
+Starting from Emacs 29, Org mode supports ~yank-media~ command to yank
+images from the clipboard and files from a file manager.
+
+#+vindex: org-yank-image-save-method
+When yanking images from clipboard, Org saves the image on disk and
+inserts the image link to Org buffer.  Images are either saved as
+attachments to heading (default) or to a globally defined directory.
+The save location is controlled by ~org-yank-image-save-method~.
+
+#+vindex: org-yank-image-file-name-function
+The yanked images are saved under automatically generated name.  You
+can customize ~org-yank-image-file-name-function~ to make Org query
+the image names or change the naming scheme.
+
+When yanking files copied from a file manager, Org respects the value
+of ~org-yank-dnd-method~.  Image files pasted this way also respect
+the value of ~org-yank-image-save-method~ when the action to perform
+is =attach=.
+
 * Hacking
 :PROPERTIES:
 :DESCRIPTION: How to hack your way around.
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 252c5a9f9..7c40ec0fe 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -596,6 +596,30 @@ return a matplotlib Figure object to plot. For output results, the
 current figure (as returned by =pyplot.gcf()=) is cleared before
 evaluation, and then plotted afterwards.
 
+*** Images and files in clipboard can be pasted
+
+Org asks the user what must be done when pasting images and files
+copied to the clipboard from a file manager using the ~yank-media~
+command.  The default action can be set by customizing
+~org-yank-dnd-method~.  The ~yank-media~ command was added in Emacs
+29.
+
+Images can be saved to a separate directory instead of being attached,
+customize ~org-yank-image-save-method~.
+
+Image filename chosen can be customized by setting
+~org-yank-image-file-name-function~ which by default autogenerates a
+filename based on the current time.
+
+*** Files and images can be attached by dropping onto Emacs
+
+By default, Org asks the user what to do with the dropped file like
+for pasted files.  The same user option ~org-yank-dnd-method~ is
+respected.
+
+Images dropped also respect the value of ~org-yank-image-save-method~
+when ~org-yank-dnd-method~ is =attach=.
+
 ** New functions and changes in function arguments
 *** =TYPES= argument in ~org-element-lineage~ can now be a symbol
 
diff --git a/lisp/org.el b/lisp/org.el
index d0b2355ea..086b3dff5 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -4999,7 +4999,10 @@ The following commands are available:
   (org--set-faces-extend '(org-block-begin-line org-block-end-line)
                          org-fontify-whole-block-delimiter-line)
   (org--set-faces-extend org-level-faces org-fontify-whole-heading-line)
-  (setq-local org-mode-loading nil))
+  (setq-local org-mode-loading nil)
+
+  ;; `yank-media' handler and DND support.
+  (org-setup-yank-dnd-handlers))
 
 ;; Update `customize-package-emacs-version-alist'
 (add-to-list 'customize-package-emacs-version-alist
@@ -20254,6 +20257,249 @@ it has a `diary' type."
 		    (org-format-timestamp timestamp fmt t))
 	  (org-format-timestamp timestamp fmt (eq boundary 'end)))))))
 
+;;; Yank media handler and DND
+(defun org-setup-yank-dnd-handlers ()
+  "Setup the `yank-media' and DND handlers for buffer."
+  (setq-local dnd-protocol-alist
+              (cons (cons "^file:///"
+                          (if (>= emacs-major-version 30)
+                              #'org--dnd-multi-local-file-handler
+                            #'org--dnd-local-file-handler))
+                    dnd-protocol-alist))
+  (when (fboundp 'yank-media-handler)
+    (yank-media-handler "image/.*" #'org--image-yank-media-handler)
+    ;; Looks like different DEs go for different handler names,
+    ;; https://larsee.com/blog/2019/05/clipboard-files/.
+    (yank-media-handler "x/special-\\(?:gnome\|KDE\|mate\\)-files"
+                        #'org--copied-files-yank-media-handler))
+  (when (boundp 'x-dnd-direct-save-function)
+    (setq-local x-dnd-direct-save-function #'org--dnd-xds-function)))
+
+(defcustom org-yank-image-save-method 'attach
+  "Method to save images yanked from clipboard and dropped to Emacs.
+It can be the symbol `attach' to add it as an attachment, or a
+directory name to copy/cut the image to that directory."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Add it as attachment" attach)
+                 (directory :tag "Save it in directory"))
+  :safe (lambda (x) (eq x 'attach)))
+
+(defcustom org-yank-image-file-name-function #'org-yank-image-autogen-filename
+  "Function to generate filename for image yanked from clipboard.
+By default, this autogenerates a filename based on the current
+time.
+It is called with no arguments and should return a string without
+any extension which is used as the filename."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(radio (function-item :doc "Autogenerate filename"
+                               org-yank-image-autogen-filename)
+                (function-item :doc "Ask for filename"
+                               org-yank-image-read-filename)
+                function))
+
+(defun org-yank-image-autogen-filename ()
+  "Autogenerate filename for image in clipboard."
+  (format-time-string "clipboard-%Y%m%dT%H%M%S.%6N"))
+
+(defun org-yank-image-read-filename ()
+  "Read filename for image in clipboard."
+  (read-string "Basename for image file without extension: "))
+
+(declare-function org-attach-attach "org-attach" (file &optional visit-dir method))
+
+(defun org--image-yank-media-handler (mimetype data)
+  "Save image DATA of mime-type MIMETYPE and insert link at point.
+It is saved as per `org-yank-image-save-method'.  The name for the
+image is prompted and the extension is automatically added to the
+end."
+  (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype)))
+         (iname (funcall org-yank-image-file-name-function))
+         (filename (file-name-with-extension iname ext))
+         (absname (expand-file-name
+                   filename
+                   (if (eq org-yank-image-save-method 'attach)
+                       temporary-file-directory
+                     org-yank-image-save-method)))
+         link)
+    (when (and (not (eq org-yank-image-save-method 'attach))
+               (not (file-directory-p org-yank-image-save-method)))
+      (make-directory org-yank-image-save-method t))
+    (with-temp-file absname
+      (insert data))
+    (if (null (eq org-yank-image-save-method 'attach))
+        (setq link (org-link-make-string (concat "file:" (file-relative-name absname))))
+      (require 'org-attach)
+      (org-attach-attach absname nil 'mv)
+      (setq link (org-link-make-string (concat "attachment:" filename))))
+    (insert link)))
+
+;; I cannot find a spec for this but
+;; https://indigo.re/posts/2021-12-21-clipboard-data.html and pcmanfm
+;; suggests that this is the format.
+(defun org--copied-files-yank-media-handler (_mimetype data)
+  "Handle copied or cut files from file manager.
+They are handled as per `org-yank-dnd-method'.
+DATA is a string where the first line is the operation to
+perform: copy or cut.  Rest of the lines are file: links to the
+concerned files."
+  ;; pcmanfm adds a null byte at the end for some reason.
+  (let* ((data (split-string data "[\0\n\r]" t))
+         (files (cdr data))
+         (action (if (equal (car data) "cut")
+                     'copy
+                   'move))
+         (sep (if (= (length files) 1) "" " ")))
+    (dolist (f files)
+      (if (file-readable-p f)
+          (org--dnd-local-file-handler f action sep)
+        (message "File `%s' is not readable, skipping" f)))))
+
+(defcustom org-yank-dnd-method 'ask
+  "Action to perform on the dropped and the pasted files.
+When the value is the symbol,
+  . `attach' -- attach dropped/pasted file
+  . `open' -- visit/open dropped/pasted file in Emacs
+  . `file-link' -- insert file: link to dropped/pasted file
+  . `ask' -- ask what to do out of the above."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Attach" attach)
+                 (const :tag "Open/Visit file" open)
+                 (const :tag "Insert file: link" file-link)
+                 (const :tag "Ask what to do" ask)))
+
+(defcustom org-yank-dnd-default-attach-method nil
+  "Default attach method to use when DND action is unspecified.
+This attach method is used when the DND action is `private'.
+This is also used when `org-yank-image-save-method' is nil.
+When nil, use `org-attach-method'."
+  :group 'org
+  :package-version '(Org . "9.7")
+  :type '(choice (const :tag "Default attach method" nil)
+                 (const :tag "Copy" cp)
+                 (const :tag "Move" mv)
+                 (const :tag "Hard link" ln)
+                 (const :tag "Symbolic link" lns)))
+
+(declare-function mailcap-file-name-to-mime-type "mailcap" (file-name))
+(defvar org-attach-method)
+
+(defun org--dnd-rmc (prompt choices)
+  (if (null (use-dialog-box-p))
+      (caddr (read-multiple-choice prompt choices))
+    (setq choices
+          (mapcar
+           (pcase-lambda (`(_key ,message ,val))
+             (cons (capitalize message) val))
+           choices))
+    (x-popup-menu t (list prompt (cons "" choices)))))
+
+(defun org--dnd-multi-local-file-handler (urls action)
+  "Handle file URLS as per ACTION.
+URLS is a list of file URL."
+  (let ((sep (if (= (length urls) 1) "" " ")))
+    (dolist (u urls)
+      (org--dnd-local-file-handler u action sep))))
+
+(put 'org--dnd-multi-local-file-handler 'dnd-multiple-handler t)
+
+(defun org--dnd-local-file-handler (url action &optional separator)
+  "Handle file URL as per ACTION.
+SEPARATOR is the string to insert after each link.  It may be nil
+in which case, space is inserted."
+  (unless separator
+    (setq separator " "))
+  (let ((method (if (eq org-yank-dnd-method 'ask)
+                    (org--dnd-rmc
+                     "What to do with file?"
+                     '((?a "attach" attach)
+                       (?o "open" open)
+                       (?f "insert file: link" file-link)))
+                  org-yank-dnd-method)))
+    (pcase method
+      (`attach (org--dnd-attach-file url action separator))
+      (`open (dnd-open-local-file url action))
+      (`file-link
+       (let ((filename (dnd-get-local-file-name url)))
+         (insert (org-link-make-string (concat "file:" filename)) separator))))))
+
+(defun org--dnd-attach-file (url action separator)
+  "Attach filename given by URL using method pertaining to ACTION.
+If ACTION is `move', use `mv' attach method.
+If `copy', use `cp' attach method.
+If `ask', ask the user.
+If `private', use the method denoted in `org-yank-dnd-default-attach-method'.
+The action `private' is always returned.
+
+SEPARATOR is the string to insert after each link."
+  (require 'mailcap)
+  (let* ((filename (dnd-get-local-file-name url))
+         (mimetype (mailcap-file-name-to-mime-type filename))
+         (separatep (and (string-prefix-p "image/" mimetype)
+                         (not (eq 'attach org-yank-image-save-method))))
+         (method (pcase action
+                   ('copy 'cp)
+                   ('move 'mv)
+                   ('ask (org--dnd-rmc
+                          "Attach using method"
+                          '((?c "copy" cp)
+                            (?m "move" mv)
+                            (?l "hard link" ln)
+                            (?s "symbolic link" lns))))
+                   ('private (or org-yank-dnd-default-attach-method
+                                 org-attach-method)))))
+    (if separatep
+        (funcall
+         (pcase method
+           ('cp #'copy-file)
+           ('mv #'rename-file)
+           ('ln #'add-name-to-file)
+           ('lns #'make-symbolic-link))
+         filename
+         (expand-file-name (file-name-nondirectory filename)
+                           org-yank-image-save-method))
+      (org-attach-attach filename nil method))
+    (insert
+     (org-link-make-string
+      (concat (if separatep
+                  "file:"
+                "attachment:")
+              (if separatep
+                  (expand-file-name (file-name-nondirectory filename)
+                                    org-yank-image-save-method)
+                (file-name-nondirectory filename))))
+     separator)
+    'private))
+
+(defvar-local org--dnd-xds-method nil
+  "The method to use for dropped file.")
+(defun org--dnd-xds-function (need-name filename)
+  "Handle file with FILENAME dropped via XDS protocol.
+When NEED-NAME is t, FILNAME is the base name of the file to be
+saved.
+When NEED-NAME is nil, the drop is complete."
+  (if need-name
+      (let ((method (if (eq org-yank-dnd-method 'ask)
+                        (org--dnd-rmc
+                         "What to do with dropped file?"
+                         '((?a "attach" attach)
+                           (?o "open" open)
+                           (?f "insert file: link" file-link)))
+                      org-yank-dnd-method)))
+        (setq-local org--dnd-xds-method method)
+        (pcase method
+          (`attach (expand-file-name filename (org-attach-dir 'create)))
+          (`open (expand-file-name (make-temp-name "emacs.") temporary-file-directory))
+          (`file-link (read-file-name "Write file to: " nil nil nil filename))))
+    (pcase org--dnd-xds-method
+      (`attach (insert (org-link-make-string
+                        (concat "attachment:" (file-name-nondirectory filename)))))
+      (`file-link (insert (org-link-make-string (concat "file:" filename))))
+      (`open (find-file filename)))
+    (setq-local org--dnd-xds-method nil)))
+
 ;;; Other stuff
 
 (defvar reftex-docstruct-symbol)
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-12-05 13:18                               ` Visuwesh
@ 2023-12-10 13:53                                 ` Ihor Radchenko
  2023-12-10 14:47                                   ` Bastien Guerry
  0 siblings, 1 reply; 49+ messages in thread
From: Ihor Radchenko @ 2023-12-10 13:53 UTC (permalink / raw)
  To: Visuwesh, Bastien; +Cc: Po Lu, emacs-orgmode

Visuwesh <visuweshm@gmail.com> writes:

>> Visuwesh, do you want to update your patch accordingly?
>
> I have now adjusted the patch to insert a space between links when the
> Emacs version in <30, and when more than one file is dropped.

Thanks!
I have no comments on the patch.

Before I proceed with installing it; Bastien, may you please confirm
Visuwesh's copyright assignment status?

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-12-10 13:53                                 ` Ihor Radchenko
@ 2023-12-10 14:47                                   ` Bastien Guerry
  2023-12-10 15:07                                     ` Ihor Radchenko
  0 siblings, 1 reply; 49+ messages in thread
From: Bastien Guerry @ 2023-12-10 14:47 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: Visuwesh, Po Lu, emacs-orgmode

Ihor Radchenko <yantar92@posteo.net> writes:

> Before I proceed with installing it; Bastien, may you please confirm
> Visuwesh's copyright assignment status?

Confirmed and added to Worg. Thanks Visuwesh for contributing!

-- 
 Bastien Guerry


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [BUG] [PATCH] Add yank-media and DND handler  [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)]
  2023-12-10 14:47                                   ` Bastien Guerry
@ 2023-12-10 15:07                                     ` Ihor Radchenko
  0 siblings, 0 replies; 49+ messages in thread
From: Ihor Radchenko @ 2023-12-10 15:07 UTC (permalink / raw)
  To: Bastien Guerry; +Cc: Visuwesh, Po Lu, emacs-orgmode

Bastien Guerry <bzg@gnu.org> writes:

> Ihor Radchenko <yantar92@posteo.net> writes:
>
>> Before I proceed with installing it; Bastien, may you please confirm
>> Visuwesh's copyright assignment status?
>
> Confirmed and added to Worg. Thanks Visuwesh for contributing!

Thanks for checking!
Applied, onto main.
https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=b77f991c0
Fixed.

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>


^ permalink raw reply	[flat|nested] 49+ messages in thread

end of thread, other threads:[~2023-12-10 15:05 UTC | newest]

Thread overview: 49+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <87bkdccihf.fsf.ref@yahoo.com>
2023-09-22 14:52 ` [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)] Visuwesh
2023-09-22 16:51   ` Max Nikulin
2023-09-22 17:29     ` Visuwesh
2023-09-24  8:06       ` Max Nikulin
2023-09-23 10:28   ` Ihor Radchenko
2023-09-23 16:55     ` Visuwesh
2023-09-25 13:14       ` Visuwesh
2023-09-26 16:25         ` Max Nikulin
2023-09-27  8:33           ` Visuwesh
2023-10-07 11:56           ` Ihor Radchenko
2023-10-07 12:07         ` Ihor Radchenko
2023-10-07 12:27           ` Visuwesh
2023-10-07 12:36             ` Ihor Radchenko
2023-10-07 14:03             ` Visuwesh
2023-10-08  9:30               ` Ihor Radchenko
2023-10-08 11:21                 ` Visuwesh
2023-10-09 11:12                   ` Ihor Radchenko
2023-10-09 12:17                     ` Visuwesh
2023-10-19  7:34                       ` Visuwesh
2023-10-19  9:44                         ` Ihor Radchenko
2023-10-20  1:52                           ` Po Lu
2023-10-20  7:29                             ` Ihor Radchenko
2023-10-20  7:46                               ` Po Lu
2023-10-20  7:57                                 ` Ihor Radchenko
2023-10-20  8:29                                   ` Po Lu
2023-10-20 10:17                                   ` Visuwesh
2023-10-22  6:19                     ` Visuwesh
2023-10-23  8:58                       ` Ihor Radchenko
2023-10-23 10:12                         ` Visuwesh
2023-10-26 11:39                           ` Po Lu
2023-11-05 12:02                             ` Ihor Radchenko
2023-11-05 17:45                               ` Visuwesh
2023-12-05 13:18                               ` Visuwesh
2023-12-10 13:53                                 ` Ihor Radchenko
2023-12-10 14:47                                   ` Bastien Guerry
2023-12-10 15:07                                     ` Ihor Radchenko
2023-09-24 14:58     ` Max Nikulin
2023-09-25 14:15       ` Visuwesh
2023-09-26 10:24         ` Ihor Radchenko
2023-09-27  8:29           ` Visuwesh
2023-09-28 12:01             ` Max Nikulin
2023-09-24 14:49   ` Max Nikulin
2023-10-06  7:34   ` Po Lu
2023-09-29  8:20 Liu Hui
2023-10-01 14:28 ` Visuwesh
2023-10-02  0:28   ` Liu Hui
  -- strict thread matches above, loose matches on Subject: below --
2023-10-11 14:24 Liu Hui
2023-10-11 15:36 ` Visuwesh
2023-10-12  5:12   ` Liu Hui

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).