achim f20ea041b6 correcting the visibility of Variables in CompositeText
When using composite text in report elements, the name of the variable
was displayed when inserting the reportElement into the drawing (e.g.
%{function}). This is corrected here.
Add missing variables to assignvariables.cpp
2025-08-02 22:16:12 +02:00

1515 lines
48 KiB
C++

/*
Copyright 2006-2025 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 "dynamicelementtextitem.h"
#include "../QPropertyUndoCommand/qpropertyundocommand.h"
#include "../diagram.h"
#include "../qetapp.h"
#include "../qetgraphicsitem/conductor.h"
#include "../qetgraphicsitem/terminal.h"
#include "../qetinformation.h"
#include "crossrefitem.h"
#include "element.h"
#include "elementtextitemgroup.h"
#include <QDomDocument>
#include <QDomElement>
#include <QGraphicsSceneMouseEvent>
/**
@brief DynamicElementTextItem::DynamicElementTextItem
Constructor
@param parent_element
*/
DynamicElementTextItem::DynamicElementTextItem(Element *parent_element) :
m_parent_element(parent_element),
m_uuid(QUuid::createUuid())
{
setFont(QETApp::dynamicTextsItemFont());
setText(tr("Texte"));
setParentItem(parent_element);
QSettings settings;
setRotation(settings.value("dynamic_text_rotation", 0).toInt());
setKeepVisualRotation(true);
setTextWidth(settings.value("dynamic_text_width", -1).toInt());
connect(this, &DynamicElementTextItem::textEdited, [this](const QString &old_str, const QString &new_str)
{
if(this->m_parent_element && this->m_parent_element->diagram())
{
QUndoCommand *undo = new QPropertyUndoCommand(this, "text", old_str, new_str);
undo->setText(tr("Éditer un texte d'élément"));
this->m_parent_element->diagram()->undoStack().push(undo);
}
});
//Option when text is displayed in multiple line
QTextOption option = document()->defaultTextOption();
option.setAlignment(Qt::AlignHCenter);
option.setWrapMode(QTextOption::WordWrap);
document()->setDefaultTextOption(option);
}
DynamicElementTextItem::~DynamicElementTextItem()
{}
/**
@brief DynamicElementTextItem::textFromMetaEnum
@return The QMetaEnum of the enum TextFrom
*/
QMetaEnum DynamicElementTextItem::textFromMetaEnum()
{
DynamicElementTextItem deti;
return deti.metaObject()->enumerator(deti.metaObject()->indexOfEnumerator("TextFrom"));
}
DynamicElementTextItem::DynamicElementTextItem()
{}
/**
@brief DynamicElementTextItem::toXml
Export this text to xml
@param dom_doc
@return
*/
QDomElement DynamicElementTextItem::toXml(QDomDocument &dom_doc) const
{
QDomElement root_element = dom_doc.createElement(xmlTagName());
root_element.setAttribute("x", QString::number(pos().x()));
root_element.setAttribute("y", QString::number(pos().y()));
root_element.setAttribute("rotation", QString::number(QET::correctAngle(rotation())));
root_element.setAttribute("uuid", m_uuid.toString());
root_element.setAttribute("frame", m_frame? "true" : "false");
root_element.setAttribute("text_width", QString::number(m_text_width));
root_element.setAttribute("font", font().toString());
root_element.setAttribute("keep_visual_rotation", m_keep_visual_rotation ? "true" : "false");
QMetaEnum me = textFromMetaEnum();
root_element.setAttribute("text_from", me.valueToKey(m_text_from));
me = QMetaEnum::fromType<Qt::Alignment>();
if(this->alignment() &Qt::AlignRight)
root_element.setAttribute("Halignment", me.valueToKey(Qt::AlignRight));
else if(this->alignment() &Qt::AlignLeft)
root_element.setAttribute("Halignment", me.valueToKey(Qt::AlignLeft));
else if(this->alignment() &Qt::AlignHCenter)
root_element.setAttribute("Halignment", me.valueToKey(Qt::AlignHCenter));
if(this->alignment() &Qt::AlignBottom)
root_element.setAttribute("Valignment", me.valueToKey(Qt::AlignBottom));
else if(this->alignment() & Qt::AlignTop)
root_element.setAttribute("Valignment", me.valueToKey(Qt::AlignTop));
else if(this->alignment() &Qt::AlignVCenter)
root_element.setAttribute("Valignment", me.valueToKey(Qt::AlignVCenter));
QDomElement dom_text = dom_doc.createElement("text");
dom_text.appendChild(dom_doc.createTextNode(toPlainText()));
root_element.appendChild(dom_text);
//Info name
if(!m_info_name.isEmpty())
{
QDomElement dom_info_name = dom_doc.createElement("info_name");
dom_info_name.appendChild(dom_doc.createTextNode(m_info_name));
root_element.appendChild(dom_info_name);
}
//Composite text
if(!m_composite_text.isEmpty())
{
QDomElement dom_comp_text = dom_doc.createElement("composite_text");
dom_comp_text.appendChild(dom_doc.createTextNode(m_composite_text));
root_element.appendChild(dom_comp_text);
}
//Color
if(color() != QColor(Qt::black))
{
QDomElement dom_color = dom_doc.createElement("color");
dom_color.appendChild(dom_doc.createTextNode(color().name()));
root_element.appendChild(dom_color);
}
return root_element;
}
/**
@brief DynamicElementTextItem::fromXml
Import this text from xml
@param dom_elmt
*/
void DynamicElementTextItem::fromXml(const QDomElement &dom_elmt)
{
if (dom_elmt.tagName() != xmlTagName()) {
qDebug() << "DynamicElementTextItem::fromXml : Wrong tagg name";
return;
}
QGraphicsTextItem::setRotation(dom_elmt.attribute("rotation", QString::number(0)).toDouble());
setKeepVisualRotation(dom_elmt.attribute("keep_visual_rotation", "true") == "true"? true : false);
if (dom_elmt.hasAttribute("font"))
{
QFont font;
font.fromString(dom_elmt.attribute("font"));
setFont(font);
}
else //Retrocompatibility during the 0.7 dev because the font property was added lately. TODO remove this part in futur
{
QFont font_(dom_elmt.attribute("font_family", font().family()),
dom_elmt.attribute("font_size", QString::number(9)).toInt());
font_.setStyleName(dom_elmt.attribute("dynamicitemstyle", font().styleName()));
setFont(font_);
}
m_uuid = QUuid(dom_elmt.attribute("uuid", QUuid::createUuid().toString()));
setFrame(dom_elmt.attribute("frame", "false") == "true"? true : false);
setTextWidth(dom_elmt.attribute("text_width", QString::number(-1)).toDouble());
//Text from
QMetaEnum me = textFromMetaEnum();
setTextFrom(DynamicElementTextItem::TextFrom(me.keyToValue(dom_elmt.attribute("text_from").toStdString().data())));
me = QMetaEnum::fromType<Qt::Alignment>();
if(dom_elmt.hasAttribute("Halignment"))
setAlignment(Qt::Alignment(me.keyToValue(dom_elmt.attribute("Halignment").toStdString().data())));
if(dom_elmt.hasAttribute(("Valignment")))
setAlignment(Qt::Alignment(me.keyToValue(dom_elmt.attribute("Valignment").toStdString().data())) | this->alignment());
//Text
QDomElement dom_text = dom_elmt.firstChildElement("text");
//Clear the current text.
setPlainText("");
if (!dom_text.isNull()) {
setText(dom_text.text());
}
//Info name
QDomElement dom_info_name = dom_elmt.firstChildElement("info_name");
if(!dom_info_name.isNull())
setInfoName(dom_info_name.text());
//Composite text
QDomElement dom_comp_text = dom_elmt.firstChildElement("composite_text");
if(!dom_comp_text.isNull())
setCompositeText(dom_comp_text.text());
//Color
QDomElement dom_color = dom_elmt.firstChildElement("color");
if(!dom_color.isNull())
setColor(QColor(dom_color.text()));
//Force the update of the displayed text
setTextFrom(m_text_from);
QGraphicsTextItem::setPos(dom_elmt.attribute("x", QString::number(0)).toDouble(),
dom_elmt.attribute("y", QString::number(0)).toDouble());
}
/**
@brief DynamicElementTextItem::ParentElement
@return a pointer to the parent element. Note the pointer can be null.
Note that the text can return a parent element,
even if the text belong to a group of this same element.
*/
Element *DynamicElementTextItem::parentElement() const
{
return m_parent_element;
}
/**
@brief DynamicElementTextItem::parentGroup
@return The group where this text belong, if this item
is note in a group, return nullptr.
*/
ElementTextItemGroup *DynamicElementTextItem::parentGroup() const
{
if(parentItem())
{
if(ElementTextItemGroup *grp = dynamic_cast<ElementTextItemGroup *>(parentItem()))
return grp;
}
return nullptr;
}
/**
@brief DynamicElementTextItem::elementUseForInfo
@return a pointer to the element we must use for the variable information.
If this text is owned by a simple element, the simple element is returned, this is the same element returned by the function parentElement().
If this text is owned by a master element, the master element is returned, this is the same element returned by the function parentElement().
If this text is owned by a report element, the report element is returned, this is the same element returned by the function parentElement().
If this text is owned by a terminal element, the terminal element is returned, this is the same element returned by the function parentElement().
If this text is owned by a slave element, we return the master element set as master of the parent slave element,
if the parent slave is not linked to a master, this function return a nullptr.
If this text have no parent element, return nullptr
*/
Element *DynamicElementTextItem::elementUseForInfo() const
{
Element *elmt = parentElement();
if(!elmt)
return nullptr;
switch (elmt->linkType())
{
case Element::Simple:
return elmt;
case Element::Thumbnail:
return elmt;
case Element::NextReport:
return elmt;
case Element::PreviousReport:
return elmt;
case Element::Master:
return elmt;
case Element::Slave:
{
if(elmt->linkedElements().isEmpty())
return nullptr;
else
return elmt->linkedElements().first();
}
case Element::Terminale:
return elmt;
default:
return elmt;
}
}
/**
@brief DynamicElementTextItem::refreshLabelConnection
Refresh the connection of this text when the source of text is label,
or composite text, with a variable %{label}
*/
void DynamicElementTextItem::refreshLabelConnection()
{
if ((m_text_from == ElementInfo && m_info_name == "label") ||
(m_text_from == CompositeText && m_composite_text.contains("%{label}")))
{
if(m_parent_element.data()->linkType() & Element::AllReport)
{
updateReportFormulaConnection();
updateReportText();
}
else
{
setupFormulaConnection();
updateLabel();
}
}
}
/**
@brief DynamicElementTextItem::textFrom
@return what the final text is created from.
*/
DynamicElementTextItem::TextFrom DynamicElementTextItem::textFrom() const
{
return m_text_from;
}
/**
@brief DynamicElementTextItem::setTextFrom
Set the final text is created from.
@param text_from
*/
void DynamicElementTextItem::setTextFrom(DynamicElementTextItem::TextFrom text_from)
{
if(m_text_from == text_from)
return;
setNoEditable(text_from == UserText? false : true);
clearFormulaConnection();
TextFrom old_text_from = m_text_from;
m_text_from = text_from;
if(m_text_from == UserText)
{
setPlainText(m_text);
disconnect(m_parent_element.data(), &Element::elementInfoChange, this, &DynamicElementTextItem::elementInfoChanged);
}
else if (m_text_from == ElementInfo && elementUseForInfo())
{
if(m_info_name == "label")
{
setupFormulaConnection();
updateLabel();
}
else
setPlainText(elementUseForInfo()->elementInformations().value(m_info_name).toString());
if(old_text_from == UserText)
connect(elementUseForInfo(), &Element::elementInfoChange, this, &DynamicElementTextItem::elementInfoChanged);
}
else if (m_text_from == CompositeText && elementUseForInfo())
{
if(m_composite_text.contains("%{label}"))
{
setupFormulaConnection();
updateLabel();
}
else
setPlainText(autonum::AssignVariables::replaceVariable(m_composite_text, elementUseForInfo()->elementInformations()));
if(old_text_from == UserText)
connect(elementUseForInfo(), &Element::elementInfoChange, this, &DynamicElementTextItem::elementInfoChanged);
}
if(m_parent_element.data()->linkType() == Element::Master ||
m_parent_element.data()->linkType() == Element::Slave)
updateXref();
emit textFromChanged(m_text_from);
}
/**
@brief DynamicElementTextItem::text
@return the text of this text
*/
QString DynamicElementTextItem::text() const
{
return m_text;
}
/**
@brief DynamicElementTextItem::setText
Set the text of this text
@param text
*/
void DynamicElementTextItem::setText(const QString &text)
{
m_text = text;
if(m_text_from == DynamicElementTextItem::UserText)
setPlainText(m_text);
emit textChanged(m_text);
}
/**
@brief DynamicElementTextItem::setInfoName
Set the information name of the parent element.
@param info_name
*/
void DynamicElementTextItem::setInfoName(const QString &info_name)
{
QString old_info_name = m_info_name;
m_info_name = info_name;
if(old_info_name == "label")
{
clearFormulaConnection();
updateXref();
}
if (m_parent_element && (m_parent_element.data()->linkType() & Element::AllReport)) //special treatment for report
{
if(old_info_name != info_name)
{
if(old_info_name == "label") {
removeConnectionForReportFormula(m_report_formula);
}
if(info_name == "label")
{
setConnectionForReportFormula(m_report_formula);
updateReportText();
}
else
conductorPropertiesChanged();
}
}
else if (m_info_name == "label" && elementUseForInfo())
{
setupFormulaConnection();
updateLabel();
updateXref();
}
else if(elementUseForInfo() && m_text_from == DynamicElementTextItem::ElementInfo) {
setPlainText(elementUseForInfo()->elementInformations().value(info_name).toString());
}
emit infoNameChanged(info_name);
}
/**
@brief DynamicElementTextItem::infoName
@return the info name of this text
*/
QString DynamicElementTextItem::infoName() const
{
return m_info_name;
}
/**
@brief DynamicElementTextItem::setCompositeText
Set the composite text of this text item to text
@param text
*/
void DynamicElementTextItem::setCompositeText(const QString &text)
{
QString old_composite_text = m_composite_text;
m_composite_text = text;
if(old_composite_text.contains("%{label}"))
{
clearFormulaConnection();
updateXref();
}
if (m_parent_element && (m_parent_element.data()->linkType() & Element::AllReport)) //special treatment for report
{
/*
* May be in some case the old and new composite text both have the var %{label},
* and so we don't have to remove connection and after set connection,
* but for that we must do several checks and because I'm lazy,
* in every case I remove connection and set it after ;)
*/
if(old_composite_text.contains("%{label}"))
removeConnectionForReportFormula(m_report_formula);
if(m_composite_text.contains("%{label}"))
setConnectionForReportFormula(m_report_formula);
updateReportText();
}
else if (m_composite_text.contains("%{label}") && elementUseForInfo())
{
setupFormulaConnection();
updateLabel();
updateXref();
}
else if (m_text_from == DynamicElementTextItem::CompositeText)
{
DiagramContext dc;
if(elementUseForInfo())
dc = elementUseForInfo()->elementInformations();
setPlainText(autonum::AssignVariables::replaceVariable(m_composite_text, dc));
}
emit compositeTextChanged(m_composite_text);
}
/**
@brief DynamicElementTextItem::compositeText
@return
*/
QString DynamicElementTextItem::compositeText() const
{
return m_composite_text;
}
void DynamicElementTextItem::setFrame(const bool frame)
{
m_frame = frame;
update();
emit frameChanged(m_frame);
}
bool DynamicElementTextItem::frame() const
{
return m_frame;
}
QUuid DynamicElementTextItem::uuid() const
{
return m_uuid;
}
/**
@brief DynamicElementTextItem::mousePressEvent
@param event
*/
void DynamicElementTextItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
//The text become selected, we set the real color, otherwise the editor will display the color of text as blue,
//but this is not the color set by the user.
if(m_user_color.isValid())
{
setDefaultTextColor(m_user_color);
m_user_color = QColor(); //m_user_color is now invalid
if(m_slave_Xref_item)
m_slave_Xref_item->setDefaultTextColor(Qt::black);
}
// Shift or no parent initiates movement of dynamic text, otherwise movement of parent element
if((event->modifiers() & Qt::ShiftModifier) || !m_parent_element)
{
m_move_parent = false;
DiagramTextItem::mousePressEvent(event);
} else {
m_move_parent = true;
parentElement()->mousePressEvent(event);
}
}
/**
@brief DynamicElementTextItem::mouseMoveEvent
@param event
*/
void DynamicElementTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if((event->buttons() & Qt::LeftButton) && (flags() & ItemIsMovable))
{
if(m_move_parent)
{
parentElement()->mouseMoveEvent(event);
} else {
if(diagram() && m_first_move)
diagram()->elementTextsMover().beginMovement(diagram(), this);
if(m_first_move)
{
m_initial_position = pos();
if(parentElement())
parentElement()->setHighlighted(true);
}
QPointF current_parent_pos;
QPointF button_down_parent_pos;
current_parent_pos = mapToParent(mapFromScene(event->scenePos()));
button_down_parent_pos = mapToParent(mapFromScene(event->buttonDownScenePos(Qt::LeftButton)));
int diffx = qRound(current_parent_pos.x() - button_down_parent_pos.x());
int diffy = qRound(current_parent_pos.y() - button_down_parent_pos.y());
QPointF new_pos = m_initial_position + QPointF(diffx, diffy);
setPos(new_pos);
if(diagram())
diagram()->elementTextsMover().continueMovement(event);
}
} else {
event->ignore();
}
if(m_first_move)
m_first_move = false;
}
/**
@brief DynamicElementTextItem::mouseReleaseEvent
@param event
*/
void DynamicElementTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if(m_move_parent)
{
parentElement()->mouseReleaseEvent(event);
} else {
if (m_parent_element)
m_parent_element->setHighlighted(false);
if(m_parent_element && m_parent_element->diagram())
m_parent_element.data()->diagram()->elementTextsMover().endMovement();
if(!(event->modifiers() & Qt::ControlModifier))
QGraphicsTextItem::mouseReleaseEvent(event);
}
}
/**
@brief DynamicElementTextItem::mouseDoubleClickEvent
Reimplemented functions, for add extra feature when this text is owned by a slave.
In this case if the parent slave element is linked to a master, and this text display the label of the master
(both if the 'text from' is 'element info' or 'composite text') the QGraphicsView go to master and select it.
@param event
*/
void DynamicElementTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
DiagramTextItem::mouseDoubleClickEvent(event);
zoomToLinkedElement();
}
/**
@brief DynamicElementTextItem::hoverEnterEvent
If the parent element of this text is a folio report or a slave element, the element is linked
and the text display the variable "label" we set the text blue for signal the user that the text act like
a link when we double click on.
@param event
*/
void DynamicElementTextItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
DiagramTextItem::hoverEnterEvent(event);
//If the text is selected we set the real color, otherwise the editor will display the color of text as blue,
//but this is not the color set by the user.
if(isSelected())
return;
if ((parentElement()->linkType() & Element::AllReport) && m_other_report)
{
if( (m_text_from == ElementInfo && m_info_name == "label") ||
(m_text_from == CompositeText && m_composite_text.contains("%{label}")) )
{
m_user_color = color();
setDefaultTextColor(Qt::blue);
}
}
}
/**
@brief DynamicElementTextItem::hoverLeaveEvent
@param event
*/
void DynamicElementTextItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
DiagramTextItem::hoverLeaveEvent(event);
if(m_user_color.isValid())
{
setDefaultTextColor(m_user_color);
m_user_color = QColor(); //m_user_color is now invalid
}
}
void DynamicElementTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
DiagramTextItem::paint(painter, option, widget);
if (m_frame)
{
painter->save();
painter->setFont(QETApp::dynamicTextsItemFont(font().pointSize()));
//Adjust the thickness according to the font size,
qreal w=0.3;
if(font().pointSize() >= 5)
{
w = font().pointSizeF()*0.1;
if(w > 2.5)
w = 2.5;
}
QPen pen;
pen.setColor(color());
pen.setWidthF(w);
painter->setPen(pen);
painter->setRenderHint(QPainter::Antialiasing);
//Adjust the rounding of the rectangle according to the size of the font
qreal ro = font().pointSizeF()/3;
painter->drawRoundedRect(frameRect(), ro, ro);
painter->restore();
}
}
QVariant DynamicElementTextItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
//The first time this text is added to a scene, we make several checking and connection
//according to the link type of the parent element
if(change == QGraphicsItem::ItemSceneHasChanged && m_first_scene_change)
{
if(m_parent_element.isNull())
return QGraphicsObject::itemChange(change, value);
//If the parent is slave, we keep aware about the changement of master.
if(m_parent_element.data()->linkType() == Element::Slave)
{
connect(m_parent_element.data(), &Element::linkedElementChanged, this, &DynamicElementTextItem::masterChanged);
//The parent is already linked, wa call master changed for init the connection
if(!m_parent_element.data()->linkedElements().isEmpty())
masterChanged();
}
else if(m_parent_element.data()->linkType() & Element::AllReport)
{
//Get the report formula, and add connection to keep up to date the formula.
if (m_parent_element.data()->diagram() && m_parent_element.data()->diagram()->project())
{
m_report_formula = m_parent_element.data()->diagram()->project()->defaultReportProperties();
m_report_formula_con = connect(m_parent_element.data()->diagram()->project(), &QETProject::reportPropertiesChanged, this, &DynamicElementTextItem::reportFormulaChanged);
}
//Add connection to keep up to date the status of the element linked to the parent folio report of this text.
connect(m_parent_element.data(), &Element::linkedElementChanged, this, &DynamicElementTextItem::reportChanged);
//The parent is already linked, we call reportChanged for init the connection
if(!m_parent_element.data()->linkedElements().isEmpty())
reportChanged();
if(m_parent_element.data()->terminals().size())
{
//Add connection to keep up date the conductors added or removed to the parent folio report element
connect(m_parent_element.data()->terminals().first(), &Terminal::conductorWasAdded, this, &DynamicElementTextItem::conductorWasAdded);
connect(m_parent_element.data()->terminals().first(), &Terminal::conductorWasRemoved, this, &DynamicElementTextItem::conductorWasRemoved);
}
//Get a conductor in the potential
setPotentialConductor();
}
else if(m_parent_element.data()->linkType() == Element::Master)
{
connect(m_parent_element.data(), &Element::linkedElementChanged, this, &DynamicElementTextItem::updateXref);
if(m_parent_element.data()->diagram())
connect(m_parent_element.data()->diagram()->project(), &QETProject::XRefPropertiesChanged, this, &DynamicElementTextItem::updateXref);
if(!m_parent_element.data()->linkedElements().isEmpty())
updateXref();
}
m_first_scene_change = false;
return QGraphicsObject::itemChange(change, value);
}
else if (change == QGraphicsItem::ItemParentHasChanged)
{
updateXref();
updateXref();
}
return QGraphicsObject::itemChange(change, value);
}
bool DynamicElementTextItem::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
{
if(watched != m_slave_Xref_item)
return false;
if(event->type() == QEvent::GraphicsSceneHoverEnter) {
m_slave_Xref_item->setDefaultTextColor(Qt::blue);
return true;
}
else if(event->type() == QEvent::GraphicsSceneHoverLeave) {
m_slave_Xref_item->setDefaultTextColor(Qt::black);
return true;
}
else if(event->type() == QEvent::GraphicsSceneMouseDoubleClick) {
zoomToLinkedElement();
return true;
}
return false;
}
void DynamicElementTextItem::elementInfoChanged()
{
DiagramContext dc;
Element *element = elementUseForInfo();
if(element) {
dc = element->elementInformations();
}
QString final_text;
if (m_text_from == ElementInfo)
{
//If the info is the label, then we must make some connection
//if the label is created from a formula
if(m_info_name == "label")
{
setupFormulaConnection();
if (element) {
final_text = element->actualLabel();
}
}
else {
final_text = dc.value(m_info_name).toString();
}
}
else if (m_text_from == CompositeText)
{
//If the composite have the label variable, we must make some
//connection if the label is created from a formula
if (m_composite_text.contains("%{label}"))
setupFormulaConnection();
final_text = autonum::AssignVariables::replaceVariable(m_composite_text, dc);
}
else if (m_text_from == UserText)
final_text = m_text;
setPlainText(final_text);
emit plainTextChanged();
}
/**
@brief DynamicElementTextItem::masterChanged
This function is only use when the parent element is a slave.
Call when the master element linked to the parent slave element of this text change
*/
void DynamicElementTextItem::masterChanged()
{
//First we remove the old connection
if(!m_master_element.isNull() && (m_text_from == ElementInfo || m_text_from == CompositeText))
{
disconnect(m_master_element.data(), &Element::elementInfoChange, this, &DynamicElementTextItem::elementInfoChanged);
m_master_element.clear();
updateXref();
}
if(elementUseForInfo())
{
m_master_element = elementUseForInfo();
if(m_text_from == ElementInfo || m_text_from == CompositeText)
connect(m_master_element.data(), &Element::elementInfoChange, this, &DynamicElementTextItem::elementInfoChanged);
updateXref();
}
//Because master changed we update this text
elementInfoChanged();
}
/**
@brief DynamicElementTextItem::reportChanged
This function is only use when parent element of this text is a folio report
The linked report of the parent element was changed
*/
void DynamicElementTextItem::reportChanged()
{
/*
* When the dynamic text are added by a drag & drop from the element panel,
* the connection below are made in the constructor.
* If the text are added at load of a .qet file, the text is not yet added to a diagram then the connection is not made.
* We make it now, because when the linked report changed, that mean this text is in a diagram
*/
if(!m_report_formula_con)
{
//Get the report formula, and add connection to keep up to date the formula.
if (parentElement()->diagram() && parentElement()->diagram()->project())
{
m_report_formula = parentElement()->diagram()->project()->defaultReportProperties();
m_report_formula_con = connect(parentElement()->diagram()->project(), &QETProject::reportPropertiesChanged, this, &DynamicElementTextItem::reportFormulaChanged);
}
}
bool text_have_label = false;
if((textFrom() == ElementInfo && m_info_name == "label") ||
(textFrom() == CompositeText && m_composite_text.contains("%{label}")))
text_have_label = true;
if(text_have_label)
removeConnectionForReportFormula(m_report_formula);
m_other_report.clear();
if(!m_parent_element.data()->linkedElements().isEmpty())
m_other_report = m_parent_element.data()->linkedElements().first();
//Because linked report was changed, we ensure there is a conductor watched
setPotentialConductor();
if(text_have_label)
{
setConnectionForReportFormula(m_report_formula);
updateReportText();
}
}
/**
@brief DynamicElementTextItem::reportFormulaChanged
The report formula use in the project was changed
*/
void DynamicElementTextItem::reportFormulaChanged()
{
m_report_formula = parentElement()->diagram()->project()->defaultReportProperties();
if(m_text_from == ElementInfo && m_info_name == "label")
updateReportText();
}
void DynamicElementTextItem::setConnectionForReportFormula(const QString &formula)
{
if(m_other_report.isNull() || formula.isEmpty())
return;
Element *other_elmt = m_other_report.data();
QString string = formula;
Diagram *other_diagram = m_other_report.data()->diagram();
//Because the variable %F is a reference to another text which can contain variables,
//we must replace %F by the real text, to check if the real text contains the variable %id
if (other_diagram && string.contains("%F"))
{
m_F_str = other_diagram->border_and_titleblock.folio();
string.replace("%F", other_diagram->border_and_titleblock.folio());
connect(&other_diagram->border_and_titleblock, &BorderTitleBlock::titleBlockFolioChanged, this, &DynamicElementTextItem::updateReportFormulaConnection);
}
if (other_diagram && (string.contains("%f") || string.contains("%id")))
{
connect(other_diagram->project(), &QETProject::projectDiagramsOrderChanged, this, &DynamicElementTextItem::updateReportText);
connect(other_diagram->project(), &QETProject::diagramRemoved, this, &DynamicElementTextItem::updateReportText);
}
if (string.contains("%l"))
connect(other_elmt, &Element::yChanged, this, &DynamicElementTextItem::updateReportText);
if (string.contains("%c"))
connect(other_elmt, &Element::xChanged, this, &DynamicElementTextItem::updateReportText);
}
void DynamicElementTextItem::removeConnectionForReportFormula(const QString &formula)
{
if(m_other_report.isNull() || formula.isEmpty())
return;
Element *other_element = m_other_report.data();
QString string = formula;
Diagram *other_diagram = m_other_report.data()->diagram();
//Because the variable %F is a reference to another text which can contain variables,
//we must replace %F with the real text, to check if the real text contains the variable %id
if (other_diagram && string.contains("%F"))
{
string.replace("%F", m_F_str);
disconnect(&other_diagram->border_and_titleblock, &BorderTitleBlock::titleBlockFolioChanged, this, &DynamicElementTextItem::updateReportFormulaConnection);
}
if (other_diagram && (string.contains("%f") || string.contains("%id")))
disconnect(other_diagram->project(), &QETProject::projectDiagramsOrderChanged, this, &DynamicElementTextItem::updateReportText);
if (string.contains("%l"))
disconnect(other_element, &Element::yChanged, this, &DynamicElementTextItem::updateReportText);
if (string.contains("%c"))
disconnect(other_element, &Element::xChanged, this, &DynamicElementTextItem::updateReportText);
}
/**
@brief DynamicElementTextItem::setupFormulaConnection
Setup the required connection for the formula of the label.
*/
void DynamicElementTextItem::setupFormulaConnection()
{
if ((m_text_from == ElementInfo && m_info_name == "label") ||
(m_text_from == CompositeText && m_composite_text.contains("%{label}")))
{
clearFormulaConnection();
Element *element = elementUseForInfo();
if (!element)
return;
Diagram *diagram = element->diagram();
QString formula = element->elementInformations().value("formula").toString();
//Label is frozen, so we don't update it.
if (element->isFreezeLabel())
return;
if (diagram && formula.contains("%F"))
{
m_F_str = diagram->border_and_titleblock.folio();
formula.replace("%F", m_F_str);
m_formula_connection << connect(&diagram->border_and_titleblock, &BorderTitleBlock::titleBlockFolioChanged, this, &DynamicElementTextItem::updateLabel);
}
if (diagram && (formula.contains("%f") || formula.contains("%id")))
{
m_formula_connection << connect(diagram->project(), &QETProject::projectDiagramsOrderChanged, this, &DynamicElementTextItem::updateLabel);
m_formula_connection << connect(diagram->project(), &QETProject::diagramRemoved, this, &DynamicElementTextItem::updateLabel);
}
if (formula.contains("%l"))
m_formula_connection << connect(element, &Element::yChanged, this, &DynamicElementTextItem::updateLabel);
if (formula.contains("%c"))
m_formula_connection << connect(element, &Element::xChanged, this, &DynamicElementTextItem::updateLabel);
}
}
void DynamicElementTextItem::clearFormulaConnection()
{
for (const QMetaObject::Connection& con : m_formula_connection)
disconnect(con);
m_formula_connection.clear();
}
void DynamicElementTextItem::updateReportFormulaConnection()
{
if(!(m_parent_element.data()->linkType() & Element::AllReport))
return;
removeConnectionForReportFormula(m_report_formula);
setConnectionForReportFormula(m_report_formula);
updateReportText();
}
/**
@brief DynamicElementTextItem::updateReportText
This function is only use when this text is owned by a report, and this text have for info the Label.
*/
void DynamicElementTextItem::updateReportText()
{
if(!(m_parent_element.data()->linkType() & Element::AllReport))
return;
if (m_text_from == ElementInfo && m_info_name == "label")
{
if(m_other_report)
{
Element *elmt = m_other_report.data();
QString label = m_report_formula;
label = autonum::AssignVariables::formulaToLabel(label, elmt->rSequenceStruct(), elmt->diagram(), elmt);
setPlainText(label);
}
else
{
setPlainText("");
}
}
else if (m_text_from == CompositeText) {
setPlainText(reportReplacedCompositeText());
}
}
/**
@brief DynamicElementTextItem::updateLabel
Update the displayed text, when this dynamic text is based on the label of the parent element.
This function is notably use when the label itself is based from a formula.
If this dynamic text isn't based on label, this function do nothing.
*/
void DynamicElementTextItem::updateLabel()
{
if ((m_text_from == ElementInfo && m_info_name == "label") ||
(m_text_from == CompositeText && m_composite_text.contains("%{label}")))
{
DiagramContext dc;
Element *element = elementUseForInfo();
if(element) {
dc = element->elementInformations();
}
if(m_text_from == ElementInfo && element) {
setPlainText(element->actualLabel());
}
else if (m_text_from == CompositeText) {
setPlainText(autonum::AssignVariables::replaceVariable(m_composite_text, dc));
}
}
}
/**
@brief DynamicElementTextItem::conductorWasAdded
Function only use when parent element is a folio report
@param conductor
*/
void DynamicElementTextItem::conductorWasAdded(Conductor *conductor)
{
Q_UNUSED(conductor)
setPotentialConductor();
}
/**
@brief DynamicElementTextItem::conductorWasRemoved
Function only use when parent element is a folio report
@param conductor
*/
void DynamicElementTextItem::conductorWasRemoved(Conductor *conductor)
{
if(m_watched_conductor.data() == conductor)
{
disconnect(m_watched_conductor.data(), &Conductor::propertiesChange, this, &DynamicElementTextItem::conductorPropertiesChanged);
m_watched_conductor.clear();
setPotentialConductor();
}
}
/**
@brief DynamicElementTextItem::setPotentialConductor
This function is only used when the parent element of this text is a report element
Get a conductor in the potential of the parent report
*/
void DynamicElementTextItem::setPotentialConductor()
{
if(parentElement() && (parentElement()->linkType() & Element::AllReport))
{
if(parentElement()->terminals().isEmpty())
return;
/*
* #First case, if m_watched_conductor is a conductor of the parent report, everything is ok
* #Second case, if the conductors list of parent report element is not empty,
* we set one of these conductor as m_watched_conductor, even if m_watched_conductor is already set,
* because that mean the conductor is a conductor of the linked report, and we prefer to set a conductor
* owned by the parent report element of this text.
* #third case, if m_watched_conductor is null, we set a conductor of the linked report, if any.
*/
QList<Conductor *> c_list = parentElement()->terminals().first()->conductors();
if(!c_list.isEmpty() && c_list.contains(m_watched_conductor.data()))
return;
else if(!c_list.isEmpty())
{
if(!m_watched_conductor.isNull())
disconnect(m_watched_conductor.data(), &Conductor::propertiesChange, this, &DynamicElementTextItem::conductorPropertiesChanged);
m_watched_conductor = c_list.first();
connect(m_watched_conductor.data(), &Conductor::propertiesChange, this, &DynamicElementTextItem::conductorPropertiesChanged);
}
else if(m_watched_conductor.isNull() && m_other_report)
{
if (!m_other_report.data()->terminals().first()->conductors().isEmpty())
{
m_watched_conductor = m_other_report.data()->terminals().first()->conductors().first();
connect(m_watched_conductor.data(), &Conductor::propertiesChange, this, &DynamicElementTextItem::conductorPropertiesChanged);
}
}
}
else //This text haven't got a parent element, then ther isn't a conductor in the potential
{
if(!m_watched_conductor.isNull())
disconnect(m_watched_conductor.data(), &Conductor::propertiesChange, this, &DynamicElementTextItem::conductorPropertiesChanged);
m_watched_conductor.clear();
}
}
/**
@brief DynamicElementTextItem::conductorPropertiesChanged
This function is only used when the parent element of this text is a report element
*/
void DynamicElementTextItem::conductorPropertiesChanged()
{
if(m_parent_element && (m_parent_element.data()->linkType() & Element::AllReport))
{
if(m_text_from == ElementInfo)
{
if(m_info_name == QETInformation::COND_FUNCTION)
setPlainText(m_watched_conductor? m_watched_conductor.data()->properties().m_function : "");
else if (m_info_name == QETInformation::COND_TENSION_PROTOCOL)
setPlainText(m_watched_conductor? m_watched_conductor.data()->properties().m_tension_protocol : "");
else if (m_info_name == QETInformation::COND_COLOR)
setPlainText(m_watched_conductor? m_watched_conductor.data()->properties().m_wire_color : "");
else if (m_info_name == QETInformation::COND_SECTION)
setPlainText(m_watched_conductor? m_watched_conductor.data()->properties().m_wire_section : "");
}
else if (m_text_from == CompositeText) {
setPlainText(reportReplacedCompositeText());
}
}
}
/**
@brief DynamicElementTextItem::reportReplacedCompositeText
This function is only used when the parent element of this text is a report element
@return the composite text with the variable replaced by the real value.
If the parent element of this text is not a folio report, return a default QString.
*/
QString DynamicElementTextItem::reportReplacedCompositeText() const
{
QString string;
if(m_parent_element.data()->linkType() & Element::AllReport)
{
string = m_composite_text;
if (string.contains("%{label}") && m_other_report)
{
Element *elmt = m_other_report.data();
QString label = m_report_formula;
label = autonum::AssignVariables::formulaToLabel(label, elmt->rSequenceStruct(), elmt->diagram(), elmt);
string.replace("%{label}", label);
}
// if element is not linked, replace an empty string
else
{
string.replace("%{label}", "");
}
if (m_watched_conductor)
{
if(string.contains("%{function}"))
string.replace("%{function}", m_watched_conductor.data()->properties().m_function);
if(string.contains("%{tension_protocol}"))
string.replace("%{tension_protocol}", m_watched_conductor.data()->properties().m_tension_protocol);
if(string.contains("%{conductor_color}"))
string.replace("%{conductor_color}", m_watched_conductor.data()->properties().m_wire_color);
if(string.contains("%{conductor_section}"))
string.replace("%{conductor_section}", m_watched_conductor.data()->properties().m_wire_section);
}
// if no conductor is connected, replace an empty String
else
{
if(string.contains("%{function}"))
string.replace("%{function}", "");
if(string.contains("%{tension_protocol}"))
string.replace("%{tension_protocol}", "");
if(string.contains("%{conductor_color}"))
string.replace("%{conductor_color}", "");
if(string.contains("%{conductor_section}"))
string.replace("%{conductor_section}", "");
}
}
return string;
}
/**
@brief DynamicElementTextItem::zoomToLinkedElement
If the parent element is a folio report or a slave element,
and is linked, zoom to the linked element
*/
void DynamicElementTextItem::zoomToLinkedElement()
{
if(!parentElement())
return;
Element *zoomed_element = nullptr;
if(parentElement()->linkType() == Element::Slave && m_master_element)
{
if ((m_text_from == ElementInfo && m_info_name == "label") ||
(m_text_from == CompositeText && m_composite_text.contains("%{label}")))
zoomed_element = m_master_element.data();
}
if((parentElement()->linkType() & Element::AllReport) && m_other_report)
{
if((m_text_from == ElementInfo && m_info_name == "label") ||
(m_text_from == CompositeText && m_composite_text.contains("%{label}")))
zoomed_element = m_other_report.data();
}
if(zoomed_element)
{
//Unselect and ungrab mouse to prevent unwanted
//move when linked element is in the same scene of this.
setSelected(false);
ungrabMouse();
if(scene() != zoomed_element->scene())
zoomed_element->diagram()->showMe();
zoomed_element->setSelected(true);
//Zoom to the element
for(QGraphicsView *view : zoomed_element->scene()->views())
{
QRectF fit = zoomed_element->sceneBoundingRect();
fit.adjust(-200, -200, 200, 200);
view->fitInView(fit, Qt::KeepAspectRatioByExpanding);
}
}
}
/**
* @brief DynamicElementTextItem::parentElementRotationChanged
* Called when the parent element is rotated
*/
void DynamicElementTextItem::parentElementRotationChanged()
{
if (m_parent_element && m_keep_visual_rotation)
{
//We temporally disconnect for not change m_visual_rotation value.
//We don't use block signal, because rotationChanged signal is used in other place.
disconnect(this, &DynamicElementTextItem::rotationChanged, this, &DynamicElementTextItem::thisRotationChanged);
this->setRotation(QET::correctAngle(m_visual_rotation_ref - m_parent_element->rotation(), true));
connect(this, &DynamicElementTextItem::rotationChanged, this, &DynamicElementTextItem::thisRotationChanged);
}
}
/**
* @brief DynamicElementTextItem::thisRotationChanged
* This function is called when user change the rotation of the text
* and "keep visual rotation" is to true
* to keep in memory the visual rotation wanted by the user.
*/
void DynamicElementTextItem::thisRotationChanged() {
m_visual_rotation_ref = this->rotation() + m_parent_element->rotation();
}
/**
@brief DynamicElementTextItem::updateXref
Create or delete the Xref according to the current properties of the project
*/
void DynamicElementTextItem::updateXref()
{
if(diagram())
{
if(m_parent_element.data()->linkType() == Element::Master)
{
XRefProperties xrp = diagram()->project()->defaultXRefProperties(m_parent_element.data()->kindInformations()["type"].toString());
if(m_text_from == DynamicElementTextItem::ElementInfo &&
m_info_name == "label" &&
!m_parent_element.data()->linkedElements().isEmpty() &&
xrp.snapTo() == XRefProperties::Label)
{
//For add a Xref, this text must not be in a group
if(!parentGroup())
{
if(!m_Xref_item)
m_Xref_item = new CrossRefItem(m_parent_element.data(), this);
return;
}
}
}
else if (m_parent_element.data()->linkType() == Element::Slave)
{
if(m_master_element && !parentGroup() &&
(
(m_text_from == DynamicElementTextItem::ElementInfo && m_info_name == "label") ||
(m_text_from == DynamicElementTextItem::CompositeText && m_composite_text.contains("%{label}"))
)
)
{
XRefProperties xrp = diagram()->project()->defaultXRefProperties(m_master_element.data()->kindInformations()["type"].toString());
QString xref_label = xrp.slaveLabel();
xref_label = autonum::AssignVariables::formulaToLabel(xref_label, m_master_element.data()->rSequenceStruct(), m_master_element.data()->diagram(), m_master_element.data());
if(!m_slave_Xref_item)
{
m_slave_Xref_item = new QGraphicsTextItem(xref_label, this);
m_slave_Xref_item->setFont(QETApp::diagramTextsFont(5));
m_slave_Xref_item->installSceneEventFilter(this);
m_update_slave_Xref_connection << connect(m_master_element.data(), &Element::xChanged, this, &DynamicElementTextItem::updateXref);
m_update_slave_Xref_connection << connect(m_master_element.data(), &Element::yChanged, this, &DynamicElementTextItem::updateXref);
m_update_slave_Xref_connection << connect(m_master_element.data(), &Element::elementInfoChange, this, &DynamicElementTextItem::updateXref);
m_update_slave_Xref_connection << connect(diagram(), &Diagram::diagramInformationChanged, this, &DynamicElementTextItem::updateXref);
m_update_slave_Xref_connection << connect(diagram()->project(), &QETProject::projectDiagramsOrderChanged, this, &DynamicElementTextItem::updateXref);
m_update_slave_Xref_connection << connect(diagram()->project(), &QETProject::diagramRemoved, this, &DynamicElementTextItem::updateXref);
m_update_slave_Xref_connection << connect(diagram()->project(), &QETProject::XRefPropertiesChanged, this, &DynamicElementTextItem::updateXref);
}
else
m_slave_Xref_item->setPlainText(xref_label);
setXref_item(xrp.getXrefPos());
return;
}
}
}
//There is no reason to have a xref, we delete it if exist
if(m_Xref_item)
{
delete m_Xref_item;
m_Xref_item = nullptr;
}
if(m_slave_Xref_item)
{
delete m_slave_Xref_item;
m_slave_Xref_item = nullptr;
m_update_slave_Xref_connection.clear();
}
}
void DynamicElementTextItem::setPlainText(const QString &text)
{
if (toPlainText() == text)
return;
bool update_alignment = true;
if (diagram() && (diagram()->project()->state() == QETProject::ProjectParsingRunning))
update_alignment = false;
if (m_parent_element.data()->state() == QET::GIBuildingFromXml ||
m_parent_element.data()->state() == QET::GILoadingFromXml)
update_alignment = false;
if (update_alignment) {
prepareAlignment();
}
DiagramTextItem::setPlainText(text);
//User define a text width
if (m_text_width > 0)
{
if (document()->size().width() > m_text_width)
{
document()->setTextWidth(m_text_width);
if (document()->size().width() > m_text_width)
{
document()->setTextWidth(document()->idealWidth());
}
}
}
if (update_alignment) {
finishAlignment();
}
if (m_Xref_item) {
m_Xref_item->autoPos();
}
else if (m_slave_Xref_item)
{
XRefProperties xrp = diagram()->project()->defaultXRefProperties(m_master_element.data()->kindInformations()["type"].toString());
setXref_item(xrp.getXrefPos());
}
}
void DynamicElementTextItem::setTextWidth(qreal width)
{
this->document()->setTextWidth(width);
m_text_width = width;
emit textWidthChanged(width);
}
void DynamicElementTextItem::setXref_item(Qt::AlignmentFlag m_exHrefPos)
{
QRectF r = boundingRect();
QPointF pos;
//QPointF pos(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,r.top());
if (m_exHrefPos == Qt::AlignBottom)
{
pos = QPointF(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,r.bottom());
}
else if (m_exHrefPos == Qt::AlignTop)
{
pos = QPointF(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,r.top() - m_slave_Xref_item->boundingRect().height());
}
else if (m_exHrefPos == Qt::AlignLeft) //
{
pos = QPointF(r.left() - m_slave_Xref_item->boundingRect().width(),r.center().y() - m_slave_Xref_item->boundingRect().height()/2);
}
else if (m_exHrefPos == Qt::AlignRight) //
{
pos = QPointF(r.right() ,r.center().y() - m_slave_Xref_item->boundingRect().height()/2);
}
else if (m_exHrefPos == Qt::AlignBaseline) //
{
if(this->alignment() &Qt::AlignBottom)
{
pos = QPointF(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,r.bottom());
}
else if(this->alignment() &Qt::AlignTop)
{
pos = QPointF(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,r.top() - m_slave_Xref_item->boundingRect().height());
}
else if(this->alignment() &Qt::AlignLeft)
{
pos = QPointF(r.left() - m_slave_Xref_item->boundingRect().width(),r.center().y() - m_slave_Xref_item->boundingRect().height()/2);
}
else if(this->alignment() &Qt::AlignRight)
{
pos = QPointF(r.right() ,r.center().y() - m_slave_Xref_item->boundingRect().height()/2);
}
}
m_slave_Xref_item->setPos(pos);
return;
}
void DynamicElementTextItem::setKeepVisualRotation(bool set)
{
m_keep_visual_rotation = set;
emit keepVisualRotationChanged(set);
if (set) {
m_visual_rotation_ref = this->rotation() + m_parent_element->rotation();
connect(m_parent_element, &Element::rotationChanged, this, &DynamicElementTextItem::parentElementRotationChanged);
connect(this, &DynamicElementTextItem::rotationChanged, this, &DynamicElementTextItem::thisRotationChanged);
}
else {
disconnect(m_parent_element, &Element::rotationChanged, this, &DynamicElementTextItem::parentElementRotationChanged);
disconnect(this, &DynamicElementTextItem::rotationChanged, this, &DynamicElementTextItem::thisRotationChanged);
}
}
bool DynamicElementTextItem::keepVisualRotation() const {
return m_keep_visual_rotation;
}