Enhance document editing capabilities by adding context menu options for copying, cutting, and pasting rows and cells in DocumentEditWidget. Refactor related methods for improved clipboard handling and data management. Introduce new helper functions for better code organization.
This commit is contained in:
@@ -11,3 +11,5 @@ IDI_ICON1 ICON DISCARDABLE "assets/icon2.ico"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -297,20 +297,6 @@ void SpecificationPCBController::generateTableFromComponents()
|
||||
QList<ComponentModel*> componentsInGroup = it.value();
|
||||
|
||||
if (componentsInGroup.size() > 1) {
|
||||
// Если компонентов больше одного, добавляем заголовок типа
|
||||
SpecificationPCBRowData headerRow;
|
||||
headerRow.isHeader = true;
|
||||
headerRow.format = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true);
|
||||
headerRow.zone = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true);
|
||||
headerRow.position = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true);
|
||||
headerRow.designation = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true);
|
||||
// Заголовок с componentType не должен подчеркиваться
|
||||
headerRow.name = SpecificationPCBCellData(getComponentType(componentsInGroup.first()), 0, 1, true, false, false, 100, false);
|
||||
headerRow.quantity = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true);
|
||||
headerRow.note = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true);
|
||||
m_tableModel->addEmptyRow();
|
||||
m_tableModel->addRow(headerRow);
|
||||
m_tableModel->addEmptyRow();
|
||||
// Сначала группируем компоненты по наименованию
|
||||
QList<QPair<QString, QList<ComponentModel*>>> groupedByName;
|
||||
for (ComponentModel *component : componentsInGroup) {
|
||||
@@ -450,11 +436,51 @@ void SpecificationPCBController::generateTableFromComponents()
|
||||
}
|
||||
}
|
||||
|
||||
// Разбиваем длинные группы десигнаторов (11 символов на строку)
|
||||
QStringList designatorsSplit = splitDesignatorGroups(designatorGroupsForThisName, 11);
|
||||
// Объединяем все группы дезигнаторов в одну строку через запятые
|
||||
QString allDesignators = designatorGroupsForThisName.join(", ");
|
||||
|
||||
// Определяем максимальное количество строк для названия и десигнаторов
|
||||
int maxRows = qMax(designationParts.size(), designatorsSplit.size());
|
||||
// Разбиваем длинные группы десигнаторов используя логику комплексных строк (11 символов на строку)
|
||||
QList<QString> designatorsSplitList = splitComplexStringByMiddle(allDesignators, 11);
|
||||
QStringList designatorsSplit;
|
||||
for (const QString &str : designatorsSplitList) {
|
||||
designatorsSplit.append(str);
|
||||
}
|
||||
|
||||
// Получаем тип компонента: если количество > 1, используем множественное число, иначе единственное
|
||||
QString componentType;
|
||||
if (totalQuantity > 1) {
|
||||
componentType = getComponentType(firstComponent);
|
||||
} else {
|
||||
componentType = getComponentTypeSingular(firstComponent);
|
||||
}
|
||||
|
||||
// Формируем список строк названия с учетом типа компонента
|
||||
QList<QString> nameParts;
|
||||
if (!componentType.isEmpty() && !designationParts.isEmpty()) {
|
||||
// Проверяем, помещается ли тип компонента + первая часть названия
|
||||
QString firstPartWithType = componentType + " " + designationParts[0];
|
||||
if (firstPartWithType.length() <= 34) {
|
||||
// Помещается - добавляем тип в первую строку
|
||||
nameParts.append(firstPartWithType);
|
||||
// Добавляем остальные части
|
||||
for (int i = 1; i < designationParts.size(); ++i) {
|
||||
nameParts.append(designationParts[i]);
|
||||
}
|
||||
} else {
|
||||
// Не помещается - выносим тип в отдельную строку
|
||||
nameParts.append(componentType);
|
||||
// Добавляем все части названия
|
||||
for (const QString &part : designationParts) {
|
||||
nameParts.append(part);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Типа нет или нет частей названия - используем как есть
|
||||
nameParts = designationParts;
|
||||
}
|
||||
|
||||
// Определяем максимальное количество строк для названия и дезигнаторов
|
||||
int maxRows = qMax(nameParts.size(), designatorsSplit.size());
|
||||
|
||||
for (int rowIndex = 0; rowIndex < maxRows; ++rowIndex) {
|
||||
SpecificationPCBRowData row;
|
||||
@@ -469,9 +495,9 @@ void SpecificationPCBController::generateTableFromComponents()
|
||||
row.quantity = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false);
|
||||
}
|
||||
|
||||
// Заполняем название (если есть)
|
||||
if (rowIndex < designationParts.size()) {
|
||||
row.name = SpecificationPCBCellData(designationParts[rowIndex], 0, 1, false, false, false, 100, false);
|
||||
// Заполняем название
|
||||
if (rowIndex < nameParts.size()) {
|
||||
row.name = SpecificationPCBCellData(nameParts[rowIndex], 0, 1, false, false, false, 100, false);
|
||||
} else {
|
||||
row.name = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false);
|
||||
}
|
||||
@@ -496,23 +522,63 @@ void SpecificationPCBController::generateTableFromComponents()
|
||||
ComponentModel *component = componentsInGroup.first();
|
||||
QString currentDesignation = getComponentValue(component, "ManufacturerPartNumber");
|
||||
|
||||
// Получаем тип компонента в единственном числе (количество = 1)
|
||||
QString componentType = getComponentTypeSingular(component);
|
||||
|
||||
// Разделяем длинное наименование на несколько строк
|
||||
QMap<QString, QString> props = component->properties();
|
||||
QString originalString = props.value("ManufacturerPartNumber", "");
|
||||
QList<QString> designationParts = splitLongDesignation(currentDesignation, originalString, component);
|
||||
|
||||
for (int partIndex = 0; partIndex < designationParts.size(); ++partIndex) {
|
||||
// Формируем список строк названия с учетом типа компонента
|
||||
QList<QString> nameParts;
|
||||
if (!componentType.isEmpty() && !designationParts.isEmpty()) {
|
||||
// Проверяем, помещается ли тип компонента + первая часть названия
|
||||
QString firstPartWithType = componentType + " " + designationParts[0];
|
||||
if (firstPartWithType.length() <= 34) {
|
||||
// Помещается - добавляем тип в первую строку
|
||||
nameParts.append(firstPartWithType);
|
||||
// Добавляем остальные части
|
||||
for (int i = 1; i < designationParts.size(); ++i) {
|
||||
nameParts.append(designationParts[i]);
|
||||
}
|
||||
} else {
|
||||
// Не помещается - выносим тип в отдельную строку
|
||||
nameParts.append(componentType);
|
||||
// Добавляем все части названия
|
||||
for (const QString &part : designationParts) {
|
||||
nameParts.append(part);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Типа нет или нет частей названия - используем как есть
|
||||
nameParts = designationParts;
|
||||
}
|
||||
|
||||
// Обрабатываем дезигнатор: если он длинный, разбиваем используя логику комплексных строк
|
||||
QString designatorStr = component->designator();
|
||||
QList<QString> designatorParts = splitComplexStringByMiddle(designatorStr, 11);
|
||||
|
||||
// Определяем максимальное количество строк для названия и дезигнатора
|
||||
int maxParts = qMax(nameParts.size(), designatorParts.size());
|
||||
|
||||
for (int partIndex = 0; partIndex < maxParts; ++partIndex) {
|
||||
SpecificationPCBRowData row;
|
||||
|
||||
// Только в первой строке показываем позицию и количество
|
||||
if (partIndex == 0) {
|
||||
row.position = SpecificationPCBCellData(QString::number(pos1++), 0, 1, false, false, false, 100, false);
|
||||
row.quantity = SpecificationPCBCellData("1", 0, 1, false, false, false, 100, false);
|
||||
row.note = SpecificationPCBCellData(component->designator(), 0, 1, false, false, false, 100, false);
|
||||
} else {
|
||||
// В последующих строках оставляем пустыми позицию и количество
|
||||
row.position = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false);
|
||||
row.quantity = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false);
|
||||
}
|
||||
|
||||
// Заполняем дезигнатор (если есть)
|
||||
if (partIndex < designatorParts.size()) {
|
||||
row.note = SpecificationPCBCellData(designatorParts[partIndex], 0, 1, false, false, false, 100, false);
|
||||
} else {
|
||||
row.note = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false);
|
||||
}
|
||||
|
||||
@@ -520,7 +586,13 @@ void SpecificationPCBController::generateTableFromComponents()
|
||||
row.format = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false);
|
||||
row.zone = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false);
|
||||
row.designation = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false);
|
||||
row.name = SpecificationPCBCellData(designationParts[partIndex], 0, 1, false, false, false, 100, false);
|
||||
|
||||
// Заполняем название
|
||||
if (partIndex < nameParts.size()) {
|
||||
row.name = SpecificationPCBCellData(nameParts[partIndex], 0, 1, false, false, false, 100, false);
|
||||
} else {
|
||||
row.name = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false);
|
||||
}
|
||||
|
||||
m_tableModel->addRow(row);
|
||||
}
|
||||
@@ -973,6 +1045,29 @@ QString SpecificationPCBController::getComponentType(ComponentModel *component)
|
||||
return component->designator();
|
||||
}
|
||||
|
||||
QString SpecificationPCBController::getComponentTypeSingular(ComponentModel *component)
|
||||
{
|
||||
if (!component) return "";
|
||||
|
||||
// Получаем тип компонента из маппинга дезигнаторов в единственном числе
|
||||
if (m_designatorMappingModel) {
|
||||
QString componentType = m_designatorMappingModel->getComponentType(component->designator(), false); // false для единственного числа
|
||||
if (!componentType.isEmpty()) {
|
||||
return componentType;
|
||||
}
|
||||
}
|
||||
|
||||
// Если маппинг не найден, используем тип из свойств компонента
|
||||
QMap<QString, QString> props = component->properties();
|
||||
QString type = props.value("Type", "");
|
||||
if (!type.isEmpty()) {
|
||||
return type;
|
||||
}
|
||||
|
||||
// Если и тип не найден, возвращаем пустую строку
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString SpecificationPCBController::getComponentFormat(ComponentModel *component)
|
||||
{
|
||||
if (!component) return "";
|
||||
|
||||
@@ -83,6 +83,7 @@ private:
|
||||
|
||||
// Методы для группировки
|
||||
QString getComponentType(ComponentModel *component);
|
||||
QString getComponentTypeSingular(ComponentModel *component);
|
||||
|
||||
// Метод для разделения десигнатора на буквенную и цифровую части
|
||||
QPair<QString, QString> splitDesignator(const QString &designator);
|
||||
|
||||
@@ -23,6 +23,9 @@ Q_DECLARE_METATYPE(VedomostCellData)
|
||||
#include <QMenu>
|
||||
#include <QList>
|
||||
#include <algorithm>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QMimeData>
|
||||
|
||||
// DocumentProcessor
|
||||
DocumentProcessor::DocumentProcessor(QObject *parent)
|
||||
@@ -1617,6 +1620,41 @@ void DocumentEditWidget::onContextMenuRequested(const QPoint &pos)
|
||||
|
||||
contextMenu.addSeparator();
|
||||
|
||||
// Меню для работы со строками
|
||||
QMenu *rowsMenu = contextMenu.addMenu("Строки");
|
||||
QAction *copyRowsAction = rowsMenu->addAction("Копировать строки");
|
||||
copyRowsAction->setEnabled(selectedCount > 0);
|
||||
connect(copyRowsAction, &QAction::triggered, this, &DocumentEditWidget::onContextMenuCopyRows);
|
||||
|
||||
QAction *cutRowsAction = rowsMenu->addAction("Вырезать строки");
|
||||
cutRowsAction->setEnabled(selectedCount > 0);
|
||||
connect(cutRowsAction, &QAction::triggered, this, &DocumentEditWidget::onContextMenuCutRows);
|
||||
|
||||
QAction *pasteRowsAction = rowsMenu->addAction("Вставить строки");
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
const QMimeData *mimeData = clipboard->mimeData();
|
||||
pasteRowsAction->setEnabled(mimeData && mimeData->hasText());
|
||||
connect(pasteRowsAction, &QAction::triggered, this, &DocumentEditWidget::onContextMenuPasteRows);
|
||||
|
||||
// Меню для работы с ячейками
|
||||
QMenu *cellsMenu = contextMenu.addMenu("Ячейки");
|
||||
QItemSelection cellSelection = selectionModel->selection();
|
||||
bool hasCellSelection = !cellSelection.isEmpty();
|
||||
|
||||
QAction *copyCellsAction = cellsMenu->addAction("Копировать ячейки");
|
||||
copyCellsAction->setEnabled(hasCellSelection);
|
||||
connect(copyCellsAction, &QAction::triggered, this, &DocumentEditWidget::onContextMenuCopyCells);
|
||||
|
||||
QAction *cutCellsAction = cellsMenu->addAction("Вырезать ячейки");
|
||||
cutCellsAction->setEnabled(hasCellSelection);
|
||||
connect(cutCellsAction, &QAction::triggered, this, &DocumentEditWidget::onContextMenuCutCells);
|
||||
|
||||
QAction *pasteCellsAction = cellsMenu->addAction("Вставить ячейки");
|
||||
pasteCellsAction->setEnabled(mimeData && mimeData->hasText());
|
||||
connect(pasteCellsAction, &QAction::triggered, this, &DocumentEditWidget::onContextMenuPasteCells);
|
||||
|
||||
contextMenu.addSeparator();
|
||||
|
||||
QAction *cellSettingsAction = contextMenu.addAction("Настройка ячейки...");
|
||||
connect(cellSettingsAction, &QAction::triggered, this, &DocumentEditWidget::onContextMenuCellSettings);
|
||||
|
||||
@@ -2103,4 +2141,378 @@ void DocumentEditWidget::onContextMenuCellSettings()
|
||||
qDebug() << "DocumentEditWidget::onContextMenuCellSettings: Ошибка при сохранении настроек в модель - модель не найдена";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Методы для копирования/вставки/вырезания строк
|
||||
void DocumentEditWidget::onContextMenuCopyRows()
|
||||
{
|
||||
QItemSelectionModel *selectionModel = m_tableView->selectionModel();
|
||||
QModelIndexList selectedRows = selectionModel->selectedRows();
|
||||
|
||||
if (selectedRows.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
copyRowsToClipboard(selectedRows);
|
||||
}
|
||||
|
||||
void DocumentEditWidget::onContextMenuCutRows()
|
||||
{
|
||||
QItemSelectionModel *selectionModel = m_tableView->selectionModel();
|
||||
QModelIndexList selectedRows = selectionModel->selectedRows();
|
||||
|
||||
if (selectedRows.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QModelIndex currentIndex = m_tableView->currentIndex();
|
||||
int insertPosition = currentIndex.isValid() ? currentIndex.row() : -1;
|
||||
|
||||
cutRows(insertPosition);
|
||||
}
|
||||
|
||||
void DocumentEditWidget::onContextMenuPasteRows()
|
||||
{
|
||||
QModelIndex currentIndex = m_tableView->currentIndex();
|
||||
int insertPosition = currentIndex.isValid() ? currentIndex.row() + 1 : -1;
|
||||
|
||||
if (insertPosition < 0) {
|
||||
// Если нет текущей позиции, вставляем в конец
|
||||
QAbstractItemModel *model = m_tableView->model();
|
||||
if (model) {
|
||||
insertPosition = model->rowCount();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pasteRowsFromClipboard(insertPosition);
|
||||
}
|
||||
|
||||
// Методы для копирования/вставки/вырезания ячеек
|
||||
void DocumentEditWidget::onContextMenuCopyCells()
|
||||
{
|
||||
QItemSelectionModel *selectionModel = m_tableView->selectionModel();
|
||||
QItemSelection selection = selectionModel->selection();
|
||||
|
||||
if (selection.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
copyCellsToClipboard(selection);
|
||||
}
|
||||
|
||||
void DocumentEditWidget::onContextMenuCutCells()
|
||||
{
|
||||
QModelIndex currentIndex = m_tableView->currentIndex();
|
||||
|
||||
if (!currentIndex.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
cutCells(currentIndex);
|
||||
}
|
||||
|
||||
void DocumentEditWidget::onContextMenuPasteCells()
|
||||
{
|
||||
QModelIndex currentIndex = m_tableView->currentIndex();
|
||||
|
||||
if (!currentIndex.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pasteCellsFromClipboard(currentIndex);
|
||||
}
|
||||
|
||||
// Вспомогательные методы для копирования/вставки строк
|
||||
void DocumentEditWidget::copyRowsToClipboard(const QModelIndexList &rows)
|
||||
{
|
||||
if (rows.isEmpty() || !m_tableView->model()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QAbstractItemModel *model = m_tableView->model();
|
||||
int columnCount = model->columnCount();
|
||||
|
||||
// Собираем номера строк и сортируем их
|
||||
QList<int> rowNumbers;
|
||||
for (const QModelIndex &index : rows) {
|
||||
if (index.isValid()) {
|
||||
rowNumbers.append(index.row());
|
||||
}
|
||||
}
|
||||
std::sort(rowNumbers.begin(), rowNumbers.end());
|
||||
rowNumbers.erase(std::unique(rowNumbers.begin(), rowNumbers.end()), rowNumbers.end());
|
||||
|
||||
// Формируем текст в формате CSV (табуляция для разделения столбцов, перенос строки для строк)
|
||||
QStringList rowTexts;
|
||||
for (int row : rowNumbers) {
|
||||
QStringList cellTexts;
|
||||
for (int col = 0; col < columnCount; ++col) {
|
||||
QModelIndex cellIndex = model->index(row, col);
|
||||
QString cellText = model->data(cellIndex, Qt::DisplayRole).toString();
|
||||
// Заменяем переносы строк и табуляции на пробелы для совместимости
|
||||
cellText.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ');
|
||||
cellTexts.append(cellText);
|
||||
}
|
||||
rowTexts.append(cellTexts.join("\t"));
|
||||
}
|
||||
|
||||
QString clipboardText = rowTexts.join("\n");
|
||||
|
||||
// Копируем в буфер обмена
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
clipboard->setText(clipboardText);
|
||||
|
||||
qDebug() << "DocumentEditWidget::copyRowsToClipboard: Скопировано строк:" << rowNumbers.size();
|
||||
}
|
||||
|
||||
void DocumentEditWidget::pasteRowsFromClipboard(int insertPosition)
|
||||
{
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
QString clipboardText = clipboard->text();
|
||||
|
||||
if (clipboardText.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QAbstractItemModel *model = m_tableView->model();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Парсим текст из буфера обмена
|
||||
QStringList rows = clipboardText.split('\n', Qt::SkipEmptyParts);
|
||||
if (rows.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int columnCount = model->columnCount();
|
||||
|
||||
// Определяем, какой контроллер использовать для добавления строк
|
||||
PerechenProcessor *perechenProcessor = qobject_cast<PerechenProcessor*>(m_processor);
|
||||
SpecificationPCBProcessor *specPCBProcessor = qobject_cast<SpecificationPCBProcessor*>(m_processor);
|
||||
SpecificationProcessor *specProcessor = qobject_cast<SpecificationProcessor*>(m_processor);
|
||||
VedomostProcessor *vedomostProcessor = qobject_cast<VedomostProcessor*>(m_processor);
|
||||
|
||||
// Вставляем строки в обратном порядке, чтобы позиция вставки не смещалась
|
||||
// и порядок строк сохранялся правильно
|
||||
for (int i = rows.size() - 1; i >= 0; --i) {
|
||||
QStringList cells = rows[i].split('\t');
|
||||
|
||||
// Добавляем пустую строку через соответствующий контроллер
|
||||
if (perechenProcessor && m_controller && m_controller->perechenTableController()) {
|
||||
m_controller->perechenTableController()->addEmptyRowAt(insertPosition);
|
||||
// Заполняем ячейки вставленной строки
|
||||
for (int col = 0; col < qMin(columnCount, cells.size()); ++col) {
|
||||
QModelIndex cellIndex = model->index(insertPosition, col);
|
||||
if (cellIndex.isValid()) {
|
||||
model->setData(cellIndex, cells[col].trimmed(), Qt::EditRole);
|
||||
}
|
||||
}
|
||||
} else if (specPCBProcessor && m_controller && m_controller->specificationPCBController()) {
|
||||
m_controller->specificationPCBController()->addEmptyRowAt(insertPosition);
|
||||
for (int col = 0; col < qMin(columnCount, cells.size()); ++col) {
|
||||
QModelIndex cellIndex = model->index(insertPosition, col);
|
||||
if (cellIndex.isValid()) {
|
||||
model->setData(cellIndex, cells[col].trimmed(), Qt::EditRole);
|
||||
}
|
||||
}
|
||||
} else if (specProcessor && m_controller && m_controller->specificationController()) {
|
||||
m_controller->specificationController()->addEmptyRowAt(insertPosition);
|
||||
for (int col = 0; col < qMin(columnCount, cells.size()); ++col) {
|
||||
QModelIndex cellIndex = model->index(insertPosition, col);
|
||||
if (cellIndex.isValid()) {
|
||||
model->setData(cellIndex, cells[col].trimmed(), Qt::EditRole);
|
||||
}
|
||||
}
|
||||
} else if (vedomostProcessor && m_controller && m_controller->vedomostController()) {
|
||||
m_controller->vedomostController()->addEmptyRowAt(insertPosition);
|
||||
for (int col = 0; col < qMin(columnCount, cells.size()); ++col) {
|
||||
QModelIndex cellIndex = model->index(insertPosition, col);
|
||||
if (cellIndex.isValid()) {
|
||||
model->setData(cellIndex, cells[col].trimmed(), Qt::EditRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "DocumentEditWidget::pasteRowsFromClipboard: Вставлено строк:" << rows.size() << "в позицию:" << insertPosition;
|
||||
}
|
||||
|
||||
void DocumentEditWidget::cutRows(int insertPosition)
|
||||
{
|
||||
QItemSelectionModel *selectionModel = m_tableView->selectionModel();
|
||||
QModelIndexList selectedRows = selectionModel->selectedRows();
|
||||
|
||||
if (selectedRows.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Копируем строки
|
||||
copyRowsToClipboard(selectedRows);
|
||||
|
||||
// Удаляем строки (в обратном порядке)
|
||||
QList<int> rowNumbers;
|
||||
for (const QModelIndex &index : selectedRows) {
|
||||
if (index.isValid()) {
|
||||
rowNumbers.append(index.row());
|
||||
}
|
||||
}
|
||||
std::sort(rowNumbers.begin(), rowNumbers.end());
|
||||
rowNumbers.erase(std::unique(rowNumbers.begin(), rowNumbers.end()), rowNumbers.end());
|
||||
std::reverse(rowNumbers.begin(), rowNumbers.end());
|
||||
|
||||
// Удаляем строки через соответствующий контроллер
|
||||
PerechenProcessor *perechenProcessor = qobject_cast<PerechenProcessor*>(m_processor);
|
||||
SpecificationPCBProcessor *specPCBProcessor = qobject_cast<SpecificationPCBProcessor*>(m_processor);
|
||||
SpecificationProcessor *specProcessor = qobject_cast<SpecificationProcessor*>(m_processor);
|
||||
VedomostProcessor *vedomostProcessor = qobject_cast<VedomostProcessor*>(m_processor);
|
||||
|
||||
for (int row : rowNumbers) {
|
||||
if (perechenProcessor && m_controller && m_controller->perechenTableController()) {
|
||||
m_controller->perechenTableController()->removeRow(row);
|
||||
} else if (specPCBProcessor && m_controller && m_controller->specificationPCBController()) {
|
||||
m_controller->specificationPCBController()->removeRow(row);
|
||||
} else if (specProcessor && m_controller && m_controller->specificationController()) {
|
||||
m_controller->specificationController()->removeRow(row);
|
||||
} else if (vedomostProcessor && m_controller && m_controller->vedomostController()) {
|
||||
m_controller->vedomostController()->removeRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "DocumentEditWidget::cutRows: Вырезано строк:" << rowNumbers.size();
|
||||
}
|
||||
|
||||
// Вспомогательные методы для копирования/вставки ячеек
|
||||
void DocumentEditWidget::copyCellsToClipboard(const QItemSelection &selection)
|
||||
{
|
||||
if (selection.isEmpty() || !m_tableView->model()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QAbstractItemModel *model = m_tableView->model();
|
||||
QModelIndexList indexes = selection.indexes();
|
||||
|
||||
if (indexes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Определяем границы выбранной области
|
||||
int minRow = indexes.first().row();
|
||||
int maxRow = indexes.first().row();
|
||||
int minCol = indexes.first().column();
|
||||
int maxCol = indexes.first().column();
|
||||
|
||||
for (const QModelIndex &index : indexes) {
|
||||
minRow = qMin(minRow, index.row());
|
||||
maxRow = qMax(maxRow, index.row());
|
||||
minCol = qMin(minCol, index.column());
|
||||
maxCol = qMax(maxCol, index.column());
|
||||
}
|
||||
|
||||
// Формируем текст в формате таблицы (табуляция для столбцов, перенос строки для строк)
|
||||
QStringList rowTexts;
|
||||
for (int row = minRow; row <= maxRow; ++row) {
|
||||
QStringList cellTexts;
|
||||
for (int col = minCol; col <= maxCol; ++col) {
|
||||
QModelIndex cellIndex = model->index(row, col);
|
||||
QString cellText;
|
||||
// Проверяем, есть ли эта ячейка в выделении
|
||||
if (indexes.contains(cellIndex)) {
|
||||
cellText = model->data(cellIndex, Qt::DisplayRole).toString();
|
||||
}
|
||||
// Заменяем переносы строк и табуляции на пробелы для совместимости
|
||||
cellText.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ');
|
||||
cellTexts.append(cellText);
|
||||
}
|
||||
rowTexts.append(cellTexts.join("\t"));
|
||||
}
|
||||
|
||||
QString clipboardText = rowTexts.join("\n");
|
||||
|
||||
// Копируем в буфер обмена
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
clipboard->setText(clipboardText);
|
||||
|
||||
qDebug() << "DocumentEditWidget::copyCellsToClipboard: Скопировано ячеек:" << indexes.size();
|
||||
}
|
||||
|
||||
void DocumentEditWidget::pasteCellsFromClipboard(const QModelIndex &topLeft)
|
||||
{
|
||||
if (!topLeft.isValid() || !m_tableView->model()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
QString clipboardText = clipboard->text();
|
||||
|
||||
if (clipboardText.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QAbstractItemModel *model = m_tableView->model();
|
||||
|
||||
// Парсим текст из буфера обмена
|
||||
QStringList rows = clipboardText.split('\n', Qt::SkipEmptyParts);
|
||||
if (rows.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int startRow = topLeft.row();
|
||||
int startCol = topLeft.column();
|
||||
int maxRow = model->rowCount() - 1;
|
||||
int maxCol = model->columnCount() - 1;
|
||||
|
||||
// Вставляем данные, начиная с topLeft
|
||||
for (int i = 0; i < rows.size(); ++i) {
|
||||
int currentRow = startRow + i;
|
||||
if (currentRow > maxRow) {
|
||||
break; // Достигли конца таблицы
|
||||
}
|
||||
|
||||
QStringList cells = rows[i].split('\t');
|
||||
for (int j = 0; j < cells.size(); ++j) {
|
||||
int currentCol = startCol + j;
|
||||
if (currentCol > maxCol) {
|
||||
break; // Достигли конца строки
|
||||
}
|
||||
|
||||
QModelIndex cellIndex = model->index(currentRow, currentCol);
|
||||
if (cellIndex.isValid()) {
|
||||
model->setData(cellIndex, cells[j].trimmed(), Qt::EditRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "DocumentEditWidget::pasteCellsFromClipboard: Вставлено ячеек из буфера обмена, начиная с row:" << startRow << "col:" << startCol;
|
||||
}
|
||||
|
||||
void DocumentEditWidget::cutCells(const QModelIndex &topLeft)
|
||||
{
|
||||
if (!topLeft.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QItemSelectionModel *selectionModel = m_tableView->selectionModel();
|
||||
QItemSelection selection = selectionModel->selection();
|
||||
|
||||
if (selection.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Копируем ячейки
|
||||
copyCellsToClipboard(selection);
|
||||
|
||||
// Очищаем выбранные ячейки
|
||||
QModelIndexList indexes = selection.indexes();
|
||||
QAbstractItemModel *model = m_tableView->model();
|
||||
|
||||
for (const QModelIndex &index : indexes) {
|
||||
if (index.isValid()) {
|
||||
model->setData(index, "", Qt::EditRole);
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "DocumentEditWidget::cutCells: Вырезано ячеек:" << indexes.size();
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <QHBoxLayout>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QClipboard>
|
||||
#include <QMimeData>
|
||||
#include "../model/componenttablemodel.h"
|
||||
#include "../model/projectparamtablemodel.h"
|
||||
#include "../model/perechentablemodel.h"
|
||||
@@ -217,6 +219,14 @@ private slots:
|
||||
void onContextMenuMergeRows(); // Объединение нескольких выделенных строк (все столбцы)
|
||||
void onContextMenuMergeColumn(); // Объединение только определенного столбца
|
||||
void onContextMenuCellSettings();
|
||||
|
||||
// Слоты для копирования/вставки/вырезания
|
||||
void onContextMenuCopyRows(); // Копирование строк
|
||||
void onContextMenuCutRows(); // Вырезание строк
|
||||
void onContextMenuPasteRows(); // Вставка строк
|
||||
void onContextMenuCopyCells(); // Копирование ячеек
|
||||
void onContextMenuCutCells(); // Вырезание ячеек
|
||||
void onContextMenuPasteCells(); // Вставка ячеек
|
||||
|
||||
private:
|
||||
void setUp();
|
||||
@@ -228,6 +238,14 @@ private:
|
||||
// Вспомогательные методы
|
||||
int getCurrentSelectedRow() const;
|
||||
void applyColumnStretching(); // Метод для применения настроек растягивания столбцов
|
||||
|
||||
// Вспомогательные методы для копирования/вставки
|
||||
void copyRowsToClipboard(const QModelIndexList &rows);
|
||||
void pasteRowsFromClipboard(int insertPosition);
|
||||
void cutRows(int insertPosition); // Вырезает строки (копирует и удаляет)
|
||||
void copyCellsToClipboard(const QItemSelection &selection);
|
||||
void pasteCellsFromClipboard(const QModelIndex &topLeft);
|
||||
void cutCells(const QModelIndex &topLeft); // Вырезает ячейки (копирует и очищает)
|
||||
|
||||
DocumentProcessor *m_processor;
|
||||
ComponentTableModel *m_componentModel;
|
||||
|
||||
Reference in New Issue
Block a user