diff --git a/sources/qetgraphicsitem/crossrefitem.cpp b/sources/qetgraphicsitem/crossrefitem.cpp index fea2754be..d0c0c9706 100644 --- a/sources/qetgraphicsitem/crossrefitem.cpp +++ b/sources/qetgraphicsitem/crossrefitem.cpp @@ -117,6 +117,10 @@ void CrossRefItem::setUpConnection() m_update_connection << connect(m_element, &Element::rotationChanged, this, &CrossRefItem::autoPos); set=true; } + // For PLC masters, always set up connections for update notifications + // (page reorder, diagram removal, etc.) + if (!set && m_element->elementData().m_master_type == ElementData::PLC && !m_text && !m_group) + set = true; if(set) { @@ -234,9 +238,24 @@ void CrossRefItem::updateLabel() qp.setPen(pen_); qp.setFont(QETApp::diagramTextsFont(5)); - // PLC table is drawn by Element::drawPlcTable(), not by CrossRefItem + // PLC table is drawn and managed entirely by CrossRefItem if (m_element->elementData().m_master_type == ElementData::PLC) + { + // Position at the PLC table position from the .elmt definition + QList positions = m_element->plcTablePositions(); + QPointF pos = positions.isEmpty() ? QPointF(0, 0) : positions.first(); + setPos(pos); + + // Populate m_hovered_contacts_map using drawAsPlcTable on a + // dummy painter (m_update_map=true). + m_update_map = true; + drawAsPlcTable(qp); + m_update_map = false; + + update(); + QTimer::singleShot(0, this, [this]{ update(); }); return; + } //Draw cross or contact, only if master element is linked. else if (! m_element->linkedElements().isEmpty()) { @@ -263,6 +282,11 @@ void CrossRefItem::updateLabel() */ void CrossRefItem::autoPos() { + // For PLC masters, position is set by updateLabel() based on + // m_plc_table_positions - don't override it here. + if (m_element->elementData().m_master_type == ElementData::PLC) + return; + //We calculate the position according to the snapTo of the xrefproperties if (m_properties.snapTo() == XRefProperties::Bottom) QGIUtility::centerToBottomDiagram(this, @@ -328,7 +352,9 @@ void CrossRefItem::paint( // confirmed by analysis of 19+ coredumps. // m_update_map=false: draw functions do not overwrite m_hovered_contacts_map. - // PLC table is drawn by Element::drawPlcTable(), not by CrossRefItem + // PLC: do not draw here (Element::drawPlcTable handles visual rendering). + // The m_hovered_contacts_map was populated in updateLabel() for + // click navigation and PDF hyperlink injection. if (m_element->elementData().m_master_type == ElementData::PLC) return; @@ -1378,11 +1404,24 @@ void CrossRefItem::drawAsPlcTable(QPainter &painter) headers[COL_COMMENT] = QObject::tr("Commentaire"); headers[COL_CROSSREF] = QObject::tr("Réf. croisée"); - // Build list of visible columns + // Build visible columns (must match Element::drawPlcTable logic) QList visible_cols; - for (int i = 0; i < COL_COUNT; ++i) { - if (plc_data.colVisible.value(i, true)) - visible_cols.append(i); + if (!plc_data.columnOrder.isEmpty()) { + for (int logical : plc_data.columnOrder) { + if (logical >= 0 && logical < COL_COUNT + && plc_data.colVisible.value(logical, true) + && !visible_cols.contains(logical)) + visible_cols.append(logical); + } + for (int i = 0; i < COL_COUNT; ++i) { + if (plc_data.colVisible.value(i, true) && !visible_cols.contains(i)) + visible_cols.append(i); + } + } else { + for (int i = 0; i < COL_COUNT; ++i) { + if (plc_data.colVisible.value(i, true)) + visible_cols.append(i); + } } if (visible_cols.isEmpty()) return; @@ -1405,7 +1444,7 @@ void CrossRefItem::drawAsPlcTable(QPainter &painter) } qreal row_h = plc_data.rowHeight > 0 ? plc_data.rowHeight : 8.0; - qreal header_h = row_h + 2.0; + qreal header_h = plc_data.showHeaders ? (row_h + 2.0) : 0; // Calculate total width qreal total_width = 0; @@ -1431,6 +1470,7 @@ void CrossRefItem::drawAsPlcTable(QPainter &painter) qreal total_height; int block_count = block_starts.size(); + qreal block_total_width = total_width; // width of one block, before multi-block scaling if (block_count > 1) { int max_rows = 0; @@ -1455,7 +1495,7 @@ void CrossRefItem::drawAsPlcTable(QPainter &painter) // Draw header row for (int block = 0; block < block_count; ++block) { - qreal block_x = block * (col_widths.value(visible_cols.first(), 30) * visible_cols.size() + 3); + qreal block_x = block * (block_total_width + 3); qreal cx = block_x; // Draw column headers diff --git a/sources/qetgraphicsitem/dynamicelementtextitem.cpp b/sources/qetgraphicsitem/dynamicelementtextitem.cpp index a9af725ae..a93ed5d83 100644 --- a/sources/qetgraphicsitem/dynamicelementtextitem.cpp +++ b/sources/qetgraphicsitem/dynamicelementtextitem.cpp @@ -252,6 +252,29 @@ ElementTextItemGroup *DynamicElementTextItem::parentGroup() const return nullptr; } +/** + @brief DynamicElementTextItem::masterElement + @return the master element for slave elements, or the parent element for others. + For PLC slaves, returns the actual master from linkedElements() instead of + m_master_element (which points to self because elementUseForInfo() returns + self for PLC slaves to resolve %{plc_*} variables). +*/ +Element *DynamicElementTextItem::masterElement() const +{ + Element *elmt = parentElement(); + if (!elmt) + return m_master_element.data(); + + if (elmt->linkType() == Element::Slave && + elmt->elementData().m_slave_type == ElementData::PLCSlave && + !elmt->linkedElements().isEmpty()) + { + return elmt->linkedElements().first(); + } + + return m_master_element.data(); +} + /** @brief DynamicElementTextItem::elementUseForInfo @return a pointer to the element we must use for the variable information. diff --git a/sources/qetgraphicsitem/dynamicelementtextitem.h b/sources/qetgraphicsitem/dynamicelementtextitem.h index 935a98006..b6eb5c4cb 100644 --- a/sources/qetgraphicsitem/dynamicelementtextitem.h +++ b/sources/qetgraphicsitem/dynamicelementtextitem.h @@ -92,7 +92,7 @@ class DynamicElementTextItem : public DiagramTextItem /// DXF export: the master-side cross-reference item (the table/cross /// drawn next to a report/master element), if this text item has one. CrossRefItem *masterXrefItem() const { return m_Xref_item; } - Element *masterElement() const { return m_master_element.data(); } + Element *masterElement() const; ElementTextItemGroup *parentGroup() const; Element *elementUseForInfo() const; void refreshLabelConnection(); diff --git a/sources/qetgraphicsitem/element.h b/sources/qetgraphicsitem/element.h index 03b2ed5da..9e94d2c4e 100644 --- a/sources/qetgraphicsitem/element.h +++ b/sources/qetgraphicsitem/element.h @@ -266,6 +266,10 @@ class Element : public QetGraphicsItem void drawPlcTable(QPainter *painter); + public: + /// Positions where the PLC IO table is drawn (from the .elmt file). + QList plcTablePositions() const { return m_plc_table_positions; } + private: bool m_must_highlight = false; QSize dimensions; diff --git a/sources/qetgraphicsitem/masterelement.cpp b/sources/qetgraphicsitem/masterelement.cpp index 28d03085e..14659bb72 100644 --- a/sources/qetgraphicsitem/masterelement.cpp +++ b/sources/qetgraphicsitem/masterelement.cpp @@ -158,6 +158,14 @@ QVariant MasterElement::itemChange(QGraphicsItem::GraphicsItemChange change, con { m_first_scene_change = false; connect(diagram()->project(), &QETProject::XRefPropertiesChanged, this, &MasterElement::xrefPropertiesChanged); + + // For PLC masters, create the CrossRefItem immediately so the + // IO table is visible even without linked slaves. + if (m_data.m_master_type == ElementData::PLC && !m_Xref_item) + { + m_Xref_item = new CrossRefItem(this); + m_Xref_item->updateLabel(); + } } return Element::itemChange(change, value); }