Буфер обмена#
В разделе кратко описан буфер обмена как системный механизм операционных систем, а также особенности вставки данных из буфера обмена в системе Global ERP.
Общее описание буфера обмена#
Буфер обмена — это системный механизм передачи данных между приложениями. Он используется при копировании и вставке и позволяет передавать не только текст, но и изображения, файлы и структурированные данные.
Где хранится буфер обмена#
В Windows буфер обмена представляет собой централизованное хранилище в оперативной памяти системы. Данные копируются в буфер и сохраняются независимо от приложения-источника. При вставке (например, по
Ctrl+V) активное приложение запрашивает данные из буфера в нужном формате.В Linux (
X11иWayland) буфер обмена работает как механизм передачи данных между приложениями. Фактически данные хранит приложение-владелец, а не сама система. При вставке активное приложение запрашивает данные у владельца буфера, и тот передает их по запросу. Если приложение-владелец закрывается, данные из буфера могут быть потеряны.
В каком виде хранятся данные#
В Windows данные хранятся как набор форматов CF_*. Например:
CF_UNICODETEXT— текст;CF_BITMAP— изображение;CF_HDROP— список файлов.
Одни и те же данные могут одновременно присутствовать в нескольких форматах.
В Linux для представления данных используются MIME-типы. Наиболее распространенные варианты:
text/plain;text/html;image/png.
Как приложение получает данные#
Данные из буфера обмена не передаются автоматически. Приложение получает их в момент вставки, например при нажатии Ctrl+V.
Процесс выглядит следующим образом:
Пользователь инициирует вставку.
Активное приложение запрашивает данные из буфера обмена в одном из доступных форматов.
Операционная система (или приложение-владелец буфера в Linux) возвращает данные.
Приложение получает данные в виде:
строки (например, текст или TSV);
бинарных данных (например, изображение);
списка файлов (если были скопированы файлы).
В системе Global ERP полученные данные далее передаются на сервер и приводятся к файлу event.file, с которым работает прикладная логика.
Вставка данных из буфера обмена в системе#
В системе реализован механизм вставки данных из буфера обмена в таблицы и выборки. Он может использоваться для вставки как обычных данных, например текста из Excel, так и файлов, если они передаются через буфер обмена.
Пользователь копирует данные стандартным способом, например через Ctrl+C. При вставке (Ctrl+V или команда «Вставить») активное приложение инициирует получение данных из буфера обмена.
На стороне пользователя в этот момент происходит событие вставки, в рамках которого:
Приложение запрашивает данные из буфера обмена в одном из поддерживаемых форматов.
Полученные данные (или файлы) передаются на сервер.
На сервере они преобразуются во временный файл (
event.file).После этого вызывается метод
onClipboardPaste, в который передается объект событияPasteEvent.
Таким образом, извлечение данных из буфера обмена происходит на стороне пользователя в момент вставки, после чего данные передаются на сервер и преобразуются в файл для дальнейшей обработки.
Дальнейшая обработка выполняется не напрямую с содержимым буфера обмена, а с файлом, который был сформирован на сервере на основе переданных данных или файлов из буфера.
Для корректной работы вставка должна выполняться в контексте редактируемой ячейки или выделенного фрейма, в котором доступна вставка. Если такой контекст отсутствует, вставка не произойдет и обработчик onClipboardPaste вызван не будет.
Если пользователь копирует диапазон ячеек, например из Excel, данные передаются как табличный набор строк и колонок. При дальнейшей обработке система разбирает их построчно и поколоночно, используя символы перевода строки и табуляции как разделители.
Если среди ячеек, в которые выполняется вставка, есть нередактируемые, сервер прерывает операцию и выводит сообщение об ошибке: «Невозможно вставить значения: среди выделенных ячеек есть нередактируемые».
Поддерживаемые форматы вставки#
При настройке выборки нужно указать, какие форматы данных система может принимать. Чаще всего используется обычный текст.
Поддерживаются следующие типы:
text/plain (Plain Text)— обычный текст без форматирования;application/xmlиtext/xml— XML-документы;text/html— HTML-документы.
Настройка выборки#
Поддержка вставки данных из буфера обмена настраивается на этапе разработки компонента выборки. Чтобы система корректно принимала данные, необходимо в методе onLoadMeta указать форматы (MIME-типы), которые данная выборка способна обработать.
Система проверяет список selection.clipboard.pasteMimeTypes при инициализации выборки и использует его для фильтрации данных при последующих операциях вставки.
override def onLoadMeta(): Unit = {
super.onLoadMeta()
// Указываем поддерживаемые форматы буфера обмена
// Приоритет обработки: слева направо
selection.clipboard.pasteMimeTypes = Seq(MimeType.textPlain)
}
Обработка вставки на примере Btk_Variant#
Копирование и вставка в данном механизме выполняются как два отдельных этапа:
при копировании источник помещает данные или файлы в буфер обмена;
при вставке система получает содержимое буфера обмена и запускает его обработку.
Основная логика вставки запускается в методе onClipboardPaste. Это обработчик события вставки данных из буфера обмена, который доступен в AVI и может быть переопределен в логике выборки.
Объект event: PasteEvent содержит:
event.file— файл, в который на сервере преобразовано содержимое буфера обмена;event.type— тип данных, то есть MIME-type.
При вставке обработка выполняется в таком порядке:
Проверяется наличие файла:
event.file != null.Из файла считываются все строки.
Пустые строки отфильтровываются.
Если данные есть, запускается дальнейшая логика загрузки.
override def onClipboardPaste(event: PasteEvent): Unit = {
dialogs.withInfoForm("Идет загрузка объектов") {
if (event.file != null) {
val values = Files.readAllLines(event.file.toPath).asScala
val convertedValues = values.filter(s => s.ns.isNotNullOrEmpty).toSeq
if (convertedValues.nonEmpty) {
if (selection.selectedRecordsCount() === 1 && !convertedValues.head.contains('\t')) {
super.onClipboardPaste(event)
} else {
Btk_VariantObjLib().loadFromClipboard(convertedValues, getMasterId)
}
}
}
}
selection.refresh()
}
Дальнейшая обработка вставленных данных#
Метод loadFromClipboard определяет, нужно ли создавать новые записи или обновлять существующие.
Если выделенных записей нет или данные не с чем сопоставить, система проходит по всем строкам, полученным при вставке из буфера обмена, и создает новые объекты.
Если в выборке есть выделенные записи, система обновляет существующие объекты значениями из соответствующих строк.
Метод setLineFromClipboardDataByRop разбирает одну строку данных в формате TSV (Tab Separated Values) и распределяет значения по атрибутам.
Алгоритм работы:
Получает список характеристик варианта, отсортированный по
nOrder.Разбивает строку по символу табуляции
\t.Проходит по колонкам.
Для каждой колонки находит соответствующий атрибут по индексу.
Если значение не пустое, устанавливает его через API.
При установке значения учитывается тип данных:
для даты при типе
idDateзначение преобразуется заменой/на.с последующей конвертацией в дату;для мультисправочника и интервала значение разбивается по
;.
Для оптимизации производительности коммит сессии может выполняться пакетно.
def setLineFromClipboardDataByRop(line: String, idpVariant: NLong, ropVariantObj: Btk_VariantObjApi#ApiRop): Unit = {
val columns = line.split("\t")
for (col <- columns.indices) {
val cellValue = columns(col).trim
if (col < sortedListUC.length) {
val attr = sortedListUC(col)
val idUniversalCharacteristic = attr.get(_.idUniversalCharacteristic)
if (cellValue.isNotNullOrEmpty) {
val ucSettings = Btk_UniversalCharacteristicApi().getUniCharSetting(idUniversalCharacteristic)
val valueToSet =
if (ucSettings.idDbType === Btk_UniCharDataTypeApi().idDate) {
cellValue.replace("/", ".").toDate
} else if (ucSettings.bMultiValue || ucSettings.bInterval) {
cellValue.split(";")
} else {
cellValue
}
Btk_VariantObjApi().setUniCharValue(ropVariantObj, idUniversalCharacteristic, valueToSet)
}
}
}
}
Примеры вставки#
Пример 1. Вставка диапазона из Excel
A1 B1 C1
A2 B2 C2
Передается:
A1\tB1\tC1
A2\tB2\tC2
Система разбивает данные на строки и колонки и сопоставляет их с атрибутами.
Пример 2. Вставка одного значения
TestValue
Если выбрана одна запись и нет табуляции, используется стандартная вставка (super.onClipboardPaste).
Пример 3. Преобразование типов
01/12/2025 value1;value2
Система:
преобразует дату;
разбивает значения;
сохраняет в атрибуты.