30.24 Перетаскивание

Данные, передаваемые путем перетаскивания, обычно представляют собой либо обычный текст, либо список URL-адресов, указывающих на файлы или другие ресурсы. Когда текст перетаскивается, он вставляется в место сброса, с возможностью сохранения его в кольце убийств, если это невозможно.

Сброшенные URL-адреса передаются соответствующим функциям-обработчикам DND в переменной dnd-protocol-alist или, альтернативно, «обработчикам URL», как определено переменными browse-url-handlers и browse-url-default-handlers; при отсутствии подходящих обработчиков любого типа они рассматриваются как обычный текст и вставляются в буфер.

Переменная: dnd-protocol-alist

Эта переменная представляет собой ассоциативный список между регулярными выражениями, с которыми сопоставляются URL-адреса, и функциями-обработчиками DND, вызываемыми при сбросе соответствующих URL-адресов.

Если функция-обработчик является символом, у которого установлено свойство dnd-multiple-handler (см. Свойства символов), то при сбросе ей передается список всех URL-адресов, соответствующих ее регулярному выражению; при отсутствии этого свойства она вызывается по одному разу для каждого из этих URL-адресов. Следом за этим первым аргументом передается один из символов copy, move, link, private или ask, определяющий действие, которое следует выполнить.

Если action равно private, программа, инициировавшая сброс, не настаивает на каком-либо определенном поведении со стороны получателя; разумным действием в таком случае будет открыть URL-адрес или скопировать его содержимое в текущий буфер. Другие значения action подразумевают примерно то же, что и в аргументе action для dnd-begin-file-drag.

После завершения своей работы функция-обработчик должна вернуть символ, обозначающий выполненное действие: либо переданное ей действие, либо символ private, который сообщает источнику сброса, что предписанное им действие не было выполнено.

Когда несколько обработчиков соответствуют перекрывающемуся подмножеству элементов внутри одного сброса, вызывается обработчик, соответствующий наибольшему числу элементов, для открытия этого подмножества. Переданные ему элементы впоследствии исключаются из других обработчиков, даже если они также им соответствуют.

Emacs не предпринимает мер для приема данных, кроме текста и URL-адресов, поскольку интерфейсы оконных систем, обеспечивающие это, слишком сильно различаются, чтобы их можно было последовательно абстрагировать. Также обработчики DND не имеют влияния на действия, которые они должны выполнять, поскольку конкретные протоколы перетаскивания отказывают получателям в таком контроле. Реализация перетаскивания в X11 опирается на несколько базовых протоколов, которые используют передачу выделений и имеют много общего; низкоуровневый доступ к ним предоставляется через следующие функции и переменные:

Переменная: x-dnd-test-function

Эта функция вызывается для определения, должен ли Emacs принять сброс. Она вызывается с тремя аргументами:

  • Окно под перетаскиваемым элементом, то есть окно, чей буфер должен принять сброс. Если элемент находится над не оконным компонентом кадра (например, полосами прокрутки, панелями инструментов и тому подобным), вместо него передается сам кадр.
  • Один из символов move, copy, link или ask, представляющий действие с данными элемента, предлагаемое источником сброса. Эти символы несут те же значения, что и в x-begin-drag.
  • Вектор типов данных выделения (см. Выделения X), которые предоставляет элемент.

Эта функция должна вернуть nil для отклонения сброса или cons из действия, которое будет выполнено (например, через передачу функции-обработчику DND), и запрашиваемого типа данных выделения. Действие, возвращенное в этом cons, также может быть символом private, что означает, что выполняемое действие пока не определено.

Переменная: x-dnd-known-types

Изменять x-dnd-test-function обычно не требуется, поскольку ее набор критериев для принятия сброса по умолчанию можно настроить, изменяя этот список типов данных выделения. Каждый элемент является строкой, и если она обнаруживается как имя символа элемента в списке типов данных с помощью «тестовой функции» по умолчанию, это заставит эту функцию принять сброс.

Добавление новой записи в этот список бесполезно, если только соответствующая функция-обработчик не добавлена в x-dnd-types-alist.

Переменная: x-dnd-types-alist

Эта переменная представляет собой ассоциативный список между строками, обозначающими типы данных выделения, и функциями, которые вызываются при сбросе объектов таких типов.

Каждая такая функция вызывается с тремя аргументами; первый — это окно или кадр под местом сброса, как в x-dnd-test-function; второй — выполняемое действие, которое может быть любым из действий, возвращенных тестовыми функциями, и третий — сами данные выделения (см. Доступ к выделениям).

Типы данных выделения, предоставляемые протоколами перетаскивания X11, иногда отличаются от тех, что предоставляются ICCCM и соответствующими владельцами буфера обмена или первичного выделения. Часто имя MIME-типа, например "text/plain;charset=utf-8" (с несовпадающим написанием “utf-8”), заменяет стандартное имя выделения X, такое как UTF8_STRING.

Протокол X Direct Save (XDS) позволяет программам возложить ответственность за именование сбрасываемого файла на получателя. Когда происходит такой сброс, обработчики DND и вышеупомянутый X-специфичный интерфейс в значительной степени обходятся, поручая другой функции реагировать на сброс.

