Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
connectionedit.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3// Qt-Security score:significant reason:default
4
5
7
8#include <QtDesigner/abstractformwindow.h>
9
10#include <QtWidgets/qapplication.h>
11#include <QtWidgets/qmenu.h>
12
13#include <QtGui/qaction.h>
14#include <QtGui/qpainter.h>
15#include <QtGui/qevent.h>
16#include <QtGui/qfontmetrics.h>
17#include <QtGui/qpixmap.h>
18#include <QtGui/qtransform.h>
19
20#include <QtCore/qmap.h>
21
23
24static const int BG_ALPHA = 32;
25static const int LINE_PROXIMITY_RADIUS = 3;
26static const int LOOP_MARGIN = 20;
27static const int VLABEL_MARGIN = 1;
28static const int HLABEL_MARGIN = 3;
29static const int GROUND_W = 20;
30static const int GROUND_H = 25;
31
32/*******************************************************************************
33** Tools
34*/
35
36static QRect fixRect(const QRect &r)
37{
38 return QRect(r.x(), r.y(), r.width() - 1, r.height() - 1);
39}
40
41static QRect expand(const QRect &r, int i)
42{
43 return QRect(r.x() - i, r.y() - i, r.width() + 2*i, r.height() + 2*i);
44}
45
46static QRect endPointRectHelper(const QPoint &pos)
47{
48 const QRect r(pos + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS),
50 return r;
51}
52
53static void paintGround(QPainter *p, QRect r)
54{
55 const QPoint mid = r.center();
56 p->drawLine(mid.x(), r.top(), mid.x(), mid.y());
57 p->drawLine(r.left(), mid.y(), r.right(), mid.y());
58 int y = r.top() + 4*r.height()/6;
59 int x = GROUND_W/6;
60 p->drawLine(r.left() + x, y, r.right() - x, y);
61 y = r.top() + 5*r.height()/6;
62 x = 2*GROUND_W/6;
63 p->drawLine(r.left() + x, y, r.right() - x, y);
64 p->drawLine(mid.x(), r.bottom(), mid.x() + 1, r.bottom());
65}
66
67static void paintEndPoint(QPainter *p, const QPoint &pos)
68{
69 const QRect r(pos + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS),
71 p->fillRect(fixRect(r), p->pen().color());
72}
73
74static qdesigner_internal::CETypes::LineDir classifyLine(const QPoint &p1, const QPoint &p2)
75{
76 if (p1.x() == p2.x())
77 return p1.y() < p2.y() ? qdesigner_internal::CETypes::DownDir : qdesigner_internal::CETypes::UpDir;
78 Q_ASSERT(p1.y() == p2.y());
79 return p1.x() < p2.x() ? qdesigner_internal::CETypes::RightDir : qdesigner_internal::CETypes::LeftDir;
80}
81
82static QPoint pointInsideRect(const QRect &r, QPoint p)
83{
84 if (p.x() < r.left())
85 p.setX(r.left());
86 else if (p.x() > r.right())
87 p.setX(r.right());
88
89 if (p.y() < r.top())
90 p.setY(r.top());
91 else if (p.y() > r.bottom())
92 p.setY(r.bottom());
93
94 return p;
95}
96
97namespace qdesigner_internal {
98
99/*******************************************************************************
100** Commands
101*/
102
108
118
129
131{
132public:
133 AdjustConnectionCommand(ConnectionEdit *edit, Connection *con,
134 const QPoint &old_source_pos,
135 const QPoint &old_target_pos,
136 const QPoint &new_source_pos,
137 const QPoint &new_target_pos);
140private:
141 Connection *m_con;
142 const QPoint m_old_source_pos;
143 const QPoint m_old_target_pos;
144 const QPoint m_new_source_pos;
145 const QPoint m_new_target_pos;
146};
147
148AdjustConnectionCommand::AdjustConnectionCommand(ConnectionEdit *edit, Connection *con,
149 const QPoint &old_source_pos,
150 const QPoint &old_target_pos,
151 const QPoint &new_source_pos,
152 const QPoint &new_target_pos) :
154 m_con(con),
159{
160 setText(QApplication::translate("Command", "Adjust connection"));
161}
162
164{
165 m_con->setEndPoint(EndPoint::Source, m_con->widget(EndPoint::Source), m_old_source_pos);
166 m_con->setEndPoint(EndPoint::Target, m_con->widget(EndPoint::Target), m_old_target_pos);
167}
168
170{
171 m_con->setEndPoint(EndPoint::Source, m_con->widget(EndPoint::Source), m_new_source_pos);
172 m_con->setEndPoint(EndPoint::Target, m_con->widget(EndPoint::Target), m_new_target_pos);
173}
174
181
195
209
211{
212public:
213 SetEndPointCommand(ConnectionEdit *edit, Connection *con, EndPoint::Type type, QObject *object);
216private:
217 Connection *m_con;
218 const EndPoint::Type m_type;
219 QObject *m_old_widget, *m_new_widget;
220 const QPoint m_old_pos;
221 QPoint m_new_pos;
222};
223
224SetEndPointCommand::SetEndPointCommand(ConnectionEdit *edit, Connection *con,
225 EndPoint::Type type, QObject *object) :
227 m_con(con),
228 m_type(type),
232{
233 if (QWidget *widget = qobject_cast<QWidget*>(object)) {
234 m_new_pos = edit->widgetRect(widget).center();
235 }
236
237 if (m_type == EndPoint::Source)
238 setText(QApplication::translate("Command", "Change source"));
239 else
240 setText(QApplication::translate("Command", "Change target"));
241}
242
244{
245 m_con->setEndPoint(m_type, m_new_widget, m_new_pos);
246 emit edit()->connectionChanged(m_con);
247}
248
250{
251 m_con->setEndPoint(m_type, m_old_widget, m_old_pos);
252 emit edit()->connectionChanged(m_con);
253}
254
255/*******************************************************************************
256** Connection
257*/
258
260 m_source_pos(QPoint(-1, -1)),
261 m_target_pos(QPoint(-1, -1)),
262 m_source(nullptr),
263 m_target(nullptr),
264 m_edit(edit),
265 m_visible(true)
266{
267
268}
269
279
281{
282 m_visible = b;
283}
284
286{
289
290 if (source == nullptr || target == nullptr) {
291 setVisible(false);
292 return;
293 }
294
295 QWidget *w = source;
296 while (w && w->parentWidget()) {
297 if (!w->isVisibleTo(w->parentWidget())) {
298 setVisible(false);
299 return;
300 }
301 w = w->parentWidget();
302 }
303
304 w = target;
305 while (w && w->parentWidget()) {
306 if (!w->isVisibleTo(w->parentWidget())) {
307 setVisible(false);
308 return;
309 }
310 w = w->parentWidget();
311 }
312
313 setVisible(true);
314}
315
317{
318 return m_visible;
319}
320
321bool Connection::ground() const
322{
323 return m_target != nullptr && m_target == m_edit->m_bg_widget;
324}
325
330
331static QPoint lineEntryPos(const QPoint &p1, const QPoint &p2, const QRect &rect)
332{
333 QPoint result;
334
335 switch (classifyLine(p1, p2)) {
336 case CETypes::UpDir:
337 result = QPoint(p1.x(), rect.bottom());
338 break;
339 case CETypes::DownDir:
340 result = QPoint(p1.x(), rect.top());
341 break;
342 case CETypes::LeftDir:
343 result = QPoint(rect.right(), p1.y());
344 break;
345 case CETypes::RightDir:
346 result = QPoint(rect.left(), p1.y());
347 break;
348 }
349
350 return result;
351}
352
353static QPolygonF arrowHead(const QPoint &p1, const QPoint &p2)
354{
355 QPolygonF result;
356
357 switch (classifyLine(p1, p2)) {
358 case CETypes::UpDir:
359 result.append(p2 + QPoint(0, 1));
360 result.append(p2 + QPoint(LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS*2 + 1));
361 result.append(p2 + QPoint(-LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS*2 + 1));
362 break;
363 case CETypes::DownDir:
364 result.append(p2);
365 result.append(p2 + QPoint(LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS*2));
366 result.append(p2 + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS*2));
367 break;
368 case CETypes::LeftDir:
369 result.append(p2 + QPoint(1, 0));
370 result.append(p2 + QPoint(2*LINE_PROXIMITY_RADIUS + 1, -LINE_PROXIMITY_RADIUS));
371 result.append(p2 + QPoint(2*LINE_PROXIMITY_RADIUS + 1, LINE_PROXIMITY_RADIUS));
372 break;
373 case CETypes::RightDir:
374 result.append(p2);
375 result.append(p2 + QPoint(-2*LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS));
376 result.append(p2 + QPoint(-2*LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS));
377 break;
378 }
379
380 return result;
381}
382
383static CETypes::LineDir closestEdge(const QPoint &p, const QRect &r)
384{
385 CETypes::LineDir result = CETypes::UpDir;
386 int min = p.y() - r.top();
387
388 int d = p.x() - r.left();
389 if (d < min) {
390 min = d;
391 result = CETypes::LeftDir;
392 }
393
394 d = r.bottom() - p.y();
395 if (d < min) {
396 min = d;
397 result = CETypes::DownDir;
398 }
399
400 d = r.right() - p.x();
401 if (d < min) {
402 min = d;
403 result = CETypes::RightDir;
404 }
405
406 return result;
407}
408
409static bool pointAboveLine(const QPoint &l1, const QPoint &l2, const QPoint &p)
410{
411 if (l1.x() == l2.x())
412 return p.x() >= l1.x();
413 return p.y() <= l1.y() + (p.x() - l1.x())*(l2.y() - l1.y())/(l2.x() - l1.x());
414}
415
417{
420
423 const QRect sr = m_source_rect;
424 const QRect tr = m_target_rect;
425
428
429 if (m_source == nullptr || s == QPoint(-1, -1) || t == QPoint(-1, -1))
430 return;
431
432 const QRect r = sr | tr;
433
435 if (m_target == nullptr) {
436 m_knee_list.append(QPoint(t.x(), s.y()));
437 } else if (m_target == m_edit->m_bg_widget) {
438 m_knee_list.append(QPoint(s.x(), t.y()));
439 } else if (tr.contains(sr) || sr.contains(tr)) {
440/*
441 +------------------+
442 | +----------+ |
443 | | | |
444 | | o | |
445 | +---|------+ |
446 | | x |
447 +-----|-----|------+
448 +-----+
449
450 We find out which edge of the outer rectangle is closest to the target
451 point, and make a loop which exits and re-enters through that edge.
452*/
453 const LineDir dir = closestEdge(t, tr);
454 switch (dir) {
455 case UpDir:
458 break;
459 case DownDir:
462 break;
463 case LeftDir:
466 break;
467 case RightDir:
470 break;
471 }
472 } else {
473 if (r.height() < sr.height() + tr.height()) {
474 if ((s.y() >= tr.top() && s.y() <= tr.bottom()) || (t.y() >= sr.bottom() || t.y() <= sr.top())) {
475/*
476 +--------+
477 | | +--------+
478 | o--+---+--x |
479 | o | | |
480 +-----|--+ | |
481 +------+--x |
482 +--------+
483
484 When dragging one end point, move the other end point to the same y position,
485 if that does not cause it to exit it's rectangle.
486*/
487 if (m_edit->state() == ConnectionEdit::Dragging) {
489 const QPoint p(t.x(), s.y());
491 if (tr.contains(p))
492 t = m_target_pos = p;
493 } else {
494 const QPoint p(s.x(), t.y());
496 if (sr.contains(p))
497 s = m_source_pos = p;
498 }
499 } else {
500 m_knee_list.append(QPoint(s.x(), t.y()));
501 }
502 } else {
503/*
504 +--------+
505 | o----+-------+
506 | | +---|----+
507 +--------+ | | |
508 | x |
509 +--------+
510*/
511 m_knee_list.append(QPoint(t.x(), s.y()));
512 }
513 } else if (r.width() < sr.width() + tr.width()) {
514 if ((s.x() >= tr.left() && s.x() <= tr.right()) || t.x() >= sr.right() || t.x() <= sr.left()) {
515/*
516 +--------+
517 | |
518 | o o+--+
519 +----|---+ |
520 +-|------|-+
521 | x x |
522 | |
523 +----------+
524
525 When dragging one end point, move the other end point to the same x position,
526 if that does not cause it to exit it's rectangle.
527*/
528 if (m_edit->state() == ConnectionEdit::Dragging) {
530 const QPoint p(s.x(), t.y());
532 if (tr.contains(p))
533 t = m_target_pos = p;
534 } else {
535 const QPoint p(t.x(), s.y());
537 if (sr.contains(p))
538 s = m_source_pos = p;
539 }
540 } else {
541 m_knee_list.append(QPoint(t.x(), s.y()));
542 }
543 } else {
544/*
545 +--------+
546 | |
547 | o |
548 +--|-----+
549 | +--------+
550 +---+-x |
551 | |
552 +--------+
553
554*/
555 m_knee_list.append(QPoint(s.x(), t.y()));
556 }
557 } else {
558/*
559 +--------+
560 | |
561 | o o-+--------+
562 +--|-----+ |
563 | +-----|--+
564 | | x |
565 +--------+-x |
566 +--------+
567
568 The line enters the target rectangle through the closest edge.
569*/
570 if (sr.topLeft() == r.topLeft()) {
572 m_knee_list.append(QPoint(t.x(), s.y()));
573 else
574 m_knee_list.append(QPoint(s.x(), t.y()));
575 } else if (sr.topRight() == r.topRight()) {
577 m_knee_list.append(QPoint(t.x(), s.y()));
578 else
579 m_knee_list.append(QPoint(s.x(), t.y()));
580 } else if (sr.bottomRight() == r.bottomRight()) {
582 m_knee_list.append(QPoint(s.x(), t.y()));
583 else
584 m_knee_list.append(QPoint(t.x(), s.y()));
585 } else {
587 m_knee_list.append(QPoint(s.x(), t.y()));
588 else
589 m_knee_list.append(QPoint(t.x(), s.y()));
590 }
591 }
592 }
594
595 if (m_knee_list.size() == 2)
597
598 trimLine();
599
606}
607
608void Connection::trimLine()
609{
610 if (m_source == nullptr || m_source_pos == QPoint(-1, -1) || m_target_pos == QPoint(-1, -1))
611 return;
612 auto cnt = m_knee_list.size();
613 if (cnt < 2)
614 return;
615
616 const QRect sr = m_source_rect;
617 const QRect tr = m_target_rect;
618
619 if (sr.contains(m_knee_list.at(1)))
621
622 cnt = m_knee_list.size();
623 if (cnt < 2)
624 return;
625
626 if (!tr.contains(sr) && tr.contains(m_knee_list.at(cnt - 2)))
628
629 cnt = m_knee_list.size();
630 if (cnt < 2)
631 return;
632
635
636 if (tr.contains(m_knee_list.at(cnt - 1)) && !tr.contains(m_knee_list.at(cnt - 2))) {
637 m_knee_list[cnt - 1]
640 }
641}
642
644{
645 if (source == m_source && m_source_pos == pos)
646 return;
647
648 update(false);
649
655 }
656
657 update(false);
658}
659
661{
662 if (target == m_target && m_target_pos == pos)
663 return;
664
665 update(false);
666
672 }
673
674 update(false);
675}
676
677static QRect lineRect(const QPoint &a, const QPoint &b)
678{
679 const QPoint c(qMin(a.x(), b.x()), qMin(a.y(), b.y()));
680 const QPoint d(qMax(a.x(), b.x()), qMax(a.y(), b.y()));
681
682 QRect result(c, d);
683 return expand(result, LINE_PROXIMITY_RADIUS);
684}
685
687{
688 if (!ground())
689 return QRect();
690 if (m_knee_list.isEmpty())
691 return QRect();
692
693 const QPoint p = m_knee_list.last();
694 return QRect(p.x() - GROUND_W/2, p.y(), GROUND_W, GROUND_H);
695}
696
698{
700
701 for (qsizetype i = 0; i < m_knee_list.size() - 1; ++i)
703
704 if (!m_arrow_head.isEmpty()) {
706 r = expand(r, 1);
708 } else if (ground()) {
710 }
711
714
715 return result;
716}
717
719{
720 m_edit->update(region());
721 if (update_widgets) {
722 if (m_source != nullptr)
724 if (m_target != nullptr)
726 }
727
730}
731
733{
734 for (qsizetype i = 0; i < m_knee_list.size() - 1; ++i)
736
737 if (!m_arrow_head.isEmpty()) {
738 p->save();
739 p->setBrush(p->pen().color());
741 p->restore();
742 } else if (ground()) {
744 }
745}
746
747bool Connection::contains(const QPoint &pos) const
748{
749 return region().contains(pos);
750}
751
753{
754 if (type == EndPoint::Source) {
755 if (m_source_pos != QPoint(-1, -1))
757 } else {
758 if (m_target_pos != QPoint(-1, -1))
760 }
761 return QRect();
762}
763
765{
766 const auto cnt = m_knee_list.size();
767 if (cnt < 2)
768 return RightDir;
769
770 LineDir dir;
771 if (type == EndPoint::Source)
773 else
775
776 if (dir == LeftDir)
777 dir = RightDir;
778 if (dir == UpDir)
779 dir = DownDir;
780
781 return dir;
782}
783
785{
786 const auto cnt = m_knee_list.size();
787 if (cnt < 2)
788 return QRect();
789 const QString text = label(type);
790 if (text.isEmpty())
791 return QRect();
792
793 const QSize size = labelPixmap(type).size();
794 QPoint p1, p2;
795 if (type == EndPoint::Source) {
796 p1 = m_knee_list.at(0);
797 p2 = m_knee_list.at(1);
798 } else {
799 p1 = m_knee_list.at(cnt - 1);
800 p2 = m_knee_list.at(cnt - 2);
801 }
802 const LineDir dir = classifyLine(p1, p2);
803
805 switch (dir) {
806 case UpDir:
807 result = QRect(p1 + QPoint(-size.width()/2, 0), size);
808 break;
809 case DownDir:
810 result = QRect(p1 + QPoint(-size.width()/2, -size.height()), size);
811 break;
812 case LeftDir:
813 result = QRect(p1 + QPoint(0, -size.height()/2), size);
814 break;
815 case RightDir:
816 result = QRect(p1 + QPoint(-size.width(), -size.height()/2), size);
817 break;
818 }
819
820 return result;
821}
822
824{
825 if (text == label(type))
826 return;
827
828 if (type == EndPoint::Source)
830 else
832
834}
835
837{
839
840 const QString text = label(type);
841 if (text.isEmpty()) {
842 *pm = QPixmap();
843 return;
844 }
845
848 *pm = QPixmap(size);
850 color.setAlpha(190);
851 pm->fill(color);
852
853 QPainter p(pm);
856 p.end();
857
858 const LineDir dir = labelDir(type);
859
860 if (dir == DownDir)
861 *pm = pm->transformed(QTransform(0.0, -1.0, 1.0, 0.0, 0.0, 0.0));
862}
863
865{
866 bool changed = false;
867
870 if (r != m_source_rect) {
871 if (m_source_pos != QPoint(-1, -1) && !r.contains(m_source_pos)) {
874 }
877 changed = true;
878 }
879 }
880
883 if (r != m_target_rect) {
884 if (m_target_pos != QPoint(-1, -1) && !r.contains(m_target_pos)) {
887 }
890 changed = true;
891 }
892 }
893
894 if (changed) {
895 update();
897 update();
898 }
899}
900
901/*******************************************************************************
902** ConnectionEdit
903*/
904
922
927
929{
932 m_bg_widget = nullptr;
933 m_widget_under_mouse = nullptr;
934 m_tmp_con = nullptr;
935}
936
938{
939 if (background == m_bg_widget) {
940 // nothing to do
941 return;
942 }
943
946}
947
954
956{
957 // Might happen while reloading a form.
958 if (m_bg_widget == nullptr)
959 return;
960
962 return;
963
966
967 updateLines();
968 update();
969}
970
972{
973 if (m_bg_widget == nullptr)
974 return nullptr;
976 if (widget == nullptr)
978
979 return widget;
980}
981
982
984{
985 if (w == nullptr)
986 return QRect();
987 QRect r = w->geometry();
988 QPoint pos = w->mapToGlobal(QPoint(0, 0));
991 return r;
992}
993
995{
996 if (m_tmp_con != nullptr)
997 return Connecting;
999 return Dragging;
1000 return Editing;
1001}
1002
1004{
1005 if (con->label(type).isEmpty())
1006 return;
1007
1008 const bool heavy = selected(con) || con == m_tmp_con;
1010 p->setBrush(Qt::NoBrush);
1011 const QRect r = con->labelRect(type);
1013 p->drawRect(fixRect(r));
1014}
1015
1019{
1022
1023 const bool heavy = selected(con) || con == m_tmp_con;
1026 con->paint(p);
1027
1028 if (source != nullptr && source != m_bg_widget)
1030
1031 if (target != nullptr && target != m_bg_widget)
1033}
1034
1036{
1037 QPainter p(this);
1038 p.setClipRegion(e->region());
1039
1041
1042 for (Connection *con : std::as_const(m_con_list)) {
1043 if (!con->isVisible())
1044 continue;
1045
1047 }
1048
1049 if (m_tmp_con != nullptr)
1051
1054
1056 p.setPen(c);
1058 p.setBrush(c);
1059
1063 }
1064
1066 p.setPen(c);
1068 p.setBrush(c);
1069
1072
1075 for (Connection *con : std::as_const(m_con_list)) {
1076 if (con->isVisible()) {
1079 }
1080 }
1081
1084
1085 for (Connection *con : std::as_const(m_con_list)) {
1086 if (!selected(con) || !con->isVisible())
1087 continue;
1088
1090
1091 if (con->widget(EndPoint::Target) != nullptr)
1093 }
1094}
1095
1097{
1098 m_tmp_con->update();
1099 delete m_tmp_con;
1100 m_tmp_con = nullptr;
1101#if QT_CONFIG(cursor)
1102 setCursor(QCursor());
1103#endif
1105 m_widget_under_mouse = nullptr;
1106}
1107
1109{
1110 // Right click only to cancel
1111 const Qt::MouseButton button = e->button();
1112 const State cstate = state();
1113 if (button != Qt::LeftButton && !(button == Qt::RightButton && cstate == Connecting)) {
1115 return;
1116 }
1117
1118 e->accept();
1119 // Prefer a non-background widget over the connection,
1120 // otherwise, widgets covered by the connection labels cannot be accessed
1121 Connection *con_under_mouse = nullptr;
1124
1127 switch (cstate) {
1128 case Connecting:
1129 if (button == Qt::RightButton)
1131 break;
1132 case Dragging:
1133 break;
1134 case Editing:
1136 if (!toggleSelection)
1138 } else if (con_under_mouse != nullptr) {
1139 if (toggleSelection) {
1141 } else {
1142 selectNone();
1144 }
1145 } else {
1146 if (!toggleSelection) {
1147 selectNone();
1150 }
1151 }
1152 break;
1153 }
1154}
1155
1157{
1158 if (e->button() != Qt::LeftButton) {
1160 return;
1161 }
1162
1163 e->accept();
1164 switch (state()) {
1165 case Connecting:
1167 break;
1168 case Dragging:
1169 break;
1170 case Editing:
1173 else if (m_sel_con_set.size() == 1)
1175 break;
1176 }
1177
1178}
1179
1181{
1182 if (e->button() != Qt::LeftButton) {
1184 return;
1185 }
1186 e->accept();
1187
1188 switch (state()) {
1189 case Connecting:
1192 else
1194#if QT_CONFIG(cursor)
1195 setCursor(QCursor());
1196#endif
1197 break;
1198 case Editing:
1199 break;
1200 case Dragging:
1201 endDrag(e->position().toPoint());
1202 break;
1203 }
1204}
1205
1206
1208{
1210
1211 QWidget *w = widgetAt(pos);
1212 // Prefer a non-background widget over the connection,
1213 // otherwise, widgets covered by the connection labels cannot be accessed
1214 if (w == m_bg_widget && con_under_mouse)
1215 w = nullptr;
1216 else
1217 con_under_mouse = nullptr;
1218
1219 if (w != m_widget_under_mouse) {
1225 }
1226
1227 const EndPoint hs = endPointAt(pos);
1228 if (hs != m_end_point_under_mouse) {
1229#if QT_CONFIG(cursor)
1232 else
1233 setCursor(QCursor());
1234#endif
1236 }
1237}
1238
1240{
1242 switch (state()) {
1243 case Connecting:
1245 break;
1246 case Editing:
1247 if ((e->buttons() & Qt::LeftButton)
1252#if QT_CONFIG(cursor)
1254#endif
1255 }
1256 break;
1257 case Dragging:
1259 break;
1260 }
1261
1262 e->accept();
1263}
1264
1266{
1267 switch (e->key()) {
1268 case Qt::Key_Delete:
1269 if (state() == Editing)
1271 break;
1272 case Qt::Key_Escape:
1273 if (state() == Connecting)
1275 break;
1276 }
1277
1278 e->accept();
1279}
1280
1282{
1283 Q_ASSERT(m_tmp_con == nullptr);
1284
1285 m_tmp_con = new Connection(this);
1287}
1288
1313
1315{
1316 Q_ASSERT(m_tmp_con != nullptr);
1317
1319}
1320
1324
1330
1331// Find all connections which in which a sequence of objects is involved
1332template <class ObjectIterator>
1333static ConnectionEdit::ConnectionSet findConnectionsOf(const ConnectionEdit::ConnectionList &cl, ObjectIterator oi1, const ObjectIterator &oi2)
1334{
1335 ConnectionEdit::ConnectionSet rc;
1336
1337 const auto ccend = cl.cend();
1338 for ( ; oi1 != oi2; ++oi1) {
1339 for (auto cit = cl.constBegin(); cit != ccend; ++cit) {
1340 Connection *con = *cit;
1341 if (con->object(ConnectionEdit::EndPoint::Source) == *oi1 || con->object(ConnectionEdit::EndPoint::Target) == *oi1)
1342 rc.insert(con, con);
1343 }
1344 }
1345 return rc;
1346}
1347
1349{
1350 // Remove all connections of that widget and its children.
1351 if (m_con_list.isEmpty())
1352 return;
1353
1356
1358
1359 if (!remove_set.isEmpty()) {
1362 }
1363
1365}
1366
1368{
1369 // Remove all connections of that object and its children (in case of action groups).
1370 if (m_con_list.isEmpty())
1371 return;
1372
1376 if (!remove_set.isEmpty()) {
1379 }
1380
1382}
1383
1385{
1386 if (!con || sel == m_sel_con_set.contains(con))
1387 return;
1388
1389 if (sel) {
1392 } else {
1394 }
1395
1396 con->update();
1397}
1398
1400{
1401 return m_sel_con_set.contains(const_cast<Connection*>(con));
1402}
1403
1405{
1407 con->update();
1408
1410}
1411
1413{
1414 if (m_sel_con_set.size() == m_con_list.size())
1415 return;
1417 setSelected(con, true);
1418}
1419
1421{
1422 for (Connection *con : m_con_list) {
1423 if (con->contains(pos))
1424 return con;
1425 }
1426 return nullptr;
1427}
1428
1430{
1431 for (Connection *con : m_con_list) {
1432 if (!selected(con))
1433 continue;
1436
1437 if (sr.contains(pos))
1438 return EndPoint(con, EndPoint::Source);
1439 if (tr.contains(pos))
1440 return EndPoint(con, EndPoint::Target);
1441 }
1442 return EndPoint();
1443}
1444
1446{
1452}
1453
1455{
1458}
1459
1460void ConnectionEdit::endDrag(const QPoint &pos)
1461{
1464
1470
1472}
1473
1475{
1478}
1479
1487
1492
1494{
1496 con->checkWidgets();
1497}
1498
1504
1506{
1507 QObject *object = nullptr;
1508 if (!obj_name.isEmpty()) {
1510 if (object == nullptr && m_bg_widget->objectName() == obj_name)
1512
1513 if (object == con->object(EndPoint::Source))
1514 return;
1515 }
1517}
1518
1520{
1521 QObject *object = nullptr;
1522 if (!obj_name.isEmpty()) {
1524 if (object == nullptr && m_bg_widget->objectName() == obj_name)
1526
1527 if (object == con->object(EndPoint::Target))
1528 return;
1529 }
1531}
1532
1534{
1535 if (!m_con_list.contains(con))
1536 return nullptr;
1538 return con;
1539}
1540
1542{
1543 delete m_tmp_con;
1544 m_tmp_con = nullptr;
1545}
1546
1563
1570
1571} // namespace qdesigner_internal
1572
1573QT_END_NAMESPACE
void redo() override
Applies a change to the document.
AdjustConnectionCommand(ConnectionEdit *edit, Connection *con, const QPoint &old_source_pos, const QPoint &old_target_pos, const QPoint &new_source_pos, const QPoint &new_target_pos)
void undo() override
Reverts a change to the document.
void undo() override
Reverts a change to the document.
SetEndPointCommand(ConnectionEdit *edit, Connection *con, EndPoint::Type type, QObject *object)
void redo() override
Applies a change to the document.
static const int VLABEL_MARGIN
static QT_BEGIN_NAMESPACE const int BG_ALPHA
static qdesigner_internal::CETypes::LineDir classifyLine(const QPoint &p1, const QPoint &p2)
static QRect fixRect(const QRect &r)
static QPoint pointInsideRect(const QRect &r, QPoint p)
static QRect expand(const QRect &r, int i)
static const int LINE_PROXIMITY_RADIUS
static const int LOOP_MARGIN
static const int GROUND_H
static const int HLABEL_MARGIN
static QRect endPointRectHelper(const QPoint &pos)
static const int GROUND_W
static void paintGround(QPainter *p, QRect r)
static void paintEndPoint(QPainter *p, const QPoint &pos)
Combined button and popup list for selecting options.
Auxiliary methods to store/retrieve settings.
static bool pointAboveLine(const QPoint &l1, const QPoint &l2, const QPoint &p)
static QPolygonF arrowHead(const QPoint &p1, const QPoint &p2)
static QRect lineRect(const QPoint &a, const QPoint &b)
static QPoint lineEntryPos(const QPoint &p1, const QPoint &p2, const QRect &rect)
static ConnectionEdit::ConnectionSet findConnectionsOf(const ConnectionEdit::ConnectionList &cl, ObjectIterator oi1, const ObjectIterator &oi2)
static CETypes::LineDir closestEdge(const QPoint &p, const QRect &r)