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

Generated by: LCOV version 2.0-1