mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-29 14:14:15 +02:00
08bce54a2f
Two problems with a custom variable newly added to a title-block template:
1. Find/Replace > Folio > Custom showed an empty table, so the user had to
know and type each variable name. It now lists every custom variable the
folios already carry plus those their templates use, with empty values.
An empty value now means "leave unchanged", like every field of the main
tab; only the variables actually filled in are written to the folios.
2. The title block showed the variable's own name ("%doc-type") until the
folio's properties were opened, because interpreteVariables() only
replaces names present in the context. Placeholders in the template text
that no key resolves now render blank, as auto-added unset ones already
did since #973. Only the template's own text is considered, so a value
that contains "%something" is never touched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
568 lines
18 KiB
C++
568 lines
18 KiB
C++
/*
|
|
Copyright 2006-2026 The QElectroTech Team
|
|
This file is part of QElectroTech.
|
|
|
|
QElectroTech is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
QElectroTech is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "searchandreplaceworker.h"
|
|
#include "../qetproject.h"
|
|
#include "../QPropertyUndoCommand/qpropertyundocommand.h"
|
|
#include "../diagram.h"
|
|
#include "../diagramcommands.h"
|
|
#include "../qetapp.h"
|
|
#include "../qetgraphicsitem/element.h"
|
|
#include "../qetgraphicsitem/independenttextitem.h"
|
|
#include "../qetinformation.h"
|
|
#include "../undocommand/changeelementinformationcommand.h"
|
|
#include "../undocommand/changetitleblockcommand.h"
|
|
|
|
SearchAndReplaceWorker::SearchAndReplaceWorker()
|
|
{
|
|
m_conductor_properties = invalidConductorProperties();
|
|
}
|
|
|
|
/**
|
|
@brief SearchAndReplaceWorker::replaceDiagram
|
|
Replace all properties of each diagram in diagram_list,
|
|
by the current titleblock properties of this worker
|
|
@param diagram_list : list of diagram to be changed,
|
|
all diagrams must belong to the same project;
|
|
*/
|
|
void SearchAndReplaceWorker::replaceDiagram(QList<Diagram *> diagram_list)
|
|
{
|
|
if (diagram_list.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
QETProject *project = diagram_list.first()->project();
|
|
for (Diagram *d : diagram_list) {
|
|
if (d->project() != project) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
QUndoStack *us = project->undoStack();
|
|
us->beginMacro(QObject::tr("Chercher/remplacer les propriétés de folio"));
|
|
for (Diagram *d : diagram_list)
|
|
{
|
|
TitleBlockProperties old_propertie = d->border_and_titleblock.exportTitleBlock();
|
|
TitleBlockProperties new_properties = old_propertie;
|
|
|
|
new_properties.title = applyChange(new_properties.title, m_titleblock_properties.title);
|
|
new_properties.author = applyChange(new_properties.author, m_titleblock_properties.author);
|
|
new_properties.filename = applyChange(new_properties.filename, m_titleblock_properties.filename);
|
|
new_properties.plant = applyChange(new_properties.plant, m_titleblock_properties.plant);
|
|
new_properties.locmach = applyChange(new_properties.locmach, m_titleblock_properties.locmach);
|
|
new_properties.indexrev = applyChange(new_properties.indexrev, m_titleblock_properties.indexrev);
|
|
new_properties.folio = applyChange(new_properties.folio, m_titleblock_properties.folio);
|
|
|
|
if (m_titleblock_properties.date.isValid())
|
|
{
|
|
if (m_titleblock_properties.date == eraseDate()) {
|
|
new_properties.date = QDate();
|
|
} else {
|
|
new_properties.date = m_titleblock_properties.date;
|
|
}
|
|
}
|
|
|
|
// The replace dialog lists every custom variable of the folios
|
|
// (#1113), so an empty value means "leave unchanged", exactly like
|
|
// the fields of the main tab.
|
|
const DiagramContext &change = m_titleblock_properties.context;
|
|
for (const QString &key : change.keys()) {
|
|
const QString value = change.value(key).toString();
|
|
if (value.isEmpty()) {
|
|
continue;
|
|
}
|
|
new_properties.context.addValue(
|
|
key,
|
|
applyChange(new_properties.context.value(key).toString(),
|
|
value));
|
|
}
|
|
|
|
if (old_propertie != new_properties) {
|
|
project->undoStack()->push(new ChangeTitleBlockCommand(d, old_propertie, new_properties));
|
|
}
|
|
}
|
|
us->endMacro();
|
|
}
|
|
|
|
void SearchAndReplaceWorker::replaceDiagram(Diagram *diagram)
|
|
{
|
|
QList<Diagram *> list;
|
|
list.append(diagram);
|
|
replaceDiagram(list);
|
|
}
|
|
|
|
/**
|
|
@brief SearchAndReplaceWorker::replaceElement
|
|
Replace all properties of each elements in list
|
|
All element must belong to the same project,
|
|
if not this function do nothing.
|
|
All change are made through a undo command append
|
|
to undo list of the project.
|
|
@param list
|
|
*/
|
|
void SearchAndReplaceWorker::replaceElement(QList<Element *> list)
|
|
{
|
|
if (list.isEmpty() || !list.first()->diagram()) {
|
|
return;
|
|
}
|
|
|
|
QETProject *project_ = list.first()->diagram()->project();
|
|
for (Element *elmt : list)
|
|
{
|
|
if (elmt->diagram()) {
|
|
if (elmt->diagram()->project() != project_) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
project_->undoStack()->beginMacro(QObject::tr("Chercher/remplacer les propriétés d'éléments."));
|
|
for (Element *elmt : list)
|
|
{
|
|
//We apply change only for master, slave, and terminal element.
|
|
if (elmt->linkType() == Element::Master ||
|
|
elmt->linkType() == Element::Simple ||
|
|
elmt->linkType() == Element::Terminale ||
|
|
elmt->linkType() == Element::Thumbnail)
|
|
{
|
|
DiagramContext old_context;
|
|
DiagramContext new_context = old_context = elmt->elementInformations();
|
|
for (QString key : QETInformation::elementInfoKeys())
|
|
{
|
|
new_context.addValue(key, applyChange(old_context.value(key).toString(),
|
|
m_element_context.value(key).toString()));
|
|
}
|
|
|
|
if (old_context != new_context)
|
|
{
|
|
ChangeElementInformationCommand *undo = new ChangeElementInformationCommand(elmt, old_context, new_context);
|
|
project_->undoStack()->push(undo);
|
|
}
|
|
}
|
|
}
|
|
project_->undoStack()->endMacro();
|
|
}
|
|
|
|
void SearchAndReplaceWorker::replaceElement(Element *element)
|
|
{
|
|
QList<Element *>list;
|
|
list.append(element);
|
|
replaceElement(list);
|
|
}
|
|
|
|
/**
|
|
@brief SearchAndReplaceWorker::replaceIndiText
|
|
Replace all displayed text of independent text of list
|
|
Each must belong to the same project, if not this function do nothing
|
|
@param list
|
|
*/
|
|
void SearchAndReplaceWorker::replaceIndiText(QList<IndependentTextItem *> list)
|
|
{
|
|
if (list.isEmpty() || !list.first()->diagram()) {
|
|
return;
|
|
}
|
|
QETProject *project_ = list.first()->diagram()->project();
|
|
for (IndependentTextItem *text : list) {
|
|
if (!text->diagram() ||
|
|
text->diagram()->project() != project_) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
project_->undoStack()->beginMacro(QObject::tr("Chercher/remplacer des textes independants"));
|
|
for (IndependentTextItem *text : list)
|
|
{
|
|
QString before = text->toPlainText();
|
|
text->setPlainText(m_indi_text);
|
|
project_->undoStack()->push(new ChangeDiagramTextCommand(text, before, m_indi_text));
|
|
}
|
|
project_->undoStack()->endMacro();
|
|
}
|
|
|
|
void SearchAndReplaceWorker::replaceIndiText(IndependentTextItem *text)
|
|
{
|
|
QList<IndependentTextItem *>list;
|
|
list.append(text);
|
|
replaceIndiText(list);
|
|
}
|
|
|
|
/**
|
|
@brief SearchAndReplaceWorker::replaceConductor
|
|
Replace all properties of each conductor in list
|
|
All conductor must belong to the same project,
|
|
if not this function do nothing.
|
|
All change are made through a undo command append
|
|
to undo list of the project.
|
|
@param list
|
|
*/
|
|
void SearchAndReplaceWorker::replaceConductor(QList<Conductor *> list)
|
|
{
|
|
if (list.isEmpty() || !list.first()->diagram()) {
|
|
return;
|
|
}
|
|
|
|
QETProject *project_ = list.first()->diagram()->project();
|
|
for (Conductor *c : list) {
|
|
if (!c->diagram() ||
|
|
c->diagram()->project() != project_) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
project_->undoStack()->beginMacro(QObject::tr("Chercher/remplacer les propriétés de conducteurs."));
|
|
for (Conductor *c : list)
|
|
{
|
|
ConductorProperties cp = applyChange(c->properties(), m_conductor_properties);
|
|
|
|
if (cp != c->properties())
|
|
{
|
|
QSet <Conductor *> conductors_list = c->relatedPotentialConductors(true);
|
|
conductors_list << c;
|
|
for (Conductor *cc : conductors_list)
|
|
{
|
|
QVariant old_value, new_value;
|
|
old_value.setValue(cc->properties());
|
|
new_value.setValue(cp);
|
|
project_->undoStack()->push(new QPropertyUndoCommand(cc, "properties", old_value, new_value));
|
|
}
|
|
}
|
|
}
|
|
project_->undoStack()->endMacro();
|
|
}
|
|
|
|
void SearchAndReplaceWorker::replaceConductor(Conductor *conductor)
|
|
{
|
|
QList<Conductor *>list;
|
|
list.append(conductor);
|
|
replaceConductor(list);
|
|
}
|
|
|
|
/**
|
|
@brief SearchAndReplaceWorker::replaceAdvanced
|
|
Apply the change of text according to the current advancedStruct
|
|
All items in the 4 list must belong to the same QETProject,
|
|
if not this function do nothing
|
|
@param diagrams :
|
|
@param elements :
|
|
@param texts :
|
|
@param conductors :
|
|
*/
|
|
void SearchAndReplaceWorker::replaceAdvanced(
|
|
QList<Diagram *> diagrams,
|
|
QList<Element *> elements,
|
|
QList<IndependentTextItem *> texts,
|
|
QList<Conductor *> conductors)
|
|
{
|
|
QETProject *project_ = nullptr;
|
|
|
|
//Some test to check if a least one list have one item
|
|
//and if all items belong to the same project
|
|
if (!diagrams.isEmpty()) {
|
|
project_ = diagrams.first()->project();
|
|
} else if (!elements.isEmpty() && elements.first()->diagram()) {
|
|
project_ = elements.first()->diagram()->project();
|
|
} else if (!texts.isEmpty() && texts.first()->diagram()) {
|
|
project_ = texts.first()->diagram()->project();
|
|
} else if (!conductors.isEmpty() && conductors.first()->diagram()) {
|
|
project_ = conductors.first()->diagram()->project();
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
for (Diagram *dd : diagrams) {
|
|
if (dd->project() != project_) {
|
|
return;
|
|
}
|
|
}
|
|
for (Element *elmt : elements) {
|
|
if (!elmt->diagram() || elmt->diagram()->project() != project_) {
|
|
return;
|
|
}
|
|
}
|
|
for (IndependentTextItem *text : texts) {
|
|
if (!text->diagram() || text->diagram()->project() != project_) {
|
|
return;
|
|
}
|
|
}
|
|
for (Conductor *cc : conductors) {
|
|
if (!cc->diagram() || cc->diagram()->project() != project_) {
|
|
return;
|
|
}
|
|
}
|
|
//The end of the test
|
|
|
|
int who = m_advanced_struct.who;
|
|
if (who == -1) {
|
|
return;
|
|
}
|
|
|
|
project_->undoStack()->beginMacro(QObject::tr("Rechercher / remplacer avancé"));
|
|
if (who == 0)
|
|
{
|
|
for (Diagram *diagram : diagrams)
|
|
{
|
|
TitleBlockProperties old_properties = diagram->border_and_titleblock.exportTitleBlock();
|
|
TitleBlockProperties new_properties = replaceAdvanced(diagram);
|
|
if (old_properties != new_properties) {
|
|
project_->undoStack()->push(new ChangeTitleBlockCommand(diagram, old_properties, new_properties));
|
|
}
|
|
}
|
|
}
|
|
else if (who == 1)
|
|
{
|
|
for (Element *element : elements)
|
|
{
|
|
DiagramContext old_context = element->elementInformations();
|
|
DiagramContext new_context = replaceAdvanced(element);
|
|
if (old_context != new_context) {
|
|
project_->undoStack()->push(new ChangeElementInformationCommand(element, old_context, new_context));
|
|
}
|
|
}
|
|
}
|
|
else if (who == 2)
|
|
{
|
|
for (Conductor *conductor : conductors)
|
|
{
|
|
ConductorProperties old_properties = conductor->properties();
|
|
ConductorProperties new_properties = replaceAdvanced(conductor);
|
|
if (old_properties != new_properties)
|
|
{
|
|
QSet <Conductor *> potential_conductors = conductor->relatedPotentialConductors(true);
|
|
potential_conductors << conductor;
|
|
|
|
for (Conductor *c : potential_conductors)
|
|
{
|
|
QVariant old_value, new_value;
|
|
old_value.setValue(c->properties());
|
|
new_value.setValue(new_properties);
|
|
project_->undoStack()->push(new QPropertyUndoCommand(c, "properties", old_value, new_value));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (who == 3)
|
|
{
|
|
for (IndependentTextItem *text : texts)
|
|
{
|
|
QRegularExpression rx(m_advanced_struct.search);
|
|
if (!rx.isValid())
|
|
{
|
|
qWarning() <<QObject::tr("this is an error in the code")
|
|
<< rx.errorString()
|
|
<< rx.patternErrorOffset();
|
|
}
|
|
QString replace = m_advanced_struct.replace;
|
|
QString after = text->toPlainText();
|
|
after = after.replace(rx, replace);
|
|
|
|
if (after != text->toPlainText()) {
|
|
project_->undoStack()->push(new ChangeDiagramTextCommand(text, text->toPlainText(), after));
|
|
}
|
|
}
|
|
}
|
|
project_->undoStack()->endMacro();
|
|
}
|
|
|
|
/**
|
|
@brief SearchAndReplaceWorker::setupLineEdit
|
|
With search and replace, when the variable to edit is a text,
|
|
the editor is always the same no matter if it is for a folio,
|
|
element or conductor.
|
|
The editor is a QLineEdit to edit the text
|
|
and checkbox to erase the text if checked.
|
|
This function fill the editor, from the current string
|
|
@param l
|
|
@param cb
|
|
@param str
|
|
*/
|
|
void SearchAndReplaceWorker::setupLineEdit(QLineEdit *l,
|
|
QCheckBox *cb,
|
|
QString str)
|
|
{
|
|
l->setText(str);
|
|
cb->setChecked(str == eraseText() ? true : false);
|
|
l->setDisabled(str == eraseText() ? true : false);
|
|
}
|
|
|
|
ConductorProperties SearchAndReplaceWorker::invalidConductorProperties()
|
|
{
|
|
ConductorProperties cp;
|
|
|
|
//init with invalid value the conductor properties
|
|
cp.text_size = 0;
|
|
cp.text.clear();
|
|
cp.m_vertical_alignment = Qt::AlignAbsolute;
|
|
cp.m_horizontal_alignment = Qt::AlignAbsolute;
|
|
cp.verti_rotate_text = -1;
|
|
cp.horiz_rotate_text = -1;
|
|
cp.color = QColor();
|
|
cp.style = Qt::NoPen;
|
|
cp.cond_size = 0;
|
|
cp.m_color_2 = QColor();
|
|
cp.m_dash_size = 0;
|
|
|
|
return cp;
|
|
}
|
|
|
|
/**
|
|
@brief SearchAndReplaceWorker::applyChange
|
|
@param original : the original properties
|
|
@param change : the change properties, to be merged with original
|
|
@return a new conductor properties with the change applyed.
|
|
*/
|
|
ConductorProperties SearchAndReplaceWorker::applyChange(
|
|
const ConductorProperties &original,
|
|
const ConductorProperties &change)
|
|
{
|
|
ConductorProperties new_properties = original;
|
|
|
|
if (change.text_size > 2) {new_properties.text_size = change.text_size;}
|
|
new_properties.m_formula = applyChange(new_properties.m_formula, change.m_formula);
|
|
new_properties.text = applyChange(new_properties.text, change.text);
|
|
new_properties.m_show_text = change.m_show_text;
|
|
new_properties.m_function = applyChange(new_properties.m_function, change.m_function);
|
|
new_properties.m_tension_protocol = applyChange(new_properties.m_tension_protocol, change.m_tension_protocol);
|
|
new_properties.m_wire_color = applyChange(new_properties.m_wire_color, change.m_wire_color);
|
|
new_properties.m_wire_section = applyChange(new_properties.m_wire_section, change.m_wire_section);
|
|
if(change.m_vertical_alignment == Qt::AlignLeft ||
|
|
change.m_vertical_alignment == Qt::AlignRight) {new_properties.m_vertical_alignment = change.m_vertical_alignment;}
|
|
if(change.m_horizontal_alignment == Qt::AlignTop ||
|
|
change.m_horizontal_alignment == Qt::AlignBottom) {new_properties.m_horizontal_alignment = change.m_horizontal_alignment;}
|
|
if (change.verti_rotate_text >= 0) {new_properties.verti_rotate_text = change.verti_rotate_text;}
|
|
if (change.horiz_rotate_text >= 0) {new_properties.horiz_rotate_text = change.horiz_rotate_text;}
|
|
if (change.color.isValid()) {new_properties.color = change.color;}
|
|
if (change.style != Qt::NoPen) {new_properties.style = change.style;}
|
|
if (change.cond_size >= 0.4) {new_properties.cond_size = change.cond_size;}
|
|
new_properties.m_bicolor = change.m_bicolor;
|
|
if (change.m_color_2.isValid()) {new_properties.m_color_2 = change.m_color_2;}
|
|
if (change.m_dash_size >= 2) {new_properties.m_dash_size = change.m_dash_size;}
|
|
new_properties.singleLineProperties = change.singleLineProperties;
|
|
|
|
return new_properties;
|
|
}
|
|
|
|
/**
|
|
@brief SearchAndReplaceWorker::applyChange
|
|
@param original : the original string
|
|
@param change : the changed string:
|
|
@return the string to be use in the properties
|
|
*/
|
|
QString SearchAndReplaceWorker::applyChange(const QString &original,
|
|
const QString &change)
|
|
{
|
|
if (change.isEmpty()) {return original;}
|
|
else if (change == eraseText()) {return QString();}
|
|
else {return change;}
|
|
}
|
|
|
|
/**
|
|
@brief SearchAndReplaceWorker::replaceAdvanced
|
|
@param diagram
|
|
@return the titleblock properties with the change applied,
|
|
according to the state of m_advanced_struct
|
|
*/
|
|
TitleBlockProperties SearchAndReplaceWorker::replaceAdvanced(Diagram *diagram)
|
|
{
|
|
TitleBlockProperties p = diagram->border_and_titleblock.exportTitleBlock();
|
|
|
|
if (m_advanced_struct.who == 0)
|
|
{
|
|
QRegularExpression rx(m_advanced_struct.search);
|
|
if (!rx.isValid())
|
|
{
|
|
qWarning() <<QObject::tr("this is an error in the code")
|
|
<< rx.errorString()
|
|
<< rx.patternErrorOffset();
|
|
}
|
|
QString replace = m_advanced_struct.replace;
|
|
QString what = m_advanced_struct.what;
|
|
if (what == "title") {p.title = p.title.replace(rx, replace);}
|
|
else if (what == "author") {p.author = p.author.replace(rx, replace);}
|
|
else if (what == "filename") {p.filename = p.filename.replace(rx, replace);}
|
|
else if (what == "folio") {p.folio = p.folio.replace(rx, replace);}
|
|
else if (what == "plant") {p.plant = p.plant.replace(rx, replace);}
|
|
else if (what == "locmach") {p.locmach = p.locmach.replace(rx, replace);}
|
|
else if (what == "indexrev") {p.indexrev = p.indexrev.replace(rx, replace);}
|
|
}
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
@brief SearchAndReplaceWorker::replaceAdvanced
|
|
@param element
|
|
@return The diagram context with the change applied,
|
|
according to the state of m_advanced_struct
|
|
*/
|
|
DiagramContext SearchAndReplaceWorker::replaceAdvanced(Element *element)
|
|
{
|
|
DiagramContext context = element->elementInformations();
|
|
|
|
if (m_advanced_struct.who == 1)
|
|
{
|
|
QString what = m_advanced_struct.what;
|
|
if (context.contains(what))
|
|
{
|
|
QRegularExpression rx(m_advanced_struct.search);
|
|
if (!rx.isValid())
|
|
{
|
|
qWarning() <<QObject::tr("this is an error in the code")
|
|
<< rx.errorString()
|
|
<< rx.patternErrorOffset();
|
|
}
|
|
QString replace = m_advanced_struct.replace;
|
|
QString value = context[what].toString();
|
|
context.addValue(what, value.replace(rx, replace));
|
|
}
|
|
}
|
|
|
|
return context;
|
|
}
|
|
|
|
/**
|
|
@brief SearchAndReplaceWorker::replaceAdvanced
|
|
@param conductor
|
|
@return the conductor properties with the change applied,
|
|
according to the state of m_advanced_struct
|
|
*/
|
|
ConductorProperties SearchAndReplaceWorker::replaceAdvanced(
|
|
Conductor *conductor)
|
|
{
|
|
ConductorProperties properties = conductor->properties();
|
|
|
|
if (m_advanced_struct.who == 2)
|
|
{
|
|
QRegularExpression rx(m_advanced_struct.search);
|
|
if (!rx.isValid())
|
|
{
|
|
qWarning() <<QObject::tr("this is an error in the code")
|
|
<< rx.errorString()
|
|
<< rx.patternErrorOffset();
|
|
}
|
|
QString what = m_advanced_struct.what;
|
|
QString replace = m_advanced_struct.replace;
|
|
|
|
if (what == "formula") {properties.m_formula.replace(rx, replace);}
|
|
else if (what == "text") {properties.text.replace(rx, replace);}
|
|
else if (what == "function") {properties.m_function.replace(rx, replace);}
|
|
else if (what == "tension/protocol") {properties.m_tension_protocol.replace(rx, replace);}
|
|
else if (what == "conductor_color") {properties.m_wire_color.replace(rx, replace);}
|
|
else if (what == "conductor_section") {properties.m_wire_section.replace(rx, replace);}
|
|
}
|
|
|
|
return properties;
|
|
}
|