1078 lines
53 KiB
C++
1078 lines
53 KiB
C++
#include "perechentablecontroller.h"
|
|
#include "../model/componentmodel.h"
|
|
#include "../model/designatormappingmodel.h"
|
|
#include <QDebug>
|
|
#include <QCollator>
|
|
|
|
PerechenTableController::PerechenTableController(QObject *parent)
|
|
: QObject(parent)
|
|
, m_tableModel(new PerechenTableModel(this))
|
|
, m_designatorMappingModel(nullptr)
|
|
, m_projectSettingsModel(nullptr)
|
|
{
|
|
// Устанавливаем маппинг по умолчанию для группировки
|
|
// Эти настройки будут переопределены в MainController
|
|
m_columnMappings << "Designator" << "Name" << "Quantity" << "Note";
|
|
}
|
|
|
|
PerechenTableController::~PerechenTableController()
|
|
{
|
|
// m_tableModel удалится автоматически, так как он является дочерним объектом
|
|
}
|
|
|
|
void PerechenTableController::setDesignatorMappingModel(DesignatorMappingModel *mappingModel)
|
|
{
|
|
m_designatorMappingModel = mappingModel;
|
|
}
|
|
|
|
void PerechenTableController::setModel(PerechenTableModel *model)
|
|
{
|
|
if (m_tableModel != model) {
|
|
qDebug() << "PerechenTableController::setModel: Заменяем модель таблицы";
|
|
m_tableModel = model;
|
|
|
|
// Если модель не пуста, загружаем из неё настройки колонок
|
|
if (m_tableModel) {
|
|
QStringList columnMappings = m_tableModel->columnMappings();
|
|
if (!columnMappings.isEmpty()) {
|
|
qDebug() << "PerechenTableController::setModel: Загружаем настройки колонок из модели:" << columnMappings;
|
|
m_columnMappings = columnMappings;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PerechenTableController::setProjectSettingsModel(ProjectSettingsModel *model)
|
|
{
|
|
m_projectSettingsModel = model;
|
|
qDebug() << "PerechenTableController::setProjectSettingsModel: Установлена модель настроек проекта";
|
|
}
|
|
|
|
void PerechenTableController::setComponents(const QList<ComponentModel*> &components)
|
|
{
|
|
m_components = components;
|
|
|
|
// Собираем доступные свойства из компонентов
|
|
m_propertyNames = collectPropertyNames();
|
|
}
|
|
|
|
void PerechenTableController::generateTableFromComponents()
|
|
{
|
|
if (!m_tableModel) {
|
|
return;
|
|
}
|
|
|
|
qDebug() << "PerechenTableController::generateTableFromComponents: Начинаем генерацию таблицы";
|
|
qDebug() << "PerechenTableController::generateTableFromComponents: Текущие настройки колонок:" << m_columnMappings;
|
|
|
|
// Дополнительная проверка настроек колонок
|
|
if (m_tableModel) {
|
|
QStringList modelMappings = m_tableModel->columnMappings();
|
|
qDebug() << "PerechenTableController::generateTableFromComponents: Настройки колонок в модели:" << modelMappings;
|
|
|
|
// Если настройки в модели отличаются от локальных, синхронизируем их
|
|
if (modelMappings != m_columnMappings) {
|
|
qDebug() << "PerechenTableController::generateTableFromComponents: Синхронизируем настройки колонок";
|
|
m_columnMappings = modelMappings;
|
|
}
|
|
}
|
|
|
|
qDebug() << "PerechenTableController::generateTableFromComponents: Количество компонентов:" << m_components.size();
|
|
|
|
// Очищаем таблицу
|
|
m_tableModel->clear();
|
|
|
|
if (m_components.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Группируем компоненты по типу
|
|
// QList<ComponentGroup> groups = groupComponentsByType(m_components);
|
|
|
|
// Добавляем заголовок таблицы
|
|
|
|
PerechenRowData emptyRow;
|
|
emptyRow.isEmpty = true;
|
|
|
|
|
|
// Добавляем группы компонентов с группировкой по типу
|
|
int rowNumber = 1;
|
|
QString currentType;
|
|
std::sort(m_components.begin(), m_components.end(),
|
|
[this](ComponentModel* a, ComponentModel* b) {
|
|
if (!a || !b) return false;
|
|
|
|
QString designatorA = a->designator();
|
|
QString designatorB = b->designator();
|
|
|
|
// Разделяем десигнаторы на буквенную и числовую части
|
|
QPair<QString, QString> partsA = PerechenTableController::splitDesignator(designatorA);
|
|
QPair<QString, QString> partsB = PerechenTableController::splitDesignator(designatorB);
|
|
|
|
QString letterA = partsA.first;
|
|
QString letterB = partsB.first;
|
|
QString numberA = partsA.second;
|
|
QString numberB = partsB.second;
|
|
|
|
// Сначала сравниваем буквенные части
|
|
if (letterA != letterB) {
|
|
return letterA < letterB;
|
|
}
|
|
|
|
// Если буквенные части одинаковые, сравниваем числовые части
|
|
bool okA, okB;
|
|
int numA = numberA.toInt(&okA);
|
|
int numB = numberB.toInt(&okB);
|
|
|
|
if (okA && okB) {
|
|
return numA < numB;
|
|
}
|
|
|
|
// Fallback: если не удалось преобразовать в числа, используем строковое сравнение
|
|
return numberA < numberB;
|
|
});
|
|
|
|
QString complesDesignatorFront = "";
|
|
QString complesDesignatorBack = "";
|
|
// Группируем компоненты по буквенной части десигнатора
|
|
QMap<QString, QList<ComponentModel*>> groupedByLetter;
|
|
for (ComponentModel *component : m_components) {
|
|
QPair<QString, QString> designatorParts = splitDesignator(component->designator());
|
|
QString desLetter = designatorParts.first;
|
|
groupedByLetter[desLetter].append(component);
|
|
}
|
|
|
|
// Обрабатываем каждую группу
|
|
for (auto it = groupedByLetter.begin(); it != groupedByLetter.end(); ++it) {
|
|
QString desLetter = it.key();
|
|
QList<ComponentModel*> componentsInGroup = it.value();
|
|
|
|
if (componentsInGroup.size() > 1) {
|
|
// Если компонентов больше одного, добавляем заголовок типа
|
|
PerechenRowData headerRow;
|
|
|
|
|
|
m_tableModel->addRow(emptyRow);
|
|
headerRow.isHeader = true;
|
|
headerRow.designation = PerechenCellData(getComponentType(componentsInGroup.first()), 0, 1, true, false, false, 100, true);
|
|
|
|
m_tableModel->addRow(headerRow);
|
|
m_tableModel->addRow(emptyRow);
|
|
|
|
// Обрабатываем компоненты последовательно, группируя по currentDesignation
|
|
QString prevDesignation = "";
|
|
QString prevOriginalString = "";
|
|
ComponentModel *prevComponent = nullptr;
|
|
QString groupStartDesignator = "";
|
|
QString groupLastDesignator = "";
|
|
int groupCount = 0;
|
|
|
|
for (int i = 0; i < componentsInGroup.size(); ++i) {
|
|
ComponentModel *component = componentsInGroup[i];
|
|
QString currentDesignation = getComponentValue(component, m_columnMappings.value(0, "<Пусто>"));
|
|
|
|
if (i == 0 || currentDesignation != prevDesignation) {
|
|
// Добавляем предыдущую группу, если она есть
|
|
if (i > 0 && groupCount > 0) {
|
|
// Разделяем длинное наименование на несколько строк
|
|
// Используем исходное значение для правильного разделения по частям
|
|
QList<QString> designationParts = splitLongDesignation(prevDesignation, prevOriginalString, prevComponent);
|
|
|
|
for (int partIndex = 0; partIndex < designationParts.size(); ++partIndex) {
|
|
PerechenRowData row;
|
|
|
|
// Только в первой строке показываем позицию и количество
|
|
if (partIndex == 0) {
|
|
QString positionText;
|
|
if (groupCount == 1) {
|
|
positionText = groupStartDesignator;
|
|
} else if (groupCount == 2) {
|
|
// Если в группе 2 элемента - через запятую
|
|
positionText = groupStartDesignator + ", " + groupLastDesignator;
|
|
} else {
|
|
// Если в группе 3 и более элементов - через дефис
|
|
positionText = groupStartDesignator + "-" + groupLastDesignator;
|
|
}
|
|
row.position = PerechenCellData(positionText, 0, 1, false, false, false, 100, false);
|
|
row.quantity = PerechenCellData(QString::number(groupCount), 0, 1, false, false, false, 100, false);
|
|
} else {
|
|
// В последующих строках оставляем пустыми позицию и количество
|
|
row.position = PerechenCellData("", 0, 1, false, false, false, 100, false);
|
|
row.quantity = PerechenCellData("", 0, 1, false, false, false, 100, false);
|
|
}
|
|
|
|
row.designation = PerechenCellData(designationParts[partIndex], 0, 1, false, false, false, 100, false);
|
|
row.note = PerechenCellData("", 0, 1, false, false, false, 100, false);
|
|
m_tableModel->addRow(row);
|
|
}
|
|
}
|
|
|
|
// Начинаем новую группу
|
|
groupStartDesignator = component->designator();
|
|
groupLastDesignator = component->designator();
|
|
groupCount = 1;
|
|
prevDesignation = currentDesignation;
|
|
|
|
// Сохраняем исходное значение и компонент для правильного разделения
|
|
QMap<QString, QString> props = component->properties();
|
|
prevOriginalString = props.value(m_columnMappings.value(0, "<Пусто>"), "");
|
|
prevComponent = component;
|
|
} else {
|
|
// Продолжаем текущую группу - обновляем последний десигнатор
|
|
groupLastDesignator = component->designator();
|
|
groupCount++;
|
|
}
|
|
}
|
|
|
|
// Добавляем последнюю группу
|
|
if (groupCount > 0) {
|
|
// Разделяем длинное наименование на несколько строк
|
|
// Используем сохраненные значения для правильного разделения
|
|
QList<QString> designationParts = splitLongDesignation(prevDesignation, prevOriginalString, prevComponent);
|
|
|
|
for (int partIndex = 0; partIndex < designationParts.size(); ++partIndex) {
|
|
PerechenRowData row;
|
|
|
|
// Только в первой строке показываем позицию и количество
|
|
if (partIndex == 0) {
|
|
QString positionText;
|
|
if (groupCount == 1) {
|
|
positionText = groupStartDesignator;
|
|
} else if (groupCount == 2) {
|
|
// Если в группе 2 элемента - через запятую
|
|
positionText = groupStartDesignator + ", " + groupLastDesignator;
|
|
} else {
|
|
// Если в группе 3 и более элементов - через дефис
|
|
positionText = groupStartDesignator + "-" + groupLastDesignator;
|
|
}
|
|
row.position = PerechenCellData(positionText, 0, 1, false, false, false, 100, false);
|
|
row.quantity = PerechenCellData(QString::number(groupCount), 0, 1, false, false, false, 100, false);
|
|
} else {
|
|
// В последующих строках оставляем пустыми позицию и количество
|
|
row.position = PerechenCellData("", 0, 1, false, false, false, 100, false);
|
|
row.quantity = PerechenCellData("", 0, 1, false, false, false, 100, false);
|
|
}
|
|
|
|
row.designation = PerechenCellData(designationParts[partIndex], 0, 1, false, false, false, 100, false);
|
|
row.note = PerechenCellData("", 0, 1, false, false, false, 100, false);
|
|
m_tableModel->addRow(row);
|
|
}
|
|
}
|
|
} else {
|
|
// Если компонент один, объединяем тип и обозначение
|
|
ComponentModel *component = componentsInGroup.first();
|
|
QString componentType = getComponentType(component);
|
|
QString currentDesignation = getComponentValue(component, m_columnMappings.value(0, "<Пусто>"));
|
|
QString fullDesignation = componentType + " " + currentDesignation;
|
|
|
|
// Разделяем длинное наименование на несколько строк
|
|
// Нужно получить исходное значение для правильного разделения
|
|
QMap<QString, QString> props = component->properties();
|
|
QString originalString = props.value(m_columnMappings.value(0, "<Пусто>"), "");
|
|
QList<QString> designationParts = splitLongDesignation(fullDesignation, originalString, component);
|
|
|
|
m_tableModel->addRow(emptyRow);
|
|
|
|
for (int partIndex = 0; partIndex < designationParts.size(); ++partIndex) {
|
|
PerechenRowData row;
|
|
|
|
// Только в первой строке показываем позицию и количество
|
|
if (partIndex == 0) {
|
|
row.position = PerechenCellData(component->designator(), 0, 1, false, false, false, 100, false);
|
|
row.quantity = PerechenCellData("1", 0, 1, false, false, false, 100, false);
|
|
} else {
|
|
// В последующих строках оставляем пустыми позицию и количество
|
|
row.position = PerechenCellData("", 0, 1, false, false, false, 100, false);
|
|
row.quantity = PerechenCellData("", 0, 1, false, false, false, 100, false);
|
|
}
|
|
|
|
row.designation = PerechenCellData(designationParts[partIndex], 0, 1, false, false, false, 100, false);
|
|
row.note = PerechenCellData("", 0, 1, false, false, false, 100, false);
|
|
m_tableModel->addRow(row);
|
|
}
|
|
}
|
|
}
|
|
|
|
// for (const ComponentGroup &group : groups) {
|
|
// if (group.components.isEmpty()) {
|
|
// continue;
|
|
// }
|
|
|
|
// // Добавляем пустую строку между разными типами компонентов
|
|
// if (currentType != group.componentType && !currentType.isEmpty()) {
|
|
// PerechenRowData emptyRow;
|
|
// emptyRow.isEmpty = true;
|
|
// m_tableModel->addRow(emptyRow);
|
|
// }
|
|
|
|
// // Добавляем заголовок типа компонента, если он изменился
|
|
// if (currentType != group.componentType) {
|
|
// PerechenRowData typeHeaderRow;
|
|
// typeHeaderRow.isHeader = true;
|
|
// typeHeaderRow.designation = PerechenCellData(group.componentType, 0, 1, true);
|
|
// m_tableModel->addRow(typeHeaderRow);
|
|
// currentType = group.componentType;
|
|
// }
|
|
|
|
// // Если в группе один элемент, объединяем тип и наименование
|
|
// if (group.components.size() == 1) {
|
|
// PerechenRowData row;
|
|
|
|
// // Заполняем данные в зависимости от маппинга
|
|
// for (int col = 0; col < 4; ++col) {
|
|
// QString mapping = m_columnMappings.value(col, "<Пусто>");
|
|
// QString value;
|
|
// bool isAuto = false;
|
|
|
|
// if (mapping == "<Авто>") {
|
|
// value = QString::number(rowNumber);
|
|
// isAuto = true;
|
|
// } else if (mapping == "<Пусто>") {
|
|
// value = "";
|
|
// } else if (mapping == "Designator") {
|
|
// // Для колонки Designator используем дезигнатор компонента
|
|
// value = group.components.first()->designator();
|
|
// } else if (mapping == "Quantity" || mapping == "Кол.") {
|
|
// // Для колонки количества используем количество компонентов в группе
|
|
// value = QString::number(group.components.size());
|
|
// } else if (mapping == "Name" || mapping == "Наименование") {
|
|
// // Для наименования используем название группы (это то, что возвращает getComponentDesignation)
|
|
// value = group.name;
|
|
// } else {
|
|
// // Для остальных свойств берем значение из компонента
|
|
// ComponentModel *component = group.components.first();
|
|
// value = getComponentValue(component, mapping);
|
|
// }
|
|
|
|
// PerechenCellData cellData(value, rowNumber, 1, false, isAuto, value.isEmpty());
|
|
|
|
// switch (col) {
|
|
// case 0: row.position = cellData; break;
|
|
// case 1: row.designation = cellData; break;
|
|
// case 2: row.quantity = cellData; break;
|
|
// case 3: row.note = cellData; break;
|
|
// }
|
|
// }
|
|
|
|
// row.rowNumber = rowNumber++;
|
|
// m_tableModel->addRow(row);
|
|
// } else {
|
|
// // Если в группе несколько элементов, добавляем строку с диапазоном
|
|
// PerechenRowData row;
|
|
|
|
// // Заполняем данные в зависимости от маппинга
|
|
// for (int col = 0; col < 4; ++col) {
|
|
// QString mapping = m_columnMappings.value(col, "<Пусто>");
|
|
// QString value;
|
|
// bool isAuto = false;
|
|
|
|
// if (mapping == "<Авто>") {
|
|
// value = QString::number(rowNumber);
|
|
// isAuto = true;
|
|
// } else if (mapping == "<Пусто>") {
|
|
// value = "";
|
|
// } else if (mapping == "Designator") {
|
|
// // Для колонки Designator используем диапазон обозначений группы
|
|
// value = group.designatorRange;
|
|
// } else if (mapping == "Quantity" || mapping == "Кол.") {
|
|
// // Для колонки количества используем количество компонентов в группе
|
|
// value = QString::number(group.components.size());
|
|
// } else if (mapping == "Name" || mapping == "Наименование") {
|
|
// // Для наименования используем название группы (это то, что возвращает getComponentDesignation)
|
|
// value = group.name;
|
|
// } else {
|
|
// // Для остальных свойств берем значение из первого компонента группы
|
|
// ComponentModel *firstComponent = group.components.first();
|
|
// value = getComponentValue(firstComponent, mapping);
|
|
// }
|
|
|
|
// PerechenCellData cellData(value, rowNumber, 1, false, isAuto, value.isEmpty());
|
|
|
|
// switch (col) {
|
|
// case 0: row.position = cellData; break;
|
|
// case 1: row.designation = cellData; break;
|
|
// case 2: row.quantity = cellData; break;
|
|
// case 3: row.note = cellData; break;
|
|
// }
|
|
// }
|
|
|
|
// row.rowNumber = rowNumber++;
|
|
// m_tableModel->addRow(row);
|
|
// }
|
|
// }
|
|
|
|
qDebug() << "PerechenTableController::generateTableFromComponents: Таблица сгенерирована, строк:" << m_tableModel->rowCount();
|
|
|
|
// Оптимизируем разрывы страниц после генерации таблицы
|
|
m_tableModel->optimizePageBreaks();
|
|
|
|
} catch (const std::exception &e) {
|
|
qDebug() << "PerechenTableController::generateTableFromComponents: Ошибка при генерации:" << e.what();
|
|
} catch (...) {
|
|
qDebug() << "PerechenTableController::generateTableFromComponents: Неизвестная ошибка при генерации";
|
|
}
|
|
}
|
|
|
|
void PerechenTableController::updateTableFromComponents()
|
|
{
|
|
if (!m_tableModel) {
|
|
qDebug() << "PerechenTableController::updateTableFromComponents: m_tableModel is null";
|
|
return;
|
|
}
|
|
|
|
qDebug() << "PerechenTableController::updateTableFromComponents: Обновляем таблицу из" << m_components.size() << "компонентов";
|
|
|
|
if (m_components.isEmpty()) {
|
|
qDebug() << "PerechenTableController::updateTableFromComponents: Нет компонентов для обновления";
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Метод updateTableFromComponents больше не используется в новой логике
|
|
qDebug() << "PerechenTableController::updateTableFromComponents: Метод устарел";
|
|
} catch (const std::exception &e) {
|
|
qDebug() << "PerechenTableController::updateTableFromComponents: Ошибка при обновлении:" << e.what();
|
|
} catch (...) {
|
|
qDebug() << "PerechenTableController::updateTableFromComponents: Неизвестная ошибка при обновлении";
|
|
}
|
|
}
|
|
|
|
QString PerechenTableController::getComponentValue(ComponentModel *component, const QString &propertyName)
|
|
{
|
|
if (!component) {
|
|
return "";
|
|
}
|
|
|
|
qDebug() << "PerechenTableController::getComponentValue: propertyName =" << propertyName;
|
|
qDebug() << "PerechenTableController::getComponentValue: Текущие настройки колонок:" << m_columnMappings;
|
|
|
|
// Получаем значение свойства компонента
|
|
QString value;
|
|
if (propertyName == "Designator") {
|
|
value = component->designator();
|
|
qDebug() << "PerechenTableController::getComponentValue: Designator =" << value;
|
|
} else {
|
|
QMap<QString, QString> props = component->properties();
|
|
value = props.value(propertyName, "");
|
|
qDebug() << "PerechenTableController::getComponentValue: Свойство" << propertyName << "=" << value;
|
|
}
|
|
|
|
// Проверяем, является ли значение свойства выражением
|
|
if (value.startsWith("=")) {
|
|
qDebug() << "PerechenTableController::getComponentValue: Значение свойства является выражением:" << value;
|
|
return parseExpression(component, value);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
QString PerechenTableController::parseExpression(ComponentModel *component, const QString &expression)
|
|
{
|
|
if (!component) {
|
|
return "";
|
|
}
|
|
|
|
qDebug() << "PerechenTableController::parseExpression: Обрабатываем выражение:" << expression;
|
|
|
|
QString result;
|
|
QString expr;
|
|
|
|
// Определяем, как обрабатывать выражение
|
|
if (expression.startsWith("=")) {
|
|
expr = expression.mid(1); // Убираем начальный "="
|
|
} else if (expression.startsWith("\"") && expression.endsWith("\"")) {
|
|
// Если это просто значение в кавычках, возвращаем его без кавычек
|
|
return expression.mid(1, expression.length() - 2);
|
|
} else {
|
|
// Если это не выражение, возвращаем как есть
|
|
return expression;
|
|
}
|
|
|
|
// Разбиваем выражение по операторам "+" с учетом кавычек
|
|
QStringList parts;
|
|
QString currentPart;
|
|
bool inDoubleQuotes = false;
|
|
bool inSingleQuotes = false;
|
|
|
|
for (int i = 0; i < expr.length(); ++i) {
|
|
QChar ch = expr[i];
|
|
|
|
if (ch == '"' && !inSingleQuotes) {
|
|
inDoubleQuotes = !inDoubleQuotes;
|
|
currentPart += ch;
|
|
} else if (ch == '\'' && !inDoubleQuotes) {
|
|
inSingleQuotes = !inSingleQuotes;
|
|
currentPart += ch;
|
|
} else if (ch == '+' && !inDoubleQuotes && !inSingleQuotes) {
|
|
// Это разделитель "+" вне кавычек
|
|
if (!currentPart.trimmed().isEmpty()) {
|
|
parts.append(currentPart.trimmed());
|
|
}
|
|
currentPart.clear();
|
|
} else {
|
|
currentPart += ch;
|
|
}
|
|
}
|
|
|
|
// Добавляем последнюю часть
|
|
if (!currentPart.trimmed().isEmpty()) {
|
|
parts.append(currentPart.trimmed());
|
|
}
|
|
|
|
qDebug() << "PerechenTableController::parseExpression: Части выражения:" << parts;
|
|
|
|
for (const QString &part : parts) {
|
|
QString trimmedPart = part.trimmed();
|
|
qDebug() << "PerechenTableController::parseExpression: Обрабатываем часть:" << trimmedPart;
|
|
|
|
// Если это не пустая строка, добавляем к результату
|
|
if (!trimmedPart.isEmpty()) {
|
|
QString value;
|
|
|
|
// Проверяем, является ли это литералом в одинарных кавычках
|
|
if (trimmedPart.startsWith("'") && trimmedPart.endsWith("'")) {
|
|
// Это литерал - убираем кавычки и используем как есть
|
|
value = trimmedPart.mid(1, trimmedPart.length() - 2);
|
|
qDebug() << "PerechenTableController::parseExpression: Найден литерал в одинарных кавычках:" << value;
|
|
}
|
|
// Проверяем, является ли это именем свойства компонента в двойных кавычках
|
|
else if (trimmedPart.startsWith("\"") && trimmedPart.endsWith("\"")) {
|
|
// Это свойство компонента - убираем кавычки и ищем в свойствах
|
|
QString propertyName = trimmedPart.mid(1, trimmedPart.length() - 2);
|
|
qDebug() << "PerechenTableController::parseExpression: Ищем свойство компонента:" << propertyName;
|
|
|
|
if (propertyName == "Designator") {
|
|
value = component->designator();
|
|
qDebug() << "PerechenTableController::parseExpression: Найдено свойство Designator =" << value;
|
|
} else {
|
|
// Ищем в свойствах компонента
|
|
QMap<QString, QString> props = component->properties();
|
|
value = props.value(propertyName, "");
|
|
qDebug() << "PerechenTableController::parseExpression: Ищем свойство" << propertyName << "в компоненте, результат:" << value;
|
|
// Рекурсивно обрабатываем вложенные выражения
|
|
if (value.startsWith("=")) {
|
|
qDebug() << "PerechenTableController::parseExpression: Значение свойства является вложенным выражением, рекурсивно обрабатываем";
|
|
value = parseExpression(component, value);
|
|
}
|
|
}
|
|
}
|
|
// Проверяем, является ли это именем свойства компонента без кавычек
|
|
else if (trimmedPart == "Designator") {
|
|
value = component->designator();
|
|
qDebug() << "PerechenTableController::parseExpression: Найдено свойство Designator =" << value;
|
|
} else {
|
|
// Ищем в свойствах компонента
|
|
QMap<QString, QString> props = component->properties();
|
|
value = props.value(trimmedPart, "");
|
|
qDebug() << "PerechenTableController::parseExpression: Ищем свойство" << trimmedPart << "в компоненте, результат:" << value;
|
|
// Рекурсивно обрабатываем вложенные выражения
|
|
if (value.startsWith("=")) {
|
|
qDebug() << "PerechenTableController::parseExpression: Значение свойства является вложенным выражением, рекурсивно обрабатываем";
|
|
value = parseExpression(component, value);
|
|
}
|
|
}
|
|
|
|
// Добавляем значение к результату
|
|
if (!value.isEmpty()) {
|
|
if (!result.isEmpty()) {
|
|
result += value;
|
|
} else {
|
|
result = value;
|
|
}
|
|
qDebug() << "PerechenTableController::parseExpression: Добавили к результату, текущий результат:" << result;
|
|
}
|
|
}
|
|
}
|
|
|
|
qDebug() << "PerechenTableController::parseExpression: Финальный результат:" << result;
|
|
return result;
|
|
}
|
|
|
|
QList<QString> PerechenTableController::splitComplexStringByMiddle(const QString &text, int maxLength)
|
|
{
|
|
QList<QString> result;
|
|
|
|
if (text.length() <= maxLength) {
|
|
result.append(text);
|
|
return result;
|
|
}
|
|
|
|
qDebug() << "PerechenTableController::splitComplexStringByMiddle: Разделяем комплексную строку:" << text;
|
|
|
|
// Ищем запятые в тексте
|
|
QList<int> commaPositions;
|
|
for (int i = 0; i < text.length(); ++i) {
|
|
if (text[i] == ',') {
|
|
commaPositions.append(i);
|
|
}
|
|
}
|
|
|
|
if (commaPositions.isEmpty()) {
|
|
// Если запятых нет, используем обычное разделение по пробелам
|
|
qDebug() << "PerechenTableController::splitComplexStringByMiddle: Запятых не найдено, используем splitBySpaces";
|
|
return splitBySpaces(text, maxLength);
|
|
}
|
|
|
|
// Ищем запятую, ближайшую к середине строки
|
|
int targetLength = maxLength;
|
|
int bestSplitPos = -1;
|
|
int minDifference = text.length();
|
|
|
|
for (int pos : commaPositions) {
|
|
int leftLength = pos + 1; // Включаем запятую в левую часть
|
|
int rightLength = text.length() - pos - 1;
|
|
|
|
// Вычисляем разницу с целевой длиной
|
|
int leftDiff = abs(leftLength - targetLength);
|
|
int rightDiff = abs(rightLength - targetLength);
|
|
int totalDiff = leftDiff + rightDiff;
|
|
|
|
// Если эта позиция лучше предыдущей, запоминаем её
|
|
if (totalDiff < minDifference) {
|
|
minDifference = totalDiff;
|
|
bestSplitPos = pos;
|
|
}
|
|
}
|
|
|
|
// Если нашли подходящую запятую, разделяем по ней
|
|
if (bestSplitPos != -1) {
|
|
QString leftPart = text.left(bestSplitPos + 1).trimmed(); // Включаем запятую
|
|
QString rightPart = text.mid(bestSplitPos + 1).trimmed();
|
|
|
|
qDebug() << "PerechenTableController::splitComplexStringByMiddle: Найдена запятая на позиции" << bestSplitPos;
|
|
qDebug() << "PerechenTableController::splitComplexStringByMiddle: Левая часть:" << leftPart;
|
|
qDebug() << "PerechenTableController::splitComplexStringByMiddle: Правая часть:" << rightPart;
|
|
|
|
// Рекурсивно обрабатываем обе части
|
|
if (!leftPart.isEmpty()) {
|
|
QList<QString> leftParts = splitComplexStringByMiddle(leftPart, maxLength);
|
|
result.append(leftParts);
|
|
}
|
|
|
|
if (!rightPart.isEmpty()) {
|
|
QList<QString> rightParts = splitComplexStringByMiddle(rightPart, maxLength);
|
|
result.append(rightParts);
|
|
}
|
|
} else {
|
|
// Если не удалось найти подходящую запятую, используем обычное разделение
|
|
qDebug() << "PerechenTableController::splitComplexStringByMiddle: Не удалось найти подходящую запятую, используем splitBySpaces";
|
|
result = splitBySpaces(text, maxLength);
|
|
}
|
|
|
|
qDebug() << "PerechenTableController::splitComplexStringByMiddle: Результат:" << result;
|
|
return result;
|
|
}
|
|
|
|
QStringList PerechenTableController::collectPropertyNames()
|
|
{
|
|
QStringList propertyNames;
|
|
|
|
// Добавляем основные свойства компонента
|
|
propertyNames << "Designator";
|
|
|
|
// Собираем дополнительные свойства из компонентов
|
|
for (ComponentModel *component : m_components) {
|
|
QMap<QString, QString> props = component->properties();
|
|
for (auto it = props.begin(); it != props.end(); ++it) {
|
|
if (!propertyNames.contains(it.key())) {
|
|
propertyNames.append(it.key());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Если дополнительные свойства не найдены в компонентах, добавляем стандартные
|
|
if (propertyNames.size() == 1) { // Только Designator
|
|
propertyNames << "Name" << "Type" << "Value" << "Footprint" << "Description";
|
|
qDebug() << "PerechenTableController: Дополнительные свойства не найдены, используем стандартные:" << propertyNames;
|
|
}
|
|
|
|
// Добавляем специальное свойство для количества компонентов в группе
|
|
propertyNames << "Quantity" << "Кол.";
|
|
|
|
return propertyNames;
|
|
}
|
|
|
|
void PerechenTableController::setColumnMappings(const QStringList &mappings)
|
|
{
|
|
qDebug() << "PerechenTableController::setColumnMappings: Устанавливаем новые настройки колонок:" << mappings;
|
|
m_columnMappings = mappings;
|
|
|
|
// Сохраняем настройки в ProjectSettingsModel
|
|
if (m_projectSettingsModel) {
|
|
for (int i = 0; i < mappings.size(); ++i) {
|
|
m_projectSettingsModel->setColumnMapping("Perechen", i, mappings[i]);
|
|
}
|
|
qDebug() << "PerechenTableController::setColumnMappings: Настройки сохранены в ProjectSettingsModel";
|
|
}
|
|
|
|
// Обновляем настройки в модели
|
|
if (m_tableModel) {
|
|
m_tableModel->setColumnMappings(mappings);
|
|
qDebug() << "PerechenTableController::setColumnMappings: Настройки обновлены в модели";
|
|
|
|
// Проверяем, что настройки действительно применились
|
|
QStringList appliedMappings = m_tableModel->columnMappings();
|
|
qDebug() << "PerechenTableController::setColumnMappings: Проверка - настройки в модели:" << appliedMappings;
|
|
}
|
|
|
|
qDebug() << "PerechenTableController::setColumnMappings: Настройки колонок обновлены:" << m_columnMappings;
|
|
}
|
|
|
|
QStringList PerechenTableController::getColumnMappings() const
|
|
{
|
|
// Если есть ProjectSettingsModel, читаем настройки из него
|
|
if (m_projectSettingsModel) {
|
|
QStringList settings = m_projectSettingsModel->getColumnMappings("Perechen");
|
|
if (!settings.isEmpty()) {
|
|
qDebug() << "PerechenTableController::getColumnMappings: Получены настройки из ProjectSettingsModel:" << settings;
|
|
return settings;
|
|
}
|
|
}
|
|
|
|
// Fallback: возвращаем локальные настройки
|
|
qDebug() << "PerechenTableController::getColumnMappings: Возвращаем локальные настройки:" << m_columnMappings;
|
|
return m_columnMappings;
|
|
}
|
|
|
|
QStringList PerechenTableController::getAvailableProperties() const
|
|
{
|
|
QStringList properties = m_propertyNames;
|
|
properties.prepend("<Пусто>");
|
|
properties.prepend("<Авто>");
|
|
return properties;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QString PerechenTableController::getComponentType(ComponentModel *component)
|
|
{
|
|
if (!component) return "";
|
|
|
|
// Получаем тип компонента из маппинга дезигнаторов
|
|
if (m_designatorMappingModel) {
|
|
// Сначала пытаемся найти по полному дезигнатору (две буквы)
|
|
QString componentType = m_designatorMappingModel->getComponentType(component->designator(), true); // true для множественного числа
|
|
if (!componentType.isEmpty()) {
|
|
return componentType;
|
|
}
|
|
|
|
// Если не найдено по двум буквам, пытаемся найти по первой букве
|
|
QString designator = component->designator();
|
|
if (designator.length() >= 1) {
|
|
QString firstLetter = designator.left(1);
|
|
componentType = m_designatorMappingModel->getComponentType(firstLetter, true); // true для множественного числа
|
|
if (!componentType.isEmpty()) {
|
|
return componentType;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Если маппинг не найден, используем тип из свойств компонента
|
|
QMap<QString, QString> props = component->properties();
|
|
QString type = props.value("Type", "");
|
|
if (!type.isEmpty()) {
|
|
return type;
|
|
}
|
|
|
|
// Если и тип не найден, используем designator как fallback
|
|
return component->designator();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QPair<QString, QString> PerechenTableController::splitDesignator(const QString &designator)
|
|
{
|
|
if (designator.isEmpty()) {
|
|
return QPair<QString, QString>("", "");
|
|
}
|
|
|
|
// Используем регулярное выражение для разделения буквенной и цифровой части
|
|
QRegExp regex("([A-Za-z]+)(\\d*)");
|
|
if (regex.indexIn(designator) != -1) {
|
|
QString letterPart = regex.cap(1);
|
|
QString numberPart = regex.cap(2);
|
|
return QPair<QString, QString>(letterPart, numberPart);
|
|
}
|
|
|
|
// Fallback: если регулярное выражение не сработало,
|
|
// ищем первую цифру и разделяем по ней
|
|
for (int i = 0; i < designator.length(); ++i) {
|
|
if (designator[i].isDigit()) {
|
|
QString letterPart = designator.left(i);
|
|
QString numberPart = designator.mid(i);
|
|
return QPair<QString, QString>(letterPart, numberPart);
|
|
}
|
|
}
|
|
|
|
// Если цифр нет, возвращаем весь десигнатор как буквенную часть
|
|
return QPair<QString, QString>(designator, "");
|
|
}
|
|
|
|
// Новые методы для управления строками
|
|
void PerechenTableController::addEmptyRow()
|
|
{
|
|
if (m_tableModel) {
|
|
m_tableModel->addEmptyRow();
|
|
qDebug() << "PerechenTableController::addEmptyRow: Добавлена пустая строка";
|
|
|
|
// Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы"
|
|
// m_tableModel->optimizePageBreaks();
|
|
}
|
|
}
|
|
|
|
void PerechenTableController::addEmptyRowAt(int position)
|
|
{
|
|
if (m_tableModel) {
|
|
m_tableModel->addEmptyRowAt(position);
|
|
qDebug() << "PerechenTableController::addEmptyRowAt: Добавлена пустая строка в позицию" << position;
|
|
|
|
// Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы"
|
|
// m_tableModel->optimizePageBreaks();
|
|
}
|
|
}
|
|
|
|
void PerechenTableController::insertEmptyRowAt(int position)
|
|
{
|
|
if (m_tableModel) {
|
|
m_tableModel->addEmptyRowAt(position);
|
|
qDebug() << "PerechenTableController::insertEmptyRowAt: Вставлена пустая строка в позицию" << position;
|
|
|
|
// Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы"
|
|
// m_tableModel->optimizePageBreaks();
|
|
}
|
|
}
|
|
|
|
void PerechenTableController::removeRow(int row)
|
|
{
|
|
if (m_tableModel) {
|
|
m_tableModel->removeRow(row);
|
|
qDebug() << "PerechenTableController::removeRow: Удалена строка" << row;
|
|
|
|
// Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы"
|
|
// m_tableModel->optimizePageBreaks();
|
|
}
|
|
}
|
|
|
|
void PerechenTableController::clearTable()
|
|
{
|
|
if (m_tableModel) {
|
|
m_tableModel->clear();
|
|
qDebug() << "PerechenTableController::clearTable: Таблица очищена";
|
|
}
|
|
}
|
|
|
|
void PerechenTableController::optimizePageBreaks()
|
|
{
|
|
if (m_tableModel) {
|
|
qDebug() << "PerechenTableController::optimizePageBreaks: Оптимизируем разрывы страниц";
|
|
m_tableModel->optimizePageBreaks();
|
|
}
|
|
}
|
|
|
|
int PerechenTableController::getFontSize() const
|
|
{
|
|
if (m_projectSettingsModel) {
|
|
return m_projectSettingsModel->getFontSize("Perechen");
|
|
}
|
|
return 12; // По умолчанию
|
|
}
|
|
|
|
void PerechenTableController::setFontSize(int fontSize)
|
|
{
|
|
if (m_projectSettingsModel) {
|
|
m_projectSettingsModel->setFontSize("Perechen", fontSize);
|
|
}
|
|
}
|
|
|
|
int PerechenTableController::getFontStretch() const
|
|
{
|
|
if (m_projectSettingsModel) {
|
|
return m_projectSettingsModel->getFontStretch("Perechen");
|
|
}
|
|
return 100; // По умолчанию (нормальный)
|
|
}
|
|
|
|
void PerechenTableController::setFontStretch(int stretch)
|
|
{
|
|
if (m_projectSettingsModel) {
|
|
m_projectSettingsModel->setFontStretch("Perechen", stretch);
|
|
}
|
|
}
|
|
|
|
QList<QString> PerechenTableController::splitLongDesignation(const QString &designation, const QString originalString, ComponentModel *component, int maxLength)
|
|
{
|
|
QList<QString> result;
|
|
|
|
if (designation.length() <= maxLength) {
|
|
result.append(designation);
|
|
return result;
|
|
}
|
|
|
|
qDebug() << "PerechenTableController::splitLongDesignation: Разделяем длинное наименование:" << designation;
|
|
|
|
// Проверяем, является ли наименование выражением с частями
|
|
if (originalString.startsWith("=") && component) {
|
|
// Это выражение - разделяем по частям
|
|
QString expr = originalString.mid(1); // Убираем начальный "="
|
|
QStringList parts;
|
|
QString currentPart;
|
|
bool inDoubleQuotes = false;
|
|
bool inSingleQuotes = false;
|
|
|
|
for (int i = 0; i < expr.length(); ++i) {
|
|
QChar ch = expr[i];
|
|
|
|
if (ch == '"' && !inSingleQuotes) {
|
|
inDoubleQuotes = !inDoubleQuotes;
|
|
currentPart += ch;
|
|
} else if (ch == '\'' && !inDoubleQuotes) {
|
|
inSingleQuotes = !inSingleQuotes;
|
|
currentPart += ch;
|
|
} else if (ch == '+' && !inDoubleQuotes && !inSingleQuotes) {
|
|
// Это разделитель "+" вне кавычек
|
|
if (!currentPart.trimmed().isEmpty()) {
|
|
parts.append(currentPart.trimmed());
|
|
}
|
|
currentPart.clear();
|
|
} else {
|
|
currentPart += ch;
|
|
}
|
|
}
|
|
|
|
// Добавляем последнюю часть
|
|
if (!currentPart.trimmed().isEmpty()) {
|
|
parts.append(currentPart.trimmed());
|
|
}
|
|
|
|
qDebug() << "PerechenTableController::splitLongDesignation: Части выражения:" << parts;
|
|
|
|
// Сначала собираем все части в одну строку для комплексных строк
|
|
QString fullString;
|
|
|
|
for (int i = 0; i < parts.size(); ++i) {
|
|
QString trimmedPart = parts[i].trimmed();
|
|
if (!trimmedPart.isEmpty()) {
|
|
QString displayPart;
|
|
|
|
// Проверяем, является ли это литералом в одинарных кавычках
|
|
if (trimmedPart.startsWith("'") && trimmedPart.endsWith("'")) {
|
|
// Это литерал (запятая, пробел и т.д.)
|
|
displayPart = trimmedPart.mid(1, trimmedPart.length() - 2);
|
|
} else {
|
|
// Это имя свойства - получаем значение из компонента
|
|
QMap<QString, QString> props = component->properties();
|
|
qDebug() << "Обрабатываем свойство:" << trimmedPart;
|
|
|
|
if (trimmedPart == "Designator") {
|
|
displayPart = component->designator();
|
|
} else {
|
|
QString propertyName = trimmedPart.startsWith("\"") && trimmedPart.endsWith("\"")
|
|
? trimmedPart.mid(1, trimmedPart.length() - 2)
|
|
: trimmedPart;
|
|
displayPart = props.value(propertyName, "");
|
|
// Рекурсивно обрабатываем вложенные выражения
|
|
if (displayPart.startsWith("=")) {
|
|
qDebug() << "PerechenTableController::splitLongDesignation: Значение свойства является вложенным выражением, рекурсивно обрабатываем";
|
|
displayPart = parseExpression(component, displayPart);
|
|
}
|
|
}
|
|
}
|
|
|
|
fullString += displayPart;
|
|
}
|
|
}
|
|
|
|
qDebug() << "PerechenTableController::splitLongDesignation: Собранная полная строка:" << fullString << ", длина:" << fullString.length();
|
|
|
|
// Проверяем, содержит ли строка запятые (комплексная строка)
|
|
bool isComplexString = fullString.contains(',');
|
|
|
|
if (isComplexString && fullString.length() > maxLength) {
|
|
qDebug() << "PerechenTableController::splitLongDesignation: Комплексная строка с запятыми, делим по середине";
|
|
// Для комплексной строки делим по середине, а не по началу
|
|
result = splitComplexStringByMiddle(fullString, maxLength);
|
|
} else if (fullString.length() > maxLength) {
|
|
qDebug() << "PerechenTableController::splitLongDesignation: Строка длинная, разделяем по пробелам";
|
|
// Обычная длинная строка - разделяем по пробелам
|
|
result = splitBySpaces(fullString, maxLength);
|
|
} else {
|
|
qDebug() << "PerechenTableController::splitLongDesignation: Строка короткая, возвращаем как есть";
|
|
result.append(fullString);
|
|
}
|
|
} else {
|
|
// Обычный текст - разделяем по пробелам
|
|
result = splitBySpaces(designation, maxLength);
|
|
}
|
|
|
|
qDebug() << "PerechenTableController::splitLongDesignation: Результат разделения:" << result;
|
|
return result;
|
|
}
|
|
|
|
QList<QString> PerechenTableController::splitBySpaces(const QString &text, int maxLength)
|
|
{
|
|
QList<QString> result;
|
|
|
|
if (text.length() <= maxLength) {
|
|
result.append(text);
|
|
return result;
|
|
}
|
|
|
|
// Ищем пробел, ближайший к середине строки
|
|
int targetLength = maxLength;
|
|
int bestSplitPos = -1;
|
|
int minDifference = text.length(); // Минимальная разница с целевой длиной
|
|
|
|
// Ищем пробелы в тексте
|
|
for (int i = 0; i < text.length(); ++i) {
|
|
if (text[i] == ' ') {
|
|
int leftLength = i;
|
|
int rightLength = text.length() - i - 1;
|
|
|
|
// Вычисляем разницу с целевой длиной
|
|
int leftDiff = abs(leftLength - targetLength);
|
|
int rightDiff = abs(rightLength - targetLength);
|
|
int totalDiff = leftDiff + rightDiff;
|
|
|
|
// Если эта позиция лучше предыдущей, запоминаем её
|
|
if (totalDiff < minDifference) {
|
|
minDifference = totalDiff;
|
|
bestSplitPos = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Если нашли подходящий пробел, разделяем по нему
|
|
if (bestSplitPos != -1) {
|
|
QString leftPart = text.left(bestSplitPos).trimmed();
|
|
QString rightPart = text.mid(bestSplitPos + 1).trimmed();
|
|
|
|
// Рекурсивно обрабатываем обе части
|
|
if (!leftPart.isEmpty()) {
|
|
QList<QString> leftParts = splitBySpaces(leftPart, maxLength);
|
|
result.append(leftParts);
|
|
}
|
|
|
|
if (!rightPart.isEmpty()) {
|
|
QList<QString> rightParts = splitBySpaces(rightPart, maxLength);
|
|
result.append(rightParts);
|
|
}
|
|
} else {
|
|
// Если пробелов нет или не удалось найти подходящий, разбиваем посимвольно
|
|
// for (int i = 0; i < text.length(); i += maxLength) {
|
|
// result.append(text.mid(i, maxLength));
|
|
// }
|
|
result.append(text);
|
|
}
|
|
|
|
return result;
|
|
}
|