LCOV - code coverage report
Current view: top level - dbe/apps/SchemaEditor - SchemaGraphicSegmentedArrow.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 0.0 % 211 0
Test Date: 2026-10-11 15:05:20 Functions: 0.0 % 14 0

            Line data    Source code
       1              : /// Including QT Headers
       2              : #include <QPainter>
       3              : #include <QPen>
       4              : #include <QToolTip>
       5              : /// Including Schema Editor
       6              : #include "dbe/SchemaGraphicSegmentedArrow.hpp"
       7              : #include "dbe/SchemaKernelWrapper.hpp"
       8              : #include "dbe/SchemaStyle.hpp"
       9              : 
      10              : /// Including Oks Headers
      11              : #include "oks/class.hpp"
      12              : 
      13              : /// Including C++ Headers
      14              : #include <cmath>
      15              : 
      16              : namespace dbse {
      17              : 
      18            0 : SchemaGraphicSegmentedArrow::SchemaGraphicSegmentedArrow(SchemaGraphicObject* start_item,
      19              :                                                          SchemaGraphicObject* end_item,
      20              :                                                          int connection_count,
      21              :                                                          bool is_inheritance,
      22              :                                                          bool is_composite,
      23              :                                                          QString arrow_name,
      24              :                                                          QString arrow_cardinality,
      25            0 :                                                          QGraphicsItem* parent)
      26              :   : QGraphicsPathItem(parent)
      27            0 :   , m_start_item(start_item)
      28            0 :   , m_end_item(end_item)
      29            0 :   , m_connection_count(connection_count)
      30            0 :   , m_inheritance(is_inheritance)
      31            0 :   , m_composite(is_composite)
      32            0 :   , m_name(arrow_name)
      33            0 :   , m_cardinality(arrow_cardinality)
      34            0 :   , LastDegree(0)
      35            0 :   , LastRotation(0)
      36              : {
      37            0 :   if (!is_inheritance) {
      38            0 :     setAcceptHoverEvents(true);
      39              :   }
      40              :   // setPen(QPen(Qt::black,2,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin));
      41            0 :   setFlag(ItemIsSelectable, true);
      42            0 :   m_arrow_size = 20;
      43            0 : }
      44              : 
      45            0 : SchemaGraphicSegmentedArrow::~SchemaGraphicSegmentedArrow() {}
      46              : 
      47              : void
      48            0 : dbse::SchemaGraphicSegmentedArrow::hoverEnterEvent(QGraphicsSceneHoverEvent* he)
      49              : {
      50            0 :   if (!m_name.isEmpty()) {
      51            0 :     auto info = m_start_item->GetClass();
      52              : 
      53            0 :     auto rel = info->find_relationship(m_name.toStdString());
      54              : 
      55            0 :     QToolTip::showText(he->screenPos(), QString::fromStdString(rel->get_description()));
      56              :   }
      57            0 :   he->ignore();
      58            0 : }
      59              : void
      60            0 : dbse::SchemaGraphicSegmentedArrow::hoverLeaveEvent(QGraphicsSceneHoverEvent* he)
      61              : {
      62            0 :   QToolTip::hideText();
      63            0 :   he->ignore();
      64            0 : }
      65              : 
      66              : QRectF
      67            0 : SchemaGraphicSegmentedArrow::boundingRect() const
      68              : {
      69            0 :   if (m_start_item->collidesWithItem(m_end_item)) {
      70            0 :     return QRectF();
      71              :   }
      72              : 
      73            0 :   qreal extra = (pen().width() + 20) / 2.0 + 10;
      74            0 :   auto br =
      75            0 :     QRectF(p1(), QSizeF(p2().x() - p1().x(), p2().y() - p1().y())).normalized().adjusted(-extra, -extra, extra, extra);
      76            0 :   br = br.united(m_rel_label_br);
      77            0 :   br = br.united(m_rel_cardinality_br);
      78            0 :   return br;
      79              : }
      80              : 
      81              : QPainterPath
      82            0 : SchemaGraphicSegmentedArrow::shape() const
      83              : {
      84            0 :   QPainterPath path = QGraphicsPathItem::shape();
      85            0 :   path.addRect(m_rel_label_br);
      86            0 :   path.addRect(m_rel_cardinality_br);
      87            0 :   path.addPolygon(m_marker);
      88              :   // path.addText ( p2() + QPoint ( 10, 10 ), QFont ( "Helvetica [Cronyx]", 10 ),
      89              :   //  m_cardinality );
      90              :   // path.addText ( p1() + QPoint ( -10, -10 ), QFont ( "Helvetica [Cronyx]", 10 ),
      91              :   //  m_cardinality );
      92            0 :   return path;
      93            0 : }
      94              : 
      95              : void
      96            0 : SchemaGraphicSegmentedArrow::UpdatePosition()
      97              : {
      98            0 :   if (m_start_item->collidesWithItem(m_end_item)) {
      99              :     // this->setPath(QPainterPath());
     100              :     // m_marker = QPolygonF();
     101            0 :     return;
     102              :   }
     103              : 
     104            0 :   std::vector<QLineF> norms = {
     105              :     { 0., 0., 0., 1. },  // up
     106              :     { 0., 0., 1., 0. },  // right
     107              :     { 0., 0., 0, -1. },  // down
     108              :     { 0., 0., -1., 0. }, // left
     109            0 :   };
     110              : 
     111            0 :   qreal xoffset;
     112            0 :   qreal yoffset;
     113            0 :   const qreal xfactor = 23.0;
     114            0 :   const qreal yfactor = 17.0;
     115            0 :   if (m_start_item->boundingRect().x() < m_end_item->boundingRect().x()) {
     116            0 :     xoffset = m_connection_count * xfactor;
     117              :   } else {
     118            0 :     xoffset = m_connection_count * -xfactor;
     119              :   }
     120              : 
     121            0 :   if (m_start_item->boundingRect().y() > m_end_item->boundingRect().y()) {
     122            0 :     yoffset = m_connection_count * yfactor;
     123              :   } else {
     124            0 :     yoffset = m_connection_count * -yfactor;
     125              :   }
     126              : 
     127            0 :   QPointF start_offset(xoffset, yoffset);
     128            0 :   QPointF end_offset(0, 0);
     129              : 
     130            0 :   QLineF center_line(m_start_item->mapToScene(m_start_item->boundingRect().center() + start_offset),
     131            0 :                      m_end_item->mapToScene(m_end_item->boundingRect().center() + end_offset));
     132            0 :   QPolygonF start_polygon = QPolygonF(m_start_item->boundingRect());
     133            0 :   QPolygonF end_polygon = QPolygonF(m_end_item->boundingRect());
     134            0 :   QPointF intersect_point_start, intersect_point_end;
     135            0 :   QLineF intersect_norm_start, intersect_norm_end;
     136              : 
     137              :   // Iterate on the sides starting from top-left
     138            0 :   QPointF p1 = end_polygon.first() + m_end_item->pos(), p2;
     139            0 :   for (int i = 1; i < end_polygon.count(); ++i) {
     140              : 
     141            0 :     p2 = end_polygon.at(i) + m_end_item->pos();
     142            0 :     QLineF item_side = QLineF(p1, p2);
     143            0 :     QLineF::IntersectType intersect_type = item_side.intersects(center_line, &intersect_point_end);
     144              : 
     145            0 :     if (intersect_type == QLineF::BoundedIntersection) {
     146            0 :       intersect_norm_end = norms[i - 1];
     147            0 :       break;
     148              :     }
     149              : 
     150            0 :     p1 = p2;
     151              :   }
     152              : 
     153            0 :   p1 = start_polygon.first() + m_start_item->pos();
     154            0 :   QPointF start_sp;
     155            0 :   for (int i = 1; i < start_polygon.count(); ++i) {
     156            0 :     p2 = start_polygon.at(i) + m_start_item->pos();
     157            0 :     QLineF item_side = QLineF(p1, p2);
     158            0 :     QLineF::IntersectType intersect_type = item_side.intersects(center_line, &intersect_point_start);
     159            0 :     if (intersect_type == QLineF::BoundedIntersection) {
     160            0 :       switch (i) {
     161            0 :         case 1:
     162            0 :           start_sp = QPointF{ m_start_item->pos().x() + (m_start_item->boundingRect().width() / 2) + xoffset,
     163            0 :                               intersect_point_start.y() };
     164            0 :           break;
     165            0 :         case 3:
     166            0 :           start_sp = QPointF{ m_start_item->pos().x() + (m_start_item->boundingRect().width() / 2) - xoffset,
     167            0 :                               intersect_point_start.y() };
     168            0 :           break;
     169              : 
     170            0 :         case 2:
     171            0 :         case 4:
     172            0 :           start_sp = { intersect_point_start.x(),
     173            0 :                        m_start_item->pos().y() + m_start_item->boundingRect().height() / 2 + yoffset };
     174            0 :           break;
     175              :       }
     176            0 :       intersect_norm_start = norms[i - 1];
     177            0 :       break;
     178              :     }
     179              : 
     180            0 :     p1 = p2;
     181              :   }
     182              : 
     183            0 :   QLineF direct_line(start_sp, intersect_point_end);
     184            0 :   auto font = QFont(SchemaStyle::get_font("line"));
     185            0 :   auto label_br = QRectF(QFontMetrics(font).boundingRect(m_name));
     186              :   // Center rectangle on origin
     187            0 :   label_br.translate(-label_br.width() / 2, label_br.height() / 2);
     188              : 
     189            0 :   auto cardinality_br = QRectF(QFontMetrics(font).boundingRect(m_cardinality));
     190              :   // Center rectangle on origin
     191            0 :   cardinality_br.translate(-cardinality_br.width() / 2, cardinality_br.height() / 2);
     192              : 
     193            0 :   qreal label_x_padding = 2;
     194            0 :   qreal label_y_padding = -2;
     195            0 :   qreal card_x_padding = 2;
     196            0 :   qreal card_y_padding = -2;
     197              : 
     198            0 :   QPainterPath path(intersect_point_start);
     199              : 
     200            0 :   if ((intersect_norm_start.dx() == 0) && (intersect_norm_end.dx() == 0)) {
     201              :     // Three segments, starting and ending vertically
     202            0 :     QPointF wp1 = QPointF(direct_line.x1(), direct_line.center().y() + yoffset);
     203            0 :     QPointF wp2 = QPointF(direct_line.x2(), direct_line.center().y() + yoffset);
     204            0 :     path.moveTo(direct_line.x1(), direct_line.y1());
     205            0 :     path.lineTo(wp1);
     206            0 :     path.lineTo(wp2);
     207              : 
     208              :     // Place the label
     209            0 :     label_br.translate(wp2 + QPointF((direct_line.dx() < 0 ? 1 : -1) * (label_x_padding + label_br.width() / 2),
     210            0 :                                      (direct_line.dy() > 0 ? 1 : -1) * (label_br.height() + label_y_padding)));
     211              :     // Place cardinality
     212            0 :     cardinality_br.translate(intersect_point_start +
     213            0 :                              QPointF((direct_line.dx() < 0 ? 1 : -1) * (card_x_padding + cardinality_br.width() / 2),
     214            0 :                                      (direct_line.dy() > 0 ? 1 : -1) * (cardinality_br.height() + card_y_padding)));
     215              : 
     216            0 :   } else if ((intersect_norm_start.dy() == 0) && (intersect_norm_end.dy() == 0)) {
     217              :     // Three segments, starting and ending horizontally
     218            0 :     QPointF wp1 = QPointF(direct_line.center().x() - xoffset, direct_line.y1());
     219            0 :     QPointF wp2 = QPointF(direct_line.center().x() - xoffset, direct_line.y2());
     220              : 
     221            0 :     path.moveTo(direct_line.x1(), direct_line.y1());
     222            0 :     path.lineTo(wp1);
     223            0 :     path.lineTo(wp2);
     224              : 
     225              :     // Place the label
     226            0 :     label_br.translate(wp2 + QPointF((direct_line.dx() > 0 ? 1 : -1) * (label_x_padding + label_br.width() / 2),
     227            0 :                                      (direct_line.dy() < 0 ? 1 : -1) * (label_br.height() + label_y_padding)));
     228            0 :     cardinality_br.translate(intersect_point_start +
     229            0 :                              QPointF((direct_line.dx() > 0 ? 1 : -1) * (card_x_padding + cardinality_br.width() / 2),
     230            0 :                                      (direct_line.dy() < 0 ? 1 : -1) * (cardinality_br.height() + card_y_padding)));
     231              : 
     232            0 :   } else if (intersect_norm_start.dx() == 0) {
     233              :     // Two segments, starting vertically, ending horizontally
     234            0 :     QPointF wp1 = QPointF(direct_line.x1(), direct_line.y2());
     235              : 
     236            0 :     path.moveTo(direct_line.x1(), direct_line.y1());
     237            0 :     path.lineTo(wp1);
     238              : 
     239            0 :     label_br.translate(wp1 + QPointF((direct_line.dx() > 0 ? 1 : -1) * (label_x_padding + label_br.width() / 2),
     240            0 :                                      (direct_line.dy() < 0 ? 1 : -1) * (label_br.height() + label_y_padding)));
     241            0 :     cardinality_br.translate(intersect_point_start +
     242            0 :                              QPointF((direct_line.dx() < 0 ? 1 : -1) * (card_x_padding + cardinality_br.width() / 2),
     243            0 :                                      (direct_line.dy() > 0 ? 1 : -1) * (cardinality_br.height() + card_y_padding)));
     244              : 
     245            0 :   } else if (intersect_norm_start.dy() == 0) {
     246              :     // Two segments, starting horizontally, ending vertically
     247            0 :     QPointF wp1 = QPointF(direct_line.x2(), direct_line.y1());
     248            0 :     path.moveTo(direct_line.x1(), direct_line.y1());
     249              : 
     250            0 :     path.lineTo(wp1);
     251              : 
     252            0 :     label_br.translate(wp1 + QPointF((direct_line.dx() > 0 ? 1 : -1) * (label_x_padding + label_br.width() / 2),
     253            0 :                                      (direct_line.dy() < 0 ? 1 : -1) * (label_br.height() + label_y_padding)));
     254            0 :     cardinality_br.translate(intersect_point_start +
     255            0 :                              QPointF((direct_line.dx() < 0 ? 1 : -1) * (card_x_padding + cardinality_br.width() / 2),
     256            0 :                                      (direct_line.dy() > 0 ? 1 : -1) * (cardinality_br.height() + card_y_padding)));
     257              :   }
     258              : 
     259            0 :   path.lineTo(intersect_point_end);
     260            0 :   setPath(path);
     261              : 
     262            0 :   m_rel_label_br = label_br;
     263            0 :   m_rel_cardinality_br = cardinality_br;
     264              : 
     265              :   // Why does a function called UpdatePosition add decorations to a line??
     266              :   // =====================================================================
     267            0 :   if (m_inheritance) {
     268            0 :     m_marker = this->make_arrow_head(-intersect_norm_end.angle() / 180 * M_PI).translated(this->p2());
     269              :   } else {
     270            0 :     m_marker = this->make_rhombus(-intersect_norm_start.angle() / 180 * M_PI).translated(this->p1());
     271              :   }
     272            0 : }
     273              : 
     274              : SchemaGraphicObject*
     275            0 : SchemaGraphicSegmentedArrow::GetStartItem() const
     276              : {
     277            0 :   return m_start_item;
     278              : }
     279              : 
     280              : SchemaGraphicObject*
     281            0 : SchemaGraphicSegmentedArrow::GetEndItem() const
     282              : {
     283            0 :   return m_end_item;
     284              : }
     285              : 
     286              : bool
     287            0 : SchemaGraphicSegmentedArrow::GetInheritanceMode()
     288              : {
     289            0 :   return m_inheritance;
     290              : }
     291              : 
     292              : void
     293            0 : SchemaGraphicSegmentedArrow::RemoveArrow()
     294              : {
     295            0 :   if (m_inheritance) {
     296              :     /// Remove Super class
     297              :   } else {
     298              :     /// Remove relationship
     299              :   }
     300            0 : }
     301              : 
     302              : QPolygonF
     303            0 : SchemaGraphicSegmentedArrow::make_arrow_head(qreal rotation) const
     304              : {
     305              : 
     306            0 :   float opening_angle = atan2(1, 0.5);
     307            0 :   qreal size = m_arrow_size;
     308            0 :   QPointF a1 = QPointF(sin(rotation + opening_angle) * size, cos(rotation + opening_angle) * size);
     309            0 :   QPointF a2 = QPointF(sin(rotation + M_PI - opening_angle) * size, cos(rotation + M_PI - opening_angle) * size);
     310              : 
     311            0 :   QPolygonF arrow;
     312            0 :   arrow << QPointF(0, 0) << a1 << a2;
     313            0 :   return arrow;
     314            0 : }
     315              : 
     316              : QPolygonF
     317            0 : SchemaGraphicSegmentedArrow::make_rhombus(qreal rotation) const
     318              : {
     319              : 
     320            0 :   float opening_angle = M_PI / 3;
     321            0 :   qreal size = 0.6 * m_arrow_size;
     322              : 
     323            0 :   QPointF a1 = QPointF(sin(rotation + opening_angle) * size, cos(rotation + opening_angle) * size);
     324            0 :   QPointF a2 = QPointF(sin(rotation + M_PI - opening_angle) * size, cos(rotation + M_PI - opening_angle) * size);
     325              : 
     326            0 :   QPointF m = QPointF((a1.x() + a2.x()) / 2, (a1.y() + a2.y()) / 2);
     327            0 :   QPointF a3 = QPointF(2 * m.x(), 2 * m.y());
     328              : 
     329            0 :   QPolygonF rhombus;
     330            0 :   rhombus << QPointF(0, 0) << a1 << a3 << a2;
     331            0 :   return rhombus;
     332            0 : }
     333              : 
     334              : void
     335            0 : SchemaGraphicSegmentedArrow::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
     336              : {
     337            0 :   Q_UNUSED(option)
     338            0 :   Q_UNUSED(widget)
     339              : 
     340            0 :   if (m_start_item->collidesWithItem(m_end_item)) {
     341            0 :     return;
     342              :   }
     343              : 
     344            0 :   const QPen line_pen = QPen(SchemaStyle::get_color("foreground", "line"), 1);
     345            0 :   const QPen arrow_pen = QPen(SchemaStyle::get_color("foreground", "line"), 1);
     346              : 
     347            0 :   painter->setFont(QFont(SchemaStyle::get_font("line")));
     348            0 :   painter->setPen(line_pen);
     349            0 :   painter->setBrush({});
     350            0 :   painter->setRenderHint(QPainter::Antialiasing);
     351              : 
     352            0 :   painter->drawPath(this->path());
     353              : 
     354            0 :   painter->setPen(arrow_pen);
     355            0 :   if (!m_inheritance) {
     356            0 :     painter->drawText(m_rel_label_br, Qt::AlignTop | Qt::AlignLeft, QString(m_name));
     357            0 :     painter->drawText(m_rel_cardinality_br, Qt::AlignTop | Qt::AlignLeft, QString(m_cardinality));
     358              :   }
     359              : 
     360            0 :   if (m_inheritance) {
     361            0 :     painter->setBrush(Qt::white);
     362            0 :   } else if (m_composite) {
     363              :     /// Draw Rhombus
     364            0 :     painter->setBrush(SchemaStyle::get_color("foreground", "line"));
     365              : 
     366              :   } else {
     367              :     /// Draw Rhombus
     368            0 :     painter->setBrush(Qt::white);
     369              :   }
     370            0 :   painter->drawPolygon(m_marker);
     371            0 : }
     372              : 
     373              : } // namespace dbse
        

Generated by: LCOV version 2.0-1