拖放传输的数据通常为纯文本或标识文件及其他资源的 URL 列表。文本被拖放时会插入到释放位置,若无法插入则保存至删除环。
被拖放的 URL 会交由变量 dnd-protocol-alist 中对应的 DND 处理函数(DND handler functions),或由变量 browse-url-handlers 与 browse-url-default-handlers 设置的 “URL 处理器(URL handlers)” 处理;若无匹配处理器,则按纯文本插入缓冲区。
该变量是一个关联列表,键为匹配 URL 的正则表达式,值为拖放匹配 URL 时调用的 DND 处理函数。
若处理函数为符号,且其 dnd-multiple-handler 属性(see 符号属性)已设置,则拖放时会将所有匹配其正则表达式的 URL 以列表形式传入;若无此属性,则对每个 URL 单独调用一次。第一个参数之后会传入标识操作类型的符号:copy、move、link、private 或 ask。
若 action 为 private,发起拖放的程序不要求接收方执行特定行为;此时合理的操作是打开 URL 或将其内容复制到当前缓冲区。action 的其他取值含义与 dnd-begin-file-drag 的 action 参数基本一致。
处理函数完成工作后必须返回一个符号,标识实际执行的操作:可以是传入的操作,或 private(告知拖放源未执行其指定操作)。
若多个处理器匹配拖放中的重叠项目子集,匹配项目最多的处理器会被调用处理该子集,且这些项目不会再交由其他处理器处理。
Emacs 不支持接收文本与 URL 之外的数据,因为实现该功能的各窗口系统接口差异过大,无法统一抽象。同时,部分拖放协议不允许接收方控制操作行为,因此 DND 处理器也无法干预预期行为。X11 拖放实现基于多个底层协议,这些协议使用选区传输且共性较强,以下函数与变量提供了对其底层访问能力:
该函数用于判断 Emacs 是否接收此次拖放。调用时传入三个参数:
move、copy、link 或 ask,含义与 x-begin-drag 中一致。
该函数必须返回 nil 以拒绝拖放,或返回一个 cons,其中包含将要执行的操作(如传递给 DND 处理器)与请求的选区数据类型。cons 中返回的操作也可以是符号 private,表示尚未确定具体行为。
通常无需修改 x-dnd-test-function,因其默认的拖放接收规则可通过修改此选区数据类型列表调整。列表中每个元素为字符串,若默认 “测试函数” 在数据类型列表中找到对应符号名,则会接收此次拖放。
仅向此列表添加新项并无实际意义,除非同时向 x-dnd-types-alist 添加对应的处理函数。
该变量是一个关联列表,键为标识选区数据类型的字符串,值为对应类型数据被拖放时调用的函数。
每个函数接收三个参数:第一个为拖放位置下方的窗口或框架,与 x-dnd-test-function 一致;第二个为将要执行的操作,可以是测试函数返回的任意操作;第三个为选区数据本身(see 选择区访问)。
X11 拖放协议提供的选区数据类型有时与 ICCCM 及兼容剪贴板或主选区所有者提供的类型不同。通常会使用 MIME 类型名称(如 "text/plain;charset=utf-8,其中 “utf-8” 大小写可能不一致)替代标准 X 选区名称(如 UTF8_STRING)。
X 直接保存(XDS)协议允许程序将拖放文件的命名职责委托给接收方。发生此类拖放时,DND 处理器与前述 X 专用接口基本会被绕过,由另一函数响应拖放。
该变量应设置为一个函数,用于以两步流程注册并命名通过 XDS 协议拖放的文件。函数接收两个参数:need-name 与 filename。
nil,第二个参数 filename 为待保存文件的基本名称。函数应返回用于保存文件的完整扩展绝对路径。例如,若文件被拖至 Dired 窗口,默认目录应为拖放位置显示文件所在目录。若因某种原因无法保存文件,函数应返回 nil 以取消拖放操作。
nil,第二个参数 filename 为已保存文件的完整绝对路径。函数应执行文件保存后的必要操作,例如 Dired 应刷新显示目录以显示新文件。
该变量的默认值为 x-dnd-save-direct。
当 need-name 参数为非 nil 时,该函数会提示用户输入保存文件的绝对路径。若指定文件已存在,会额外询问是否覆盖,并仅在用户确认后返回绝对路径。
当 need-name 参数为 nil 时,若当前缓冲区为 Dired 模式或其子模式,则刷新 Dired 列表;否则调用 find-file 访问该文件(see 访问文件的函数)。
该函数功能与 x-dnd-save-direct 类似,但当 need-name 为非 nil 时,不会提示用户输入文件完整名称,而是直接返回相对于当前缓冲区默认目录扩展后的 filename 路径(see 文件名展开相关函数)。(若默认目录下存在同名文件,仍会请求确认。)
若当前窗口系统支持,也可将内容从 Emacs 拖放至其他程序。实现该功能的函数如下:
该函数从 frame 发起至其他程序(称为 拖放目标(drop target))的拖放操作,并在 text 被释放或操作取消后返回。
action 必须为符号 copy 或 move:copy 表示拖放目标应插入文本;move 含义与 copy 相同,但调用方需按下文说明从源位置删除文本。
frame 为当前鼠标按下所在的框架,若为 nil 则使用选中框架。由于未按下鼠标按键时该函数可能立即返回,因此仅应在响应 down-mouse-1 或类似事件时调用(see 鼠标事件),并将 frame 设置为事件生成所在框架(see 点击事件)。
若 allow-same-frame 为 nil,则忽略在 frame 自身上的释放操作。
返回值表示拖放目标实际执行的操作,也即调用方应执行的后续操作,为下列符号之一:
copy拖放目标已插入拖放文本。
move拖放目标已插入拖放文本,调用方应从源缓冲区删除对应 text(若适用)。
private拖放目标执行了其他未指定操作。
nil拖放操作已取消。
该函数从 frame 发起至其他程序(称为 拖放目标(drop target))的文件拖放操作,并在 file 被释放或操作取消后返回。
若 file 为远程文件,会创建本地临时副本。
action 必须为符号 copy、move 或 link:copy 表示拖放目标应打开或复制文件;move 表示拖放目标应将文件移至其他位置;link 表示拖放目标应为 file 创建符号链接。若 file 为远程文件,指定 link 为操作类型会报错。
frame 与 allow-same-frame 的含义与 dnd-begin-text-drag 一致。
返回值为拖放目标实际执行的操作,为下列符号之一:
copy拖放目标已打开文件或将其复制至其他位置。
move拖放目标已将文件移至其他位置。
link拖放目标(通常为文件管理器)已为文件创建符号链接。
private拖放目标执行了其他未指定操作。
nil拖放操作已取消。
该函数与 dnd-begin-file-drag 类似,区别在于 files 为文件列表。若拖放目标不支持多文件拖放,则仅使用第一个文件。
该函数行为与 dnd-begin-file-drag(使用默认操作 copy 时)类似,区别在于它接受一个用于保存副本的目标名称。
上述高级接口基于底层原语实现。底层接口 x-begin-drag 也可用于拖放文本与文件之外的内容,但要求调用方详细了解各平台程序支持的数据类型与操作。
该函数从 frame 开始拖放,并在拖放操作结束(成功释放或被拒绝)后返回。当所有鼠标按键在 frame 之外的 X 窗口(拖放目标(drop target))上方释放时发生拖放;若 allow-current-frame 为非 nil,则可在任意 X 窗口释放。若拖放开始时未按下任何鼠标按键,函数可能立即返回 nil。
targets 为字符串列表,表示拖放目标可从 Emacs 请求的选区目标,与 gui-get-selection 的 data-type 参数类似(see 窗口系统选择)。
action 为推荐给目标的操作符号,可以是 XdndActionCopy 或 XdndActionMove;两者均表示将 XdndSelection 选区内容复制到拖放目标,但后者还承诺在复制完成后删除选区内容。
action 也可以是一个关联列表,将可用操作符号与拖放目标展示给用户选择的字符串关联。
action 也可以是一个关联列表,将可用操作符号与拖放目标展示给用户选择的字符串关联。
若 return-frame 为非 nil,且鼠标离开 frame 后移至另一 Emacs 框架,则会立即返回该框架。若 return-frame 为符号 now,则会直接返回鼠标指针下方的任意框架,无需等待鼠标离开原框架。return-frame 适用于需要特殊处理框架间拖放同时向其他程序拖放的场景,但不保证在所有系统与窗口管理器下生效。
若 follow-tooltip 为非 nil,拖放过程中任意提示框(如 tooltip-show 显示的提示框)位置会跟随鼠标指针移动,所有鼠标按键释放后提示框会隐藏。
若拖放被拒绝或未找到目标,函数返回 nil。否则返回表示目标选择执行的操作符号,若目标不支持指定操作,返回值可能与 action 不同。除 XdndActionCopy 与 XdndActionMove 外,XdndActionPrivate 也是合法返回值,表示目标执行了未定义操作,调用方无需进一步处理。
调用方需配合目标完成所选操作。例如,若函数返回 XdndActionMove,应删除被拖放的缓冲区文本,其他拖放数据同理。
函数 x-begin-drag 在 “后台(behind the scenes)” 使用了多种拖放协议。当拖动的内容已知不被某种特定拖放协议支持时,可以通过修改下列变量的值来禁用该协议:
当该变量非 nil 时,Motif 拖放协议将被禁用,拖放到仅支持该协议的程序上将无法生效。
当该变量为 nil 时,OffiX(旧版 KDE)拖放协议将被禁用。当该变量为符号 files 时,仅当 "FILE_NAME" 是传递给 x-begin-drag 的目标之一时,才会使用 OffiX 协议。其他任何值均表示使用 OffiX 协议拖放所有受支持的内容。
当传递给 x-begin-drag 的目标列表中包含 "STRING"、"UTF8_STRING"、"COMPOUND_TEXT" 或 "TEXT" 中的任意一个时,如果拖放目标完全不支持任何拖放协议,Emacs 将尝试使用合成鼠标事件与主选区来插入文本。
副作用是,在执行此类拖放操作时,Emacs 会成为主选区的所有者。将该变量设为 nil 可禁用这种模拟行为。