From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp10.migadu.com ([2001:41d0:306:2d92::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms9.migadu.com with LMTPS id 6BKlLWiqDWVTCAEAG6o9tA:P1 (envelope-from ) for ; Fri, 22 Sep 2023 16:53:28 +0200 Received: from aspmx1.migadu.com ([2001:41d0:306:2d92::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp10.migadu.com with LMTPS id 6BKlLWiqDWVTCAEAG6o9tA (envelope-from ) for ; Fri, 22 Sep 2023 16:53:28 +0200 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 3508655262 for ; Fri, 22 Sep 2023 16:53:28 +0200 (CEST) Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=L3+DtmOa; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gmail.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1695394408; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:mime-version:mime-version: content-type:content-type:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=twcBiEK3wRsCh+TOGO4+39zgbzgMQsMd6WhEVBQO414=; b=Xw8rWHUpIwrAakAKeO8EQhYlC6vpWwCIyJiwcdrp1F3qg5r0LMOOIubzJT93H7vq0nX3mZ Nfjy5SRF+6RD/5fq6aYHP3Mw0X+G1gouQLWqy4d/0MQINVbJAcW8jmuPNfR7fZL3PhiXKT qwb/W5iZDVL6il/bc3V0yMI6lKPCW+QdM9tjMeL0sprqOPoxYOQkv1IfXeeYMFYw8xXki6 QLmAuQAAGN9FZ7+lKmq3ScOA7VBhE8XWmIP3ftscFQclJdYb1x+IowtFlIv6/160ysCCL0 pG1wrRT8xbod0IXzlAYxuevTpyq7FvLKACZ7nYTotFse4dV7LrMSfDd3wzGV/w== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=L3+DtmOa; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gmail.com ARC-Seal: i=1; s=key1; d=yhetil.org; t=1695394408; a=rsa-sha256; cv=none; b=nGGMjycA39klGNQCAGQb7b+5LOZisSycBi9qCMg0cSnhr70+v6lLtF1xhFXKpgB1lpN8BO vHjjN9NXf50xxzX7lqO6ICP/HMUXuuRnBxwadzYQykbpiLoBmK4kvbrqeieZt/pEGYpC9/ Y90pFiUycY5tCBM9IABfDLMMS66rOjOFuxyRbYMJAzMDSz1kf0vzUO3YuwZPFaI8KTQKa9 3gr0v+h4FYLwPta5w00v1EV5BmbXQb3SuxnCFor7KXOoy3xz7jTgx7hUKuv/W2zCq1ROKH GT0QREAtX97L+WGVHeHxG4BFHpheR3gNLJPZr1EQYxC8hnR/tmvg+ut9kdM+BQ== Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1qjhVv-0003bD-9B; Fri, 22 Sep 2023 10:52:15 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qjhVt-0003Zz-LW for emacs-orgmode@gnu.org; Fri, 22 Sep 2023 10:52:13 -0400 Received: from mail-pj1-x1043.google.com ([2607:f8b0:4864:20::1043]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1qjhVq-0000Qy-8S for emacs-orgmode@gnu.org; Fri, 22 Sep 2023 10:52:13 -0400 Received: by mail-pj1-x1043.google.com with SMTP id 98e67ed59e1d1-274972a4cb6so1613491a91.1 for ; Fri, 22 Sep 2023 07:52:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1695394327; x=1695999127; darn=gnu.org; h=mime-version:user-agent:message-id:date:subject:to:from:from:to:cc :subject:date:message-id:reply-to; bh=twcBiEK3wRsCh+TOGO4+39zgbzgMQsMd6WhEVBQO414=; b=L3+DtmOav02KhQVaK65jJE4btk1VYQhQ+J2P2420HNh0u0tI8INyVlYqZuKUURShqX IHjWfLePNBgydoZSF9+TZ/7pqUf9mq9z970cLeYoJOzPecjEuU1uoZ4eiSStjUBI8Kx5 9/cSJKHDuil7s4BY0N3Bkn4EQyG7iyKjntpXbpFyCIa4IKAsgokzEAJkylRkoE6feAw7 LI5XJW/Iix+fZj8L0nbuOcSpQDUZO7/zc7kYQEnMGGAe4bgAS35Uen5Hfa2cb3Rg/kXp y8SCd6WwCfFcf72a/Q32ZS5wyBTlOKdhEhnACSjD5w6pG1DNLC0saf2BvflI1mxO5qYU hRtA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1695394327; x=1695999127; h=mime-version:user-agent:message-id:date:subject:to:from :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=twcBiEK3wRsCh+TOGO4+39zgbzgMQsMd6WhEVBQO414=; b=dImLqWCcbmwF9eQKyKZ1CJYPo/pBp13JgUjmgO59hVDzrtcjve0En8Accr7i5x3C+B pfp8PGRWovk2CEMkct66ovHMFPUPlHIgGpEWGeL9pn0Li92ff/ZnkL35P3cfvHIVT7LC IzeGTJI+LFPZN2k6mLLelzFiWXtnK+W3IT+6OGgcZkYlqmS9r8eXA+mUOb+VYor2mb+B 2Y0eu0cFU41ItJ/e8JPCjfKzpTd/MbIfQ+YRJDa6T1zROmSKx4zHCFGtexYK+xbZO3ql YT7Ttjg60k6Ir3kYfUJAp4qGFwJ/3FnUOonvmayKZrwns4ef5wYCzt/WS4I9KCQKlAjj QT1Q== X-Gm-Message-State: AOJu0YyyhjzjuMXQYIMMqneICYDQIbTFc5Cf3yx+dHG1MQZMDavgIOcI 8SwViMPfeZ5SYTp5bx3KfJqc63gsWLYpPQ== X-Google-Smtp-Source: AGHT+IGblClmhxjjQ8G1LQ7doo42HdEYzJhREnFSE+ej8DhMGD1+knfKK/Ab50DKv3iyL6G/vct8Zg== X-Received: by 2002:a17:90b:1e03:b0:273:e073:1d02 with SMTP id pg3-20020a17090b1e0300b00273e0731d02mr8969064pjb.38.1695394327347; Fri, 22 Sep 2023 07:52:07 -0700 (PDT) Received: from localhost ([115.240.90.130]) by smtp.gmail.com with ESMTPSA id nn5-20020a17090b38c500b002749a99318csm3394061pjb.26.2023.09.22.07.52.05 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 22 Sep 2023 07:52:06 -0700 (PDT) From: Visuwesh To: emacs-orgmode@gnu.org Subject: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)] Date: Fri, 22 Sep 2023 20:22:03 +0530 Message-ID: <87jzsintv0.fsf@gmail.com> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Received-SPF: pass client-ip=2607:f8b0:4864:20::1043; envelope-from=visuweshm@gmail.com; helo=mail-pj1-x1043.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-orgmode@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org Sender: emacs-orgmode-bounces+larch=yhetil.org@gnu.org X-Migadu-Flow: FLOW_IN X-Migadu-Country: US X-Migadu-Spam-Score: -9.52 X-Migadu-Scanner: mx2.migadu.com X-Migadu-Queue-Id: 3508655262 X-Spam-Score: -9.52 X-TUID: PdPXj4yLYKVM --=-=-= Content-Type: text/plain 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. --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-Add-support-for-yank-media-and-DND.patch >From b6f1315cbdc331f2f54e1801b03272915d344cfd Mon Sep 17 00:00:00 2001 From: Visuwesh 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 --=-=-=--