Переменная: x-dnd-direct-save-function

Эта переменная должна быть установлена в функцию, которая регистрирует и именует файлы, сбрасываемые с использованием протокола XDS, в два этапа. Ей передаются два аргумента: need-name и filename.

  1. Приложение, из которого перетаскивается файл, просит Emacs предоставить полное имя файла, под которым следует сохранить файл. Для этого функция прямого сохранения вызывается с первым аргументом need-name, отличным от nil, и вторым аргументом filename, установленным в базовое имя сохраняемого файла. Она должна вернуть полностью развернутое абсолютное имя файла, под которым следует сохранить файл. Например, если файл перетаскивается в окно Dired, естественным каталогом для файла является каталог файла, показанного в месте сброса. Если сохранение файла по какой-либо причине невозможно, функция должна вернуть nil, что отменит операцию перетаскивания.
  2. Приложение, из которого перетаскивается файл, сохраняет файл под именем, возвращенным первым вызовом функции прямого сохранения. Если ему удается сохранить файл, функция прямого сохранения вызывается снова, на этот раз с первым аргументом need-name, установленным в nil, и вторым аргументом filename, установленным в полное абсолютное имя сохраненного файла. Затем функция должна выполнить все необходимое с учетом того, что файл был сохранен. Например, Dired должен обновить отображаемый каталог, показав там новый файл.

Ее значение по умолчанию x-dnd-direct-save-function — это x-dnd-save-direct.

Функция: x-dnd-save-direct need-name filename

При вызове с аргументом need-name, отличным от nil, эта функция запрашивает у пользователя абсолютное имя файла, под которым его следует сохранить. Если указанный файл уже существует, она дополнительно спрашивает пользователя, следует ли перезаписать его, и возвращает абсолютное имя файла только в том случае, если пользователь подтверждает перезапись.

При вызове с аргументом need-name, равным nil, она обновляет список Dired, если текущий буфер находится в режиме Dired или одном из его производных, и в противном случае открывает файл вызовом find-file (см. Функции для посещения файлов).

Функция: x-dnd-save-direct-immediately need-name filename

Эта функция работает как x-dnd-save-direct, но при вызове с аргументом need-name, отличным от nil, она не запрашивает у пользователя полное имя сохраняемого файла; вместо этого она возвращает свой аргумент filename, развернутый относительно каталога по умолчанию текущего буфера (см. Функции, раскрывающие имена файлов). (Она все равно запрашивает подтверждение, если файл с таким именем уже существует в каталоге по умолчанию.)

Также возможно перетаскивание содержимого из Emacs в другие программы, если это поддерживается текущей оконной системой. Функции, обеспечивающие это, перечислены ниже:

Функция: dnd-begin-text-drag text &optional frame action allow-same-frame

Эта функция запускает операцию перетаскивания из frame в другую программу (называемую целью сброса) и возвращает управление, когда text сбрасывается или операция отменяется.

action должен быть одним из символов copy или move, где copy означает, что text должен быть вставлен целью сброса, а move означает то же, что и copy, но вызывающий код также должен удалить text из его источника, как объяснено в списке ниже.

frame — это кадр, в котором в данный момент зажата кнопка мыши, или nil, что означает использование выбранного кадра. Поскольку эта функция может немедленно вернуть управление, если ни одна кнопка мыши не зажата, она должна вызываться только в ответ на событие down-mouse-1 или аналогичное (см. События мыши), с frame, установленным в кадр, в котором было сгенерировано это событие (см. События щелчка).

Если allow-same-frame равен nil, сбросы на frame будут игнорироваться.

Возвращаемое значение отражает действие, которое цель сброса фактически выполнила, и, следовательно, какое действие, если вообще какое-либо, должен выполнить вызывающий код. Это один из следующих символов:

copy

Цель сброса вставила сброшенный текст.

move

Цель сброса вставила сброшенный текст, и вызывающий код должен удалить text из буфера, откуда он был извлечен, если это применимо.

private

Цель сброса выполнила некоторое другое неопределенное действие.

nil

Операция перетаскивания была отменена.

Функция: dnd-begin-file-drag file &optional frame action allow-same-frame

Эта функция запускает операцию перетаскивания из frame в другую программу (называемую целью сброса) и возвращает управление, когда file сбрасывается или операция отменяется.

Если file является удаленным файлом, будет создана временная локальная копия.

action должен быть одним из символов copy, move или link, где copy означает, что file должен быть открыт или скопирован целью сброса, move означает, что цель сброса должна переместить файл в другое место, а link означает, что цель сброса должна создать символическую ссылку на file. Указывать link в качестве действия ошибочно, если file является удаленным файлом.

frame и allow-same-frame означают то же, что и при вызовах dnd-begin-text-drag.

Возвращаемое значение — это действие, которое цель сброса фактически выполнила, и является одним из следующих символов:

copy

Цель сброса открыла или скопировала file в другое место.

move

