diff --git a/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp b/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp index fb208e383..7107b152a 100644 --- a/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp +++ b/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp @@ -416,6 +416,7 @@ void TerminalStripDrawer::paint(QPainter *painter) if (m_strip && m_pattern) { m_united_xref_text_rect = QRectF(); + m_xref_rects.clear(); //To draw text, QPainter need a Qrect. Instead of create an instance //for each text, we re-use the same instance of QRect. QRect text_rect; @@ -612,6 +613,9 @@ void TerminalStripDrawer::paint(QPainter *painter) auto xref_string = shared_real_terminal->xref(); const auto mapped_xref_text_rect = transform.mapRect(painter->boundingRect(xref_rect, xref_string, xref_text_option)); + if (!xref_string.isEmpty()) { + m_xref_rects.append({mapped_xref_text_rect, physical_index, i}); + } if (m_united_xref_text_rect.isNull()) { m_united_xref_text_rect = mapped_xref_text_rect; } else { @@ -792,6 +796,16 @@ hoverTerminal TerminalStripDrawer::hoveredXref() const return m_hovered_xref; } +/** + * @brief TerminalStripDrawer::xrefRects + * @return the rectangle of every xref text drawn by the last call + * of paint(), in the coordinates of the painter. + */ +QVector TerminalStripDrawer::xrefRects() const +{ + return m_xref_rects; +} + qreal TerminalStripDrawer::height() const { if (m_pattern) diff --git a/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.h b/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.h index 3c7c313f8..68d2eca2c 100644 --- a/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.h +++ b/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.h @@ -40,6 +40,18 @@ namespace TerminalStripDrawer int real{-1}; }; + /** + * @brief The XrefRect struct + * Where the xref text of a real terminal was drawn, in the coordinates + * of the item, with the physical and real index of that terminal. + * Filled by paint(): used to make the xref clickable in an exported PDF. + */ + struct XrefRect{ + QRectF rect; + int physical{-1}; + int real{-1}; + }; + class AbstractBridgeInterface { public: @@ -105,6 +117,7 @@ namespace TerminalStripDrawer bool mouseHoverXref() const; bool needUpdate(); hoverTerminal hoveredXref() const; + QVector xrefRects() const; private: qreal height() const; @@ -118,6 +131,7 @@ namespace TerminalStripDrawer QRectF m_united_xref_text_rect; bool m_last_mouse_pos_in_xrefs_rect{false}; hoverTerminal m_hovered_xref; + QVector m_xref_rects; }; } diff --git a/sources/TerminalStrip/GraphicsItem/terminalstripitem.cpp b/sources/TerminalStrip/GraphicsItem/terminalstripitem.cpp index 3138c266f..fc706fc1b 100644 --- a/sources/TerminalStrip/GraphicsItem/terminalstripitem.cpp +++ b/sources/TerminalStrip/GraphicsItem/terminalstripitem.cpp @@ -96,6 +96,45 @@ QString TerminalStripItem::name() const { return tr("plan of terminals"); } +/** + * @brief TerminalStripItem::pdfRefs + * @return the xref texts of the plan which lead to an element, + * as the folio and rectangles needed to make them clickable in a PDF. + * The rectangles are those of the last paint, so call this after + * the item was rendered. + */ +QList TerminalStripItem::pdfRefs() const +{ + QList refs; + if (m_strip.isNull()) { + return refs; + } + + const auto xref_rects = m_drawer.xrefRects(); + for (const auto &xref_rect : xref_rects) + { + const auto physical_terminal = m_strip->physicalTerminal(xref_rect.physical); + if (physical_terminal.isNull()) { + continue; + } + const auto real_terminal = physical_terminal->realTerminal(xref_rect.real); + if (real_terminal.isNull() || !real_terminal->isElement()) { + continue; + } + const QPointer element = real_terminal->element(); + if (element.isNull() || !element->diagram()) { + continue; + } + + PdfRef ref; + ref.diagram = element->diagram(); + ref.here = mapRectToScene(xref_rect.rect); + ref.there = element->mapRectToScene(element->boundingRect()); + refs.append(ref); + } + return refs; +} + void TerminalStripItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { QetGraphicsItem::hoverMoveEvent(event); diff --git a/sources/TerminalStrip/GraphicsItem/terminalstripitem.h b/sources/TerminalStrip/GraphicsItem/terminalstripitem.h index 7596f5da1..b5c29c67c 100644 --- a/sources/TerminalStrip/GraphicsItem/terminalstripitem.h +++ b/sources/TerminalStrip/GraphicsItem/terminalstripitem.h @@ -19,11 +19,14 @@ #define TERMINALSTRIPITEM_H #include +#include +#include #include #include "terminalstripdrawer.h" #include "../../qetgraphicsitem/qetgraphicsitem.h" +class Diagram; class TerminalStrip; class TerminalStripItem : public QetGraphicsItem @@ -52,6 +55,22 @@ class TerminalStripItem : public QetGraphicsItem void refreshPending(); void setLayout(QSharedPointer layout); + /** + * @brief The PdfRef struct + * One xref of the plan which leads to an element: used to make + * it a clickable link in an exported PDF. + */ + struct PdfRef + { + ///The folio of the element the xref points at + Diagram *diagram = nullptr; + ///Where the text of the xref stands on the folio of this item + QRectF here; + ///Where the element stands on its folio + QRectF there; + }; + QList pdfRefs() const; + private: void setDefaultLayout(); diff --git a/sources/pdf_links.cpp b/sources/pdf_links.cpp index 4fc9fc357..039365009 100644 --- a/sources/pdf_links.cpp +++ b/sources/pdf_links.cpp @@ -23,6 +23,7 @@ #include "qetgraphicsitem/dynamicelementtextitem.h" #include "qetgraphicsitem/element.h" #include "qetgraphicsitem/elementtextitemgroup.h" +#include "TerminalStrip/GraphicsItem/terminalstripitem.h" // Private Qt PDF engine for drawHyperlink() — not public API. // Availability of Qt::GuiPrivate is verified at configure time in CMakeLists.txt. @@ -150,6 +151,14 @@ void injectCrossRefLinks(QPdfEngine *engine, Diagram *diagram, continue; } + // --- Terminal strip plan: the xref of each terminal leads to its element --- + if (auto *strip_item = dynamic_cast(item)) { + for (const TerminalStripItem::PdfRef &ref : strip_item->pdfRefs()) { + injectLinkTo(ref.here, ref.diagram, ref.there); + } + continue; + } + // --- Folio report links (DynamicElementTextItem) --- if (auto *deti = dynamic_cast(item)) { Element *parent = deti->parentElement();