Merge remote-tracking branch 'origin/master' into qt6_cmake_joshua

# Conflicts:
#	CMakeLists.txt
This commit is contained in:
Laurent Trinques
2026-05-28 09:32:07 +02:00
93 changed files with 17932 additions and 12533 deletions
+405 -99
View File
@@ -17,6 +17,8 @@
*/
#include "crossrefitem.h"
#include <QTimer>
#include "../autoNum/assignvariables.h"
#include "../diagram.h"
#include "../diagramposition.h"
@@ -25,6 +27,7 @@
#include "element.h"
#include "elementtextitemgroup.h"
#include "qgraphicsitemutility.h"
#include "terminal.h"
//define the height of the header.
static int header = 5;
@@ -221,9 +224,12 @@ void CrossRefItem::updateLabel()
prepareGeometryChange();
m_bounding_rect = QRectF();
//init the painter
QPainter qp;
qp.begin(&m_drawing);
// Build geometry and m_hovered_contacts_map using a QImage-backed
// painter so font metrics match the screen painter in paint().
// m_update_map=true allows the draw functions to populate the map;
// paint() calls them with m_update_map=false so the map is stable.
QImage dummy(1, 1, QImage::Format_ARGB32_Premultiplied);
QPainter qp(&dummy);
QPen pen_;
pen_.setWidthF(0.5);
qp.setPen(pen_);
@@ -232,17 +238,21 @@ void CrossRefItem::updateLabel()
//Draw cross or contact, only if master element is linked.
if (! m_element->linkedElements().isEmpty())
{
m_update_map = true;
XRefProperties::DisplayHas dh = m_properties.displayHas();
if (dh == XRefProperties::Cross)
drawAsCross(qp);
else if (dh == XRefProperties::Contacts)
drawAsContacts(qp);
m_update_map = false;
}
qp.end();
autoPos();
update();
// Schedule a second update after the scene has finished laying out,
// so the initial render uses the correct bounding rect.
QTimer::singleShot(0, this, [this]{ update(); });
}
/**
@@ -310,7 +320,26 @@ void CrossRefItem::paint(
{
Q_UNUSED(option)
Q_UNUSED(widget)
m_drawing.play(painter);
// Draw directly — no QPicture involved anywhere.
// QPicture::play() + nested drawPicture() (m_hdr_no_ctc/m_hdr_nc_ctc)
// caused a use-after-free crash (QRegion::begin, Qt5Gui+0x49af60)
// confirmed by analysis of 19+ coredumps.
// m_update_map=false: draw functions do not overwrite m_hovered_contacts_map.
if (m_element->linkedElements().isEmpty()) return;
QPen pen_;
pen_.setWidthF(0.5);
painter->save();
painter->setPen(pen_);
painter->setFont(QETApp::diagramTextsFont(5));
m_update_map = false;
XRefProperties::DisplayHas dh = m_properties.displayHas();
if (dh == XRefProperties::Cross)
drawAsCross(*painter);
else if (dh == XRefProperties::Contacts)
drawAsContacts(*painter);
painter->restore();
}
/**
@@ -320,7 +349,24 @@ void CrossRefItem::paint(
void CrossRefItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
event->accept();
QetGraphicsItem::showItem(m_hovered_contact);
// Find the element under the click position directly from the map,
// rather than relying on m_hovered_contact which may have been reset
// by hoverMoveEvent between the two clicks of the double-click.
QPointF pos = event->pos();
Element *target = m_hovered_contact;
if (!target) {
for (auto it = m_hovered_contacts_map.begin();
it != m_hovered_contacts_map.end(); ++it) {
if (it.value().contains(pos)) {
target = it.key();
break;
}
}
}
QetGraphicsItem::showItem(target);
}
/**
@@ -432,49 +478,41 @@ void CrossRefItem::linkedChanged()
/**
@brief CrossRefItem::buildHeaderContact
Draw the QPicture of m_hdr_no_ctc and m_hdr_nc_ctc
Draw NO and NC contact symbols directly onto painter.
Previously used QPicture (m_hdr_no_ctc/m_hdr_nc_ctc) which caused
use-after-free crashes via nested QPicture::play() calls.
*/
void CrossRefItem::buildHeaderContact()
void CrossRefItem::buildHeaderContact(QPainter &painter, QPointF no_pos, QPointF nc_pos)
{
if (!m_hdr_no_ctc.isNull() && !m_hdr_nc_ctc.isNull()) return;
//init the painter
QPainter qp;
QPen pen_;
pen_.setWidthF(0.2);
painter.save();
painter.setPen(pen_);
//draw the NO contact
if (m_hdr_no_ctc.isNull()) {
qp.begin(&m_hdr_no_ctc);
qp.setPen(pen_);
qp.drawLine(0, 3, 5, 3);
QPointF p1[3] = {
QPointF(5, 0),
QPointF(10, 3),
QPointF(15, 3),
};
qp.drawPolyline(p1,3);
qp.end();
}
//draw the NO contact header symbol
painter.drawLine(no_pos.x()+0, no_pos.y()+3, no_pos.x()+5, no_pos.y()+3);
QPointF p1[3] = {
QPointF(no_pos.x()+5, no_pos.y()+0),
QPointF(no_pos.x()+10, no_pos.y()+3),
QPointF(no_pos.x()+15, no_pos.y()+3),
};
painter.drawPolyline(p1, 3);
//draw the NC contact
if (m_hdr_nc_ctc.isNull()) {
qp.begin(&m_hdr_nc_ctc);
qp.setPen(pen_);
QPointF p2[3] = {
QPointF(0, 3),
QPointF(5, 3),
QPointF(5, 0)
};
qp.drawPolyline(p2,3);
QPointF p3[3] = {
QPointF(4, 0),
QPointF(10, 3),
QPointF(15, 3),
};
qp.drawPolyline(p3,3);
qp.end();
}
//draw the NC contact header symbol
QPointF p2[3] = {
QPointF(nc_pos.x()+0, nc_pos.y()+3),
QPointF(nc_pos.x()+5, nc_pos.y()+3),
QPointF(nc_pos.x()+5, nc_pos.y()+0)
};
painter.drawPolyline(p2, 3);
QPointF p3[3] = {
QPointF(nc_pos.x()+4, nc_pos.y()+0),
QPointF(nc_pos.x()+10, nc_pos.y()+3),
QPointF(nc_pos.x()+15, nc_pos.y()+3),
};
painter.drawPolyline(p3, 3);
painter.restore();
}
/**
@@ -492,11 +530,95 @@ void CrossRefItem::setUpCrossBoundingRect(QPainter &painter)
QStringList no_str, nc_str;
// Helper lambda: build "[13-14] pos" string for an element.
// For power contacts (e.g. 3-pole contactor), all named terminals
// are collected (e.g. "[1-2-3-4-5-6] pos").
// For other contacts (NO/NC/SW), only the first 2 named terminals
// are used — users are expected to name and order their terminals
// in the element editor.
// Helper lambda: build "[13-14] pos" for an element.
// - Power: all terminals sorted numerically → "[1-2-3-4-5-6] pos"
// - SW with typed terminals: show relevant pair per column (handled below)
// - Others: first 2 named terminals
auto buildLabel = [this](Element *elmt, bool is_no_col) -> QString {
const bool is_power =
elmt->kindInformations()["type"].toString() == "power";
const bool is_sw =
elmt->kindInformations()["state"].toString() == "SW";
QStringList tnames;
if (is_sw) {
// Check if terminals have explicit No/Nc/Common types
bool has_typed = false;
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (t->terminalType() == TerminalData::No ||
t->terminalType() == TerminalData::Nc ||
t->terminalType() == TerminalData::Common) {
has_typed = true; break;
}
}
if (has_typed) {
QString no_name, nc_name, common_name;
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (!t->name().isEmpty()) {
if (t->terminalType() == TerminalData::No) no_name = t->name();
else if (t->terminalType() == TerminalData::Nc) nc_name = t->name();
else if (t->terminalType() == TerminalData::Common) common_name = t->name();
}
}
// NO column: show NO+Common pair; NC column: show NC+Common pair
if (is_no_col)
tnames << no_name << common_name;
else
tnames << nc_name << common_name;
} else {
// Fallback: first 2 named terminals
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
const QString tn = t->name();
if (!tn.isEmpty()) { tnames << tn; if (tnames.size() >= 2) break; }
}
}
} else {
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
const QString tn = t->name();
if (!tn.isEmpty()) {
tnames << tn;
if (!is_power && tnames.size() >= 2) break;
}
}
if (is_power) {
std::sort(tnames.begin(), tnames.end(),
[](const QString &a, const QString &b){
int i_a = a.size();
while (i_a > 0 && a[i_a-1].isDigit()) --i_a;
int i_b = b.size();
while (i_b > 0 && b[i_b-1].isDigit()) --i_b;
bool a_ok = false, b_ok = false;
int ai = a.mid(i_a).toInt(&a_ok);
int bi = b.mid(i_b).toInt(&b_ok);
if (a_ok && b_ok && a.left(i_a) == b.left(i_b))
return ai < bi;
return a < b;
});
}
}
QString pos = elementPositionText(elmt, true);
if (!tnames.isEmpty() && m_properties.showTerminalName())
return QStringLiteral("[") + tnames.join("-") + QStringLiteral("] ") + pos;
return pos;
};
for (auto elmt : NOElements()) {
no_str.append(elementPositionText(elmt, true));
no_str.append(buildLabel(elmt, true));
}
for (auto elmt : NCElements()) {
nc_str.append(elementPositionText(elmt, true));
nc_str.append(buildLabel(elmt, false));
}
//There is no string to display, we return now
@@ -509,9 +631,10 @@ void CrossRefItem::setUpCrossBoundingRect(QPainter &painter)
QRectF no_bounding;
for (auto str : no_str)
{
QRectF bounding = painter.boundingRect(QRectF (), Qt::AlignCenter, str);
no_bounding = no_bounding.united(bounding);
QRectF bounding = painter.boundingRect(QRectF(0, 0, 500, 20), Qt::AlignLeft, str);
no_bounding.setHeight(no_bounding.height() + bounding.height());
if (bounding.width() > no_bounding.width())
no_bounding.setWidth(bounding.width());
}
//Adjust according to the NO
if (no_bounding.height() > default_bounding.height() - header)
@@ -523,9 +646,10 @@ void CrossRefItem::setUpCrossBoundingRect(QPainter &painter)
QRectF nc_bounding;
for (auto str : nc_str)
{
QRectF bounding = painter.boundingRect(QRectF (), Qt::AlignCenter, str);
nc_bounding = nc_bounding.united(bounding);
QRectF bounding = painter.boundingRect(QRectF(0, 0, 500, 20), Qt::AlignLeft, str);
nc_bounding.setHeight(nc_bounding.height() + bounding.height());
if (bounding.width() > nc_bounding.width())
nc_bounding.setWidth(bounding.width());
}
//Adjust according to the NC
if (nc_bounding.height() > default_bounding.height() - header)
@@ -549,7 +673,8 @@ void CrossRefItem::drawAsCross(QPainter &painter)
{
//calculate the size of the cross
setUpCrossBoundingRect(painter);
m_hovered_contacts_map.clear();
m_drawed_contacts = 0;
if (m_update_map) m_hovered_contacts_map.clear();
//Bounding rect is empty that mean there's no contact to draw
if (boundingRect().isEmpty()) return;
@@ -559,12 +684,11 @@ void CrossRefItem::drawAsCross(QPainter &painter)
painter.drawLine(br.width()/2, 0, br.width()/2, br.height()); //vertical line
painter.drawLine(0, header, br.width(), header); //horizontal line
//Add the symbolic contacts
buildHeaderContact();
QPointF p((m_bounding_rect.width()/4) - (m_hdr_no_ctc.width()/2), 0);
painter.drawPicture (p, m_hdr_no_ctc);
p.setX((m_bounding_rect.width() * 3/4) - (m_hdr_nc_ctc.width()/2));
painter.drawPicture (p, m_hdr_nc_ctc);
//Add the symbolic contacts (drawn directly, no QPicture)
static const qreal hdr_symbol_width = 15.0;
QPointF no_pos((m_bounding_rect.width()/4) - (hdr_symbol_width/2), 0);
QPointF nc_pos((m_bounding_rect.width() * 3/4) - (hdr_symbol_width/2), 0);
buildHeaderContact(painter, no_pos, nc_pos);
//and fill it
fillCrossRef(painter);
@@ -581,7 +705,7 @@ void CrossRefItem::drawAsContacts(QPainter &painter)
return;
m_drawed_contacts = 0;
m_hovered_contacts_map.clear();
if (m_update_map) m_hovered_contacts_map.clear();
QRectF bounding_rect;
//Draw each linked contact
@@ -605,7 +729,7 @@ void CrossRefItem::drawAsContacts(QPainter &painter)
else if (type == "delayOff") option += DelayOff;
else if (type == "delayOnOff") option += DelayOnOff;
QRectF br = drawContact(painter, option, elmt);
QRectF br = drawContact(painter, option, elmt, i);
bounding_rect = bounding_rect.united(br);
}
}
@@ -624,9 +748,81 @@ void CrossRefItem::drawAsContacts(QPainter &painter)
@param elmt : the element to display text (the position of the contact)
@return The bounding rect of the draw (contact + text)
*/
QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt, int pole_index)
{
QString str = elementPositionText(elmt);
// Collect terminal names from the element definition (.elmt)
// e.g. name="13" and name="14" on each terminal
// For power contacts, sort numerically and pick the pair for pole_index.
// For SW contacts with typed terminals (No/Nc/Common), filter by role.
QStringList terminal_names;
const bool is_power_ctc =
elmt->kindInformations()["type"].toString() == "power";
const bool is_sw = (flags & SW) && !(flags & NOC);
// Check if SW terminals have explicit No/Nc/Common types
bool sw_has_typed_terminals = false;
if (is_sw) {
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (t->terminalType() == TerminalData::No ||
t->terminalType() == TerminalData::Nc ||
t->terminalType() == TerminalData::Common) {
sw_has_typed_terminals = true;
break;
}
}
}
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
const QString tname = t->name();
if (!tname.isEmpty())
terminal_names << tname;
}
if (is_power_ctc) {
// Sort terminals alphanumerically so names like "R1","R2"... or "1","2"...
// are ordered correctly. Extract trailing digits for numeric comparison;
// fall back to full string comparison when no digits are found.
std::sort(terminal_names.begin(), terminal_names.end(),
[](const QString &a, const QString &b){
// Extract trailing numeric part
int i_a = a.size();
while (i_a > 0 && a[i_a-1].isDigit()) --i_a;
int i_b = b.size();
while (i_b > 0 && b[i_b-1].isDigit()) --i_b;
bool a_ok = false, b_ok = false;
int ai = a.mid(i_a).toInt(&a_ok);
int bi = b.mid(i_b).toInt(&b_ok);
if (a_ok && b_ok && a.left(i_a) == b.left(i_b))
return ai < bi;
return a < b;
});
// Pick the pair for this pole: pole 0 → [0,1], pole 1 → [2,3], etc.
int idx = pole_index * 2;
if (idx + 1 < terminal_names.size())
terminal_names = QStringList() << terminal_names[idx] << terminal_names[idx+1];
else
terminal_names.clear();
} else if (is_sw && sw_has_typed_terminals) {
// Build [NO_name, Common_name, NC_name] from typed terminals
QString no_name, nc_name, common_name;
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (!t->name().isEmpty()) {
if (t->terminalType() == TerminalData::No) no_name = t->name();
else if (t->terminalType() == TerminalData::Nc) nc_name = t->name();
else if (t->terminalType() == TerminalData::Common) common_name = t->name();
}
}
// drawText expects: [0]=NC, [1]=NO, [2]=Common
// (drawText uses [1] for NO top-left, [0] for NC bottom-left, [2] for Common right)
terminal_names.clear();
terminal_names << nc_name << no_name << common_name;
}
int offset = m_drawed_contacts*10;
QRectF bounding_rect = QRectF(0, offset, 24, 10);
@@ -643,15 +839,17 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
painter.drawLine(0, offset+6, 8, offset+6);
painter.drawLine(16, offset+6, 24, offset+6);
///take example of this code for display the terminal text
/*QFont font = QETApp::diagramTextsFont(4);
font.setBold(true);
painter.setFont(font);
QRectF bt(0, offset, 24, 10);
int txt = 10 + m_drawed_contacts;
painter.drawText(bt, Qt::AlignLeft|Qt::AlignTop, QString::number(txt));
painter.drawText(bt, Qt::AlignRight|Qt::AlignTop, QString::number(txt));
painter.setFont(QETApp::diagramTextsFont(5));*/
// Draw terminal names on each side of the contact symbol
// terminal_names[0] on the left, terminal_names[1] on the right
if (!terminal_names.isEmpty() && m_properties.showTerminalName()) {
painter.setFont(QETApp::diagramTextsFont(4));
QRectF bt(0, offset, 24, 10);
if (terminal_names.size() >= 1)
painter.drawText(bt, Qt::AlignLeft|Qt::AlignTop, terminal_names[0]);
if (terminal_names.size() >= 2)
painter.drawText(bt, Qt::AlignRight|Qt::AlignTop, terminal_names[1]);
painter.setFont(QETApp::diagramTextsFont(5));
}
//draw open contact
if (flags &NO) {
@@ -729,14 +927,8 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
painter.drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, str);
bounding_rect = bounding_rect.united(text_rect);
if (m_hovered_contacts_map.contains(elmt))
{
if (m_update_map)
m_hovered_contacts_map.insert(elmt, bounding_rect);
}
else
{
m_hovered_contacts_map.insert(elmt, bounding_rect);
}
++m_drawed_contacts;
}
@@ -768,6 +960,26 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
};
painter.drawPolyline(p2, 3);
// Draw terminal names for switch contact (3 terminals)
// terminal_names[0] = NO side (top left)
// terminal_names[1] = NC side (bottom left)
// terminal_names[2] = common side (right)
if (!terminal_names.isEmpty() && m_properties.showTerminalName()) {
painter.setFont(QETApp::diagramTextsFont(4));
// Sort order from parseTerminal (top->bottom, left->right):
// [0]=12 (NO, top-left), [1]=14 (common, top-center), [2]=13 (NC, bottom-center)
if (terminal_names.size() >= 1)
painter.drawText(QRectF(0, offset, 8, 8),
Qt::AlignLeft|Qt::AlignTop, terminal_names[1]); // 12 NO left
if (terminal_names.size() >= 2)
painter.drawText(QRectF(16, offset+4, 8, 6),
Qt::AlignRight|Qt::AlignTop, terminal_names[2]); // 14 common right
if (terminal_names.size() >= 3)
painter.drawText(QRectF(0, offset+9, 8, 6),
Qt::AlignLeft|Qt::AlignTop, terminal_names[0]); // 13 NC left-bottom
painter.setFont(QETApp::diagramTextsFont(5));
}
//Draw the half ellipse off delay
if (flags &Delay)
{
@@ -799,12 +1011,8 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
str);
bounding_rect = bounding_rect.united(text_rect);
if (m_hovered_contacts_map.contains(elmt)) {
if (m_update_map)
m_hovered_contacts_map.insert(elmt, bounding_rect);
}
else {
m_hovered_contacts_map.insert(elmt, bounding_rect);
}
//a switch contact take place of two normal contact
m_drawed_contacts += 2;
@@ -835,12 +1043,8 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
str);
bounding_rect = bounding_rect.united(text_rect);
if (m_hovered_contacts_map.contains(elmt)) {
if (m_update_map)
m_hovered_contacts_map.insert(elmt, bounding_rect);
}
else {
m_hovered_contacts_map.insert(elmt, bounding_rect);
}
++m_drawed_contacts;
}
return bounding_rect;
@@ -866,7 +1070,60 @@ void CrossRefItem::fillCrossRef(QPainter &painter)
m_hovered_contact == elmt ? pen.setColor(Qt::blue) :pen.setColor(Qt::black);
painter.setPen(pen);
// Collect terminal names for NO column.
// Power: all terminals sorted numerically.
// SW with typed terminals: NO+Common pair.
// Others: first 2 named terminals.
const bool is_power_no =
elmt->kindInformations()["type"].toString() == "power";
const bool is_sw_no =
elmt->kindInformations()["state"].toString() == "SW";
QStringList tnames;
if (is_sw_no) {
bool has_typed = false;
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (t->terminalType() == TerminalData::No ||
t->terminalType() == TerminalData::Nc ||
t->terminalType() == TerminalData::Common) {
has_typed = true; break;
}
}
if (has_typed) {
QString no_name, common_name;
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (!t->name().isEmpty()) {
if (t->terminalType() == TerminalData::No) no_name = t->name();
else if (t->terminalType() == TerminalData::Common) common_name = t->name();
}
}
tnames << no_name << common_name;
} else {
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
const QString tn = t->name();
if (!tn.isEmpty()) { tnames << tn; if (tnames.size() >= 2) break; }
}
}
} else {
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
const QString tn = t->name();
if (!tn.isEmpty()) {
tnames << tn;
if (!is_power_no && tnames.size() >= 2) break;
}
}
}
QString terminal_label;
if (!tnames.isEmpty() && m_properties.showTerminalName())
terminal_label = QStringLiteral("[") + tnames.join("-") + QStringLiteral("]");
QString str = elementPositionText(elmt, true);
if (!terminal_label.isEmpty())
str = terminal_label + QStringLiteral(" ") + str;
QRectF bounding = painter.boundingRect(
QRectF(no_top_left,
QSize(middle_cross, 1)),
@@ -874,13 +1131,11 @@ void CrossRefItem::fillCrossRef(QPainter &painter)
str);
painter.drawText(bounding, Qt::AlignLeft, str);
if (m_hovered_contacts_map.contains(elmt))
{
m_hovered_contacts_map.insert(elmt, bounding);
}
else
{
m_hovered_contacts_map.insert(elmt, bounding);
if (m_update_map) {
QString pos_str = elementPositionText(elmt, true);
QRectF pos_rect = painter.boundingRect(bounding, Qt::AlignRight, pos_str);
pos_rect.adjust(-2, -1, 2, 1); // extend hit area slightly
m_hovered_contacts_map.insert(elmt, pos_rect);
}
no_top_left.ry() += bounding.height();
@@ -895,7 +1150,60 @@ void CrossRefItem::fillCrossRef(QPainter &painter)
:pen.setColor(Qt::black);
painter.setPen(pen);
// Collect terminal names for NC column.
// Power: all terminals sorted numerically.
// SW with typed terminals: NC+Common pair.
// Others: first 2 named terminals.
const bool is_power_nc =
elmt->kindInformations()["type"].toString() == "power";
const bool is_sw_nc =
elmt->kindInformations()["state"].toString() == "SW";
QStringList tnames_nc;
if (is_sw_nc) {
bool has_typed = false;
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (t->terminalType() == TerminalData::No ||
t->terminalType() == TerminalData::Nc ||
t->terminalType() == TerminalData::Common) {
has_typed = true; break;
}
}
if (has_typed) {
QString nc_name, common_name;
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
if (!t->name().isEmpty()) {
if (t->terminalType() == TerminalData::Nc) nc_name = t->name();
else if (t->terminalType() == TerminalData::Common) common_name = t->name();
}
}
tnames_nc << nc_name << common_name;
} else {
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
const QString tn = t->name();
if (!tn.isEmpty()) { tnames_nc << tn; if (tnames_nc.size() >= 2) break; }
}
}
} else {
for (Terminal *t : elmt->terminals()) {
if (!t) continue;
const QString tn = t->name();
if (!tn.isEmpty()) {
tnames_nc << tn;
if (!is_power_nc && tnames_nc.size() >= 2) break;
}
}
}
QString terminal_label;
if (!tnames_nc.isEmpty() && m_properties.showTerminalName())
terminal_label = QStringLiteral("[") + tnames_nc.join("-") + QStringLiteral("]");
QString str = elementPositionText(elmt, true);
if (!terminal_label.isEmpty())
str = terminal_label + QStringLiteral(" ") + str;
QRectF bounding = painter.boundingRect(
QRectF(nc_top_left,
QSize(middle_cross, 1)),
@@ -903,13 +1211,11 @@ void CrossRefItem::fillCrossRef(QPainter &painter)
str);
painter.drawText(bounding, Qt::AlignRight, str);
if (m_hovered_contacts_map.contains(elmt))
{
m_hovered_contacts_map.insert(elmt, bounding);
}
else
{
m_hovered_contacts_map.insert(elmt, bounding);
if (m_update_map) {
QString pos_str = elementPositionText(elmt, true);
QRectF pos_rect = painter.boundingRect(bounding, Qt::AlignRight, pos_str);
pos_rect.adjust(-2, -1, 2, 1); // extend hit area slightly
m_hovered_contacts_map.insert(elmt, pos_rect);
}
nc_top_left.ry() += bounding.height();
+3 -4
View File
@@ -22,7 +22,6 @@
#include <QGraphicsObject>
#include <QMultiMap>
#include <QPicture>
class Element;
class DynamicElementTextItem;
@@ -103,11 +102,11 @@ class CrossRefItem : public QGraphicsObject
private:
void linkedChanged();
void buildHeaderContact();
void buildHeaderContact(QPainter &painter, QPointF no_pos, QPointF nc_pos);
void setUpCrossBoundingRect(QPainter &painter);
void drawAsCross(QPainter &painter);
void drawAsContacts(QPainter &painter);
QRectF drawContact(QPainter &painter, int flags, Element *elmt);
QRectF drawContact(QPainter &painter, int flags, Element *elmt, int pole_index = 0);
void fillCrossRef(QPainter &painter);
void AddExtraInfo(QPainter &painter, const QString&);
QList<Element *> NOElements() const;
@@ -117,10 +116,10 @@ class CrossRefItem : public QGraphicsObject
private:
Element *m_element; //element to display the cross reference
QRectF m_bounding_rect;
QPicture m_drawing, m_hdr_no_ctc, m_hdr_nc_ctc;
QPainterPath m_shape_path;
XRefProperties m_properties;
int m_drawed_contacts;
bool m_update_map = false;
QMultiMap <Element *, QRectF> m_hovered_contacts_map;
Element *m_hovered_contact = nullptr;
DynamicElementTextItem *m_text = nullptr;
@@ -26,7 +26,7 @@
#include "crossrefitem.h"
#include "element.h"
#include "elementtextitemgroup.h"
#include <QTimer>
#include <QDomDocument>
#include <QDomElement>
#include <QGraphicsSceneMouseEvent>
@@ -302,7 +302,8 @@ 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)
if((m_parent_element.data()->linkType() & Element::AllReport) ||
m_parent_element.data()->linkType() == Element::ConductorDefinition)
{
updateReportFormulaConnection();
updateReportText();
@@ -418,7 +419,8 @@ void DynamicElementTextItem::setInfoName(const QString &info_name)
updateXref();
}
if (m_parent_element && (m_parent_element.data()->linkType() & Element::AllReport)) //special treatment for report
if (m_parent_element && ((m_parent_element.data()->linkType() & Element::AllReport) ||
m_parent_element.data()->linkType() == Element::ConductorDefinition)) //special treatment for report
{
if(old_info_name != info_name)
{
@@ -472,7 +474,8 @@ void DynamicElementTextItem::setCompositeText(const QString &text)
updateXref();
}
if (m_parent_element && (m_parent_element.data()->linkType() & Element::AllReport)) //special treatment for report
if (m_parent_element && ((m_parent_element.data()->linkType() & Element::AllReport) ||
m_parent_element.data()->linkType() == Element::ConductorDefinition)) //special treatment for report
{
/*
* May be in some case the old and new composite text both have the var %{label},
@@ -726,28 +729,29 @@ QVariant DynamicElementTextItem::itemChange(QGraphicsItem::GraphicsItemChange ch
if(!m_parent_element.data()->linkedElements().isEmpty())
masterChanged();
}
else if(m_parent_element.data()->linkType() & Element::AllReport)
else if((m_parent_element.data()->linkType() & Element::AllReport) ||
m_parent_element.data()->linkType() == Element::ConductorDefinition)
{
//Get the report formula, and add connection to keep up to date the formula.
//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.
//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
//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
//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
//Get a conductor in the potential
setPotentialConductor();
}
else if(m_parent_element.data()->linkType() == Element::Master)
@@ -1027,7 +1031,8 @@ void DynamicElementTextItem::clearFormulaConnection()
void DynamicElementTextItem::updateReportFormulaConnection()
{
if(!(m_parent_element.data()->linkType() & Element::AllReport))
if(!(m_parent_element.data()->linkType() & Element::AllReport) &&
m_parent_element.data()->linkType() != Element::ConductorDefinition)
return;
removeConnectionForReportFormula(m_report_formula);
@@ -1041,7 +1046,8 @@ void DynamicElementTextItem::updateReportFormulaConnection()
*/
void DynamicElementTextItem::updateReportText()
{
if(!(m_parent_element.data()->linkType() & Element::AllReport))
if(!(m_parent_element.data()->linkType() & Element::AllReport) &&
m_parent_element.data()->linkType() != Element::ConductorDefinition)
return;
if (m_text_from == ElementInfo && m_info_name == "label")
@@ -1098,7 +1104,10 @@ void DynamicElementTextItem::updateLabel()
void DynamicElementTextItem::conductorWasAdded(Conductor *conductor)
{
Q_UNUSED(conductor)
setPotentialConductor();
QTimer::singleShot(100, this, [this]() {
setPotentialConductor();
conductorPropertiesChanged();
});
}
/**
@@ -1123,7 +1132,8 @@ void DynamicElementTextItem::conductorWasRemoved(Conductor *conductor)
*/
void DynamicElementTextItem::setPotentialConductor()
{
if(parentElement() && (parentElement()->linkType() & Element::AllReport))
if(parentElement() && ((parentElement()->linkType() & Element::AllReport) || (parentElement()->linkType() == Element::ConductorDefinition) ||
parentElement()->linkType() == Element::ConductorDefinition))
{
if(parentElement()->terminals().isEmpty())
return;
@@ -1156,6 +1166,7 @@ void DynamicElementTextItem::setPotentialConductor()
connect(m_watched_conductor.data(), &Conductor::propertiesChange, this, &DynamicElementTextItem::conductorPropertiesChanged);
}
}
conductorPropertiesChanged();
}
else //This text haven't got a parent element, then ther isn't a conductor in the potential
{
@@ -1172,7 +1183,8 @@ void DynamicElementTextItem::setPotentialConductor()
*/
void DynamicElementTextItem::conductorPropertiesChanged()
{
if(m_parent_element && (m_parent_element.data()->linkType() & Element::AllReport))
if(m_parent_element && ((m_parent_element.data()->linkType() & Element::AllReport) || (m_parent_element.data()->linkType() == Element::ConductorDefinition) ||
m_parent_element.data()->linkType() == Element::ConductorDefinition))
{
if(m_text_from == ElementInfo)
{
@@ -1200,8 +1212,9 @@ void DynamicElementTextItem::conductorPropertiesChanged()
QString DynamicElementTextItem::reportReplacedCompositeText() const
{
QString string;
if(m_parent_element.data()->linkType() & Element::AllReport)
if((m_parent_element.data()->linkType() & Element::AllReport) || (m_parent_element.data()->linkType() == Element::ConductorDefinition) ||
m_parent_element.data()->linkType() == Element::ConductorDefinition)
{
string = m_composite_text;
+33
View File
@@ -36,6 +36,7 @@
#include "../qetxml.h"
#include "../qetversion.h"
#include "qgraphicsitemutility.h"
#include <QDebug>
#include <QDomElement>
#include <utility>
@@ -431,6 +432,11 @@ bool Element::buildFromXml(const QDomElement &xml_def_elmt, int *state)
m_data.fromXml(xml_def_elmt);
setToolTip(name());
QString my_type_str = xml_def_elmt.attribute(QStringLiteral("link_type"), QStringLiteral("simple"));
if (my_type_str == QLatin1String("conductor_definition")) {
m_link_type = Element::ConductorDefinition;
}
//load kind informations
m_kind_informations.fromXml(
xml_def_elmt.firstChildElement(QStringLiteral("kindInformations")),
@@ -627,6 +633,9 @@ Terminal *Element::parseTerminal(const QDomElement &dom_element)
Terminal *new_terminal = new Terminal(data, this);
m_terminals << new_terminal;
connect(new_terminal, &Terminal::conductorWasAdded, this, &Element::updateConductorTexts);
connect(new_terminal, &Terminal::conductorWasRemoved, this, &Element::updateConductorTexts);
//Sort from top to bottom and left to right
std::sort(m_terminals.begin(),
m_terminals.end(),
@@ -1288,6 +1297,8 @@ QString Element::linkTypeToString() const
return QStringLiteral("Terminale");
case Thumbnail:
return QStringLiteral("Thumbnail");
case ConductorDefinition:
return QStringLiteral("ConductorDefinition");
default:
return QStringLiteral("Unknown");
}
@@ -1555,3 +1566,25 @@ ElementsLocation Element::location() const
{
return m_location;
}
/**
* @brief Element::updateConductorTexts
*Slot that is triggered when a cable is *
*connected to or disconnected from a terminal on this component.
*/
/**
* @brief Element::updateConductorTexts
*/
void Element::updateConductorTexts()
{
if (m_link_type != Element::ConductorDefinition) {
return;
}
for (DynamicElementTextItem *deti : m_dynamic_text_list) {
if (deti) {
deti->setPotentialConductor();
deti->updateLabel();
}
}
}
+11 -8
View File
@@ -52,14 +52,15 @@ class Element : public QetGraphicsItem
(master, slave, report ect...)
*/
enum kind {
Simple = 1,
NextReport = 2,
PreviousReport = 4,
AllReport = 6,
Master = 8,
Slave = 16,
Terminale = 32,
Thumbnail = 64};
Simple = 1,
NextReport = 2,
PreviousReport = 4,
AllReport = 6,
Master = 8,
Slave = 16,
Terminale = 32,
Thumbnail = 64,
ConductorDefinition = 128};
Element(const ElementsLocation &location,
QGraphicsItem * = nullptr,
@@ -95,6 +96,8 @@ class Element : public QetGraphicsItem
DynamicElementTextItem *text,
ElementTextItemGroup *group);
public slots:
void updateConductorTexts();
public:
QList<Terminal *> terminals() const;
+9
View File
@@ -753,6 +753,15 @@ QString Terminal::name() const
return d->m_name;
}
/**
@brief Terminal::terminalType
@return the type of this terminal (Generic, Inner, Outer, No, Nc, Common)
*/
TerminalData::Type Terminal::terminalType() const
{
return d->m_type;
}
/**
@brief Conductor::relatedPotentialTerminal
Return terminal at the same potential from the same
+3 -1
View File
@@ -18,13 +18,14 @@
#ifndef TERMINAL_H
#define TERMINAL_H
#include "../qet.h"
#include "../properties/terminaldata.h"
#include <QtWidgets>
#include <QtXml>
class Conductor;
class Diagram;
class Element;
class TerminalData;
/**
@brief The Terminal class
@@ -75,6 +76,7 @@ class Terminal : public QGraphicsObject
Element *parentElement () const;
QUuid uuid () const;
QString name () const;
TerminalData::Type terminalType() const;
QList<Conductor *> conductors() const;
Qet::Orientation orientation() const;