#include "perechentablecontroller.h" #include "../model/componentmodel.h" #include "../model/designatormappingmodel.h" #include #include 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 &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 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 partsA = PerechenTableController::splitDesignator(designatorA); QPair 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> groupedByLetter; for (ComponentModel *component : m_components) { QPair 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 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 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 props = component->properties(); prevOriginalString = props.value(m_columnMappings.value(0, "<Пусто>"), ""); prevComponent = component; } else { // Продолжаем текущую группу - обновляем последний десигнатор groupLastDesignator = component->designator(); groupCount++; } } // Добавляем последнюю группу if (groupCount > 0) { // Разделяем длинное наименование на несколько строк // Используем сохраненные значения для правильного разделения QList 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 props = component->properties(); QString originalString = props.value(m_columnMappings.value(0, "<Пусто>"), ""); QList 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 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 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 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 PerechenTableController::splitComplexStringByMiddle(const QString &text, int maxLength) { QList result; if (text.length() <= maxLength) { result.append(text); return result; } qDebug() << "PerechenTableController::splitComplexStringByMiddle: Разделяем комплексную строку:" << text; // Ищем запятые в тексте QList 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 leftParts = splitComplexStringByMiddle(leftPart, maxLength); result.append(leftParts); } if (!rightPart.isEmpty()) { QList 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 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 props = component->properties(); QString type = props.value("Type", ""); if (!type.isEmpty()) { return type; } // Если и тип не найден, используем designator как fallback return component->designator(); } QPair PerechenTableController::splitDesignator(const QString &designator) { if (designator.isEmpty()) { return QPair("", ""); } // Используем регулярное выражение для разделения буквенной и цифровой части QRegExp regex("([A-Za-z]+)(\\d*)"); if (regex.indexIn(designator) != -1) { QString letterPart = regex.cap(1); QString numberPart = regex.cap(2); return QPair(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(letterPart, numberPart); } } // Если цифр нет, возвращаем весь десигнатор как буквенную часть return QPair(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 PerechenTableController::splitLongDesignation(const QString &designation, const QString originalString, ComponentModel *component, int maxLength) { QList 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 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 PerechenTableController::splitBySpaces(const QString &text, int maxLength) { QList 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 leftParts = splitBySpaces(leftPart, maxLength); result.append(leftParts); } if (!rightPart.isEmpty()) { QList 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; }