diff --git a/app_icon.rc b/app_icon.rc index c133b1c..ce41c89 100644 --- a/app_icon.rc +++ b/app_icon.rc @@ -11,3 +11,5 @@ IDI_ICON1 ICON DISCARDABLE "assets/icon2.ico" + + diff --git a/controller/specificationpcbcontroller.cpp b/controller/specificationpcbcontroller.cpp index 9421fcb..cc5880b 100644 --- a/controller/specificationpcbcontroller.cpp +++ b/controller/specificationpcbcontroller.cpp @@ -297,20 +297,6 @@ void SpecificationPCBController::generateTableFromComponents() QList 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>> 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 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 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 props = component->properties(); QString originalString = props.value("ManufacturerPartNumber", ""); QList designationParts = splitLongDesignation(currentDesignation, originalString, component); - for (int partIndex = 0; partIndex < designationParts.size(); ++partIndex) { + // Формируем список строк названия с учетом типа компонента + QList 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 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 props = component->properties(); + QString type = props.value("Type", ""); + if (!type.isEmpty()) { + return type; + } + + // Если и тип не найден, возвращаем пустую строку + return QString(); +} + QString SpecificationPCBController::getComponentFormat(ComponentModel *component) { if (!component) return ""; diff --git a/controller/specificationpcbcontroller.h b/controller/specificationpcbcontroller.h index cea27ca..dac2c40 100644 --- a/controller/specificationpcbcontroller.h +++ b/controller/specificationpcbcontroller.h @@ -83,6 +83,7 @@ private: // Методы для группировки QString getComponentType(ComponentModel *component); + QString getComponentTypeSingular(ComponentModel *component); // Метод для разделения десигнатора на буквенную и цифровую части QPair splitDesignator(const QString &designator); diff --git a/view/documentprocessor.cpp b/view/documentprocessor.cpp index d162d11..9190b2e 100644 --- a/view/documentprocessor.cpp +++ b/view/documentprocessor.cpp @@ -23,6 +23,9 @@ Q_DECLARE_METATYPE(VedomostCellData) #include #include #include +#include +#include +#include // 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 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(m_processor); + SpecificationPCBProcessor *specPCBProcessor = qobject_cast(m_processor); + SpecificationProcessor *specProcessor = qobject_cast(m_processor); + VedomostProcessor *vedomostProcessor = qobject_cast(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 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(m_processor); + SpecificationPCBProcessor *specPCBProcessor = qobject_cast(m_processor); + SpecificationProcessor *specProcessor = qobject_cast(m_processor); + VedomostProcessor *vedomostProcessor = qobject_cast(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(); } \ No newline at end of file diff --git a/view/documentprocessor.h b/view/documentprocessor.h index a7a79e9..8d09518 100644 --- a/view/documentprocessor.h +++ b/view/documentprocessor.h @@ -9,6 +9,8 @@ #include #include #include +#include +#include #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;