Цель сброса переместила file в другое место.

link

Цель сброса (обычно файловый менеджер) создала символическую ссылку на file.

private

Цель сброса выполнила некоторое другое неопределенное действие.

nil

Операция перетаскивания была отменена.

Функция: dnd-begin-drag-files files &optional frame action allow-same-frame

Эта функция подобна dnd-begin-file-drag, за исключением того, что files является списком файлов. Если цель сброса не поддерживает сброс нескольких файлов, вместо этого будет использован первый файл.

Функция: dnd-direct-save file name &optional frame allow-same-frame

Поведение этой функции подобно dnd-begin-file-drag (при использовании действия по умолчанию copy), за исключением того, что она принимает имя, под которым должна быть сохранена копия.

Высокоуровневые интерфейсы, описанные выше, реализованы поверх низкоуровневого примитива. Низкоуровневый интерфейс x-begin-drag также доступен для перетаскивания содержимого, отличного от текста и файлов. Он требует детального знания типов данных и действий, поддерживаемых программами на каждой платформе, которую вызывающие коды хотят поддерживать.

Функция: x-begin-drag targets &optional action frame return-frame allow-current-frame follow-tooltip

Эта функция начинает перетаскивание из frame и возвращает управление, когда операция перетаскивания завершается, либо потому, что сброс был успешным, либо потому, что сброс был отклонен. Сброс происходит, когда все кнопки мыши отпущены поверх X-окна, отличного от frame (цель сброса), или любого X-окна, если allow-current-frame не равен nil. Если при начале операции перетаскивания ни одна кнопка мыши не зажата, эта функция может немедленно вернуть nil.

targets — это список строк, представляющих цели выделения, подобно аргументу data-type для gui-get-selection, которые цель сброса может запросить у Emacs (см. Выделения оконной системы).

action — это символ, обозначающий действие, рекомендуемое цели. Это может быть либо XdndActionCopy, либо XdndActionMove; оба подразумевают копирование содержимого выделения XdndSelection в цель сброса, но второе, кроме того, передает обещание удалить содержимое выделения после копирования.

action также может быть ассоциативным списком, сопоставляющим символы, представляющие доступные действия, со строками, которые цель сброса представляет пользователю для выбора между этими действиями.

Если return-frame не равен nil и мышь перемещается над кадром Emacs после того, как сначала покинула frame, то кадр, на который перемещается мышь, будет возвращен немедленно. Если return-frame является символом now, то любой кадр под указателем мыши будет возвращен без ожидания, пока мышь сначала покинет frame. return-frame полезен, когда вы хотите обрабатывать перетаскивание содержимого из одного кадра в другой особым образом, а также перетаскивать содержимое в другие программы, но его работа не гарантируется во всех системах и со всеми оконными менеджерами.

Если follow-tooltip не равен nil, положение любой всплывающей подсказки (например, отображаемой tooltip-show) будет следовать за положением указателя мыши во время операции перетаскивания. Подсказка будет скрыта, как только все кнопки мыши будут отпущены.

Если сброс был отклонен или цель сброса не найдена, эта функция возвращает nil. В противном случае она возвращает символ, представляющий действие, которое цель решила выполнить, которое может отличаться от action, если оно не поддерживается целью сброса. XdndActionPrivate также является допустимым возвращаемым значением в дополнение к XdndActionCopy и XdndActionMove; это означает, что цель сброса выбрала неопределенное действие, и никаких дальнейших действий от вызывающего кода не требуется.

Вызывающий код должен сотрудничать с целью для завершения действия, выбранного целью. Например, вызывающие коды должны удалять любой текст буфера, который был перетащен, если эта функция возвращает XdndActionMove, и аналогично для других перетаскиваемых данных, где применимы сопоставимые критерии.

Функция x-begin-drag использует несколько протоколов перетаскивания «за кулисами». При перетаскивании содержимого, которое, как известно, не поддерживается конкретным протоколом перетаскивания, этот протокол может быть отключен путем изменения значений следующих переменных:

Переменная: x-dnd-disable-motif-protocol

Когда это значение не равно nil, протоколы перетаскивания Motif отключены, и сброс на программы, которые понимают только их, не будет работать.

Переменная: x-dnd-use-offix-drop

Когда это значение равно nil, протокол перетаскивания OffiX (старый KDE) отключен. Когда это значение является символом files, протокол OffiX будет использоваться только в том случае, если "FILE_NAME" является одной из целей, переданных в x-begin-drag. Любое другое значение означает использование протокола OffiX для сброса всего поддерживаемого содержимого.

Переменная: x-dnd-use-unsupported-drop

Когда одна из целей "STRING", "UTF8_STRING", "COMPOUND_TEXT" или "TEXT" присутствует в списке, переданном в x-begin-drag, Emacs попытается использовать синтезированные события мыши и первичное выделение для вставки текста, если цель сброса вообще не поддерживает ни один протокол перетаскивания.

Побочным эффектом является то, что Emacs станет владельцем первичного выделения при таком сбросе. Такую эмуляцию можно отключить, установив эту переменную в nil.