Emacs 折腾小记(二) 中所说,利用 org-download + irfanview 在 windows 下实现截图并插入 Org 笔记是十分顺手的,但在使用过程中发现了几个问题:

  1. 它在截图的时候没办法隐藏当前的 Emacs 窗口
  2. 图片保存的位置只与当前笔记的 headline 有关

按我个人的需求来说,想实现以下的效果:

  1. 可以选择截图的时候是否隐藏当前 Emacs 窗口
  2. 如果选择隐藏,在截图时 Emacs 窗口自动最小化,并在截图完成后恢复
  3. 图片保存的位置加上 Org 文件的文件名层级,即最终的位置为(指定绝对位置 + Org 文件名目录 + headline 目录 + 图片名.png )。

这里选择通过 elisp 中的 add-advice 来实现:

首先定义一个函数包裹 org-download-screenshot 的无参调用形式:

1
2
3
4
5
(defun yuh/org-download-screenshot-wrapper()
  "Wrap org-download-screenshot for applying advices."
  (interactive)
  (require 'org-download)
  (call-interactively #'org-download-screenshot))

因为 org-download-sreenshot 的有参形式被 org-download-clipboard 调用,如果直接在上面添加 advice ,那么在粘贴剪贴板里的图片时 Emacs 也会最小化,这显然不是我们想要的。

然后定义两个钩子函数,一个在截图开始前执行,一个在截图完成后执行。这两个函数的代码目前很简单,因为需求只是完成窗口的最小化和还原。这里调用函数 w32-send-sys-command 来实现,这个函数是 Emacs 提供用来模拟 Windows 上窗口选项的,它接受一个整型参数,它是值是一个有效的 WM_SYSCOMMAND 消息。这里只要用到其中的最小化窗口消息( 0xf020 )和还原窗口消息( 0xf120 )。因此定义这两个钩子函数为:

1
2
3
4
5
6
(defun yuh/org-download-pre-screenshot ()
  "Minimize window before capture the screen."
  (w32-send-sys-command #xf020))
(defun yuh/org-download-after-screenshot ()
  "Minimize window before capture the screen."
  (w32-send-sys-command #xf120))

接着,将这两个钩子通过 add-advice 加入到之前定义好的无参调用截图函数 yuh/org-download-screenshot-wrapper 上去。因为我想能通过命令来控制是否在截图时隐藏窗口,所以这里写成一个交互式的开关:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(defun yuh/org-download-toggle-capture-self ()
  "Toggle self-capture in org-download-screeshot."
  (interactive)
  (if (advice-member-p 'yuh/org-download-pre-screenshot 'yuh/org-download-screenshot-wrapper)
      (progn
        (advice-remove 'yuh/org-download-screenshot-wrapper #'yuh/org-download-pre-screenshot)
        (advice-remove 'yuh/org-download-screenshot-wrapper #'yuh/org-download-after-screenshot)
        (message "Self-Hide Screenshot Removed"))
    (progn
      (advice-add 'yuh/org-download-screenshot-wrapper :before #'yuh/org-download-pre-screenshot)
      (advice-add 'yuh/org-download-screenshot-wrapper :after #'yuh/org-download-after-screenshot)
      (message "Self-Hide Screenshot Toggled"))))

这样需求1和2大致就完成了。第3个需求实现起来比较简单,就是定义一个钩子函数在图片保存前将保存的地址前缀修改为(绝对地址/当前 Org 文件名/),地址剩下的部分 org-download 会自动完成,函数定义如下:

1
2
3
4
5
(defun yuh/org-download-set-image-dir (&optional basename)
  "Set image dir with base-dir + buffername."
  (setq org-download-image-dir
        (concat "C:/Users/niall/OneDrive/note/org/images/"
                (file-name-sans-extension (buffer-name)))))

最后,只要在 use-package 中将上面所有的逻辑集成进 org-download 里就行,这里默认将截图时隐藏 Emacs 窗口开启:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(use-package org-download
  :ensure t
  :after org
  :bind
  ("<f7>" . org-download-clipboard)
  ("<f8>" . yuh/org-download-screenshot-wrapper)
  :config
  (advice-add 'org-download-image :before #'yuh/org-download-set-image-dir)
  (setq org-download-screenshot-method "irfanview /capture=4 /convert=\"%s\"")
  (yuh/org-download-toggle-capture-self)
  (setq org-download-screenshot-file
        "c:\\Users\\niall\\AppData\\Local\\Temp\\screenshot.png")) ;; irfanview do not accept / path

最终的效果展示如下

截图隐藏 Emacs 窗口:

使用开关设置不隐藏:

注意到上面图片的保存路径中均加上了 Org 文件名的一级。