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
4
6
7#include <QtDesigner/abstractformwindow.h>
8
9#include <QtWidgets/qapplication.h>
10#include <QtWidgets/qmenu.h>
11
12#include <QtGui/qaction.h>
13#include <QtGui/qpainter.h>
14#include <QtGui/qevent.h>
15#include <QtGui/qfontmetrics.h>
16#include <QtGui/qpixmap.h>
17#include <QtGui/qtransform.h>
18
19#include <QtCore/qmap.h>
20
22
23static const int BG_ALPHA = 32;
24static const int LINE_PROXIMITY_RADIUS = 3;
25static const int LOOP_MARGIN = 20;
26static const int VLABEL_MARGIN = 1;
27static const int HLABEL_MARGIN = 3;
28static const int GROUND_W = 20;
29static const int GROUND_H = 25;
30
31/*******************************************************************************
32** Tools
33*/
34
35static QRect fixRect(const QRect &r)
36{
37 return QRect(r.x(), r.y(), r.width() - 1, r.height() - 1);
38}
39
40static QRect expand(const QRect &r, int i)
41{
42 return QRect(r.x() - i, r.y() - i, r.width() + 2*i, r.height() + 2*i);
43}
44
45static QRect endPointRectHelper(const QPoint &pos)
46{
47 const QRect r(pos + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS),
48 QSize(2*LINE_PROXIMITY_RADIUS, 2*LINE_PROXIMITY_RADIUS));
49 return r;
50}
51
52static void paintGround(QPainter *p, QRect r)
53{
54 const QPoint mid = r.center();
55 p->drawLine(mid.x(), r.top(), mid.x(), mid.y());
56 p->drawLine(r.left(), mid.y(), r.right(), mid.y());
57 int y = r.top() + 4*r.height()/6;
58 int x = GROUND_W/6;
59 p->drawLine(r.left() + x, y, r.right() - x, y);
60 y = r.top() + 5*r.height()/6;
61 x = 2*GROUND_W/6;
62 p->drawLine(r.left() + x, y, r.right() - x, y);
63 p->drawLine(mid.x(), r.bottom(), mid.x() + 1, r.bottom());
64}
65
66static void paintEndPoint(QPainter *p, const QPoint &pos)
67{
68 const QRect r(pos + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS),
69 QSize(2*LINE_PROXIMITY_RADIUS, 2*LINE_PROXIMITY_RADIUS));
70 p->fillRect(fixRect(r), p->pen().color());
71}
72
73static qdesigner_internal::CETypes::LineDir classifyLine(const QPoint &p1, const QPoint &p2)
74{
75 if (p1.x() == p2.x())
76 return p1.y() < p2.y() ? qdesigner_internal::CETypes::DownDir : qdesigner_internal::CETypes::UpDir;
77 Q_ASSERT(p1.y() == p2.y());
78 return p1.x() < p2.x() ? qdesigner_internal::CETypes::RightDir : qdesigner_internal::CETypes::LeftDir;
79}
80
81static QPoint pointInsideRect(const QRect &r, QPoint p)
82{
83 if (p.x() < r.left())
84 p.setX(r.left());
85 else if (p.x() > r.right())
86 p.setX(r.right());
87
88 if (p.y() < r.top())
89 p.setY(r.top());
90 else if (p.y() > r.bottom())
91 p.setY(r.bottom());
92
93 return p;
94}
95
96namespace qdesigner_internal {
97
98/*******************************************************************************
99** Commands
100*/
101
107
117
128
130{
131public:
132 AdjustConnectionCommand(ConnectionEdit *edit, Connection *con,
133 const QPoint &old_source_pos,
134 const QPoint &old_target_pos,
135 const QPoint &new_source_pos,
136 const QPoint &new_target_pos);
139private:
140 Connection *m_con;
141 const QPoint m_old_source_pos;
142 const QPoint m_old_target_pos;
143 const QPoint m_new_source_pos;
144 const QPoint m_new_target_pos;
145};
146
147AdjustConnectionCommand::AdjustConnectionCommand(ConnectionEdit *edit, Connection *con,
148 const QPoint &old_source_pos,
149 const QPoint &old_target_pos,
150 const QPoint &new_source_pos,
151 const QPoint &new_target_pos) :
153 m_con(con),
158{
159 setText(QApplication::translate("Command", "Adjust connection"));
160}
161
163{
164 m_con->setEndPoint(EndPoint::Source, m_con->widget(EndPoint::Source), m_old_source_pos);
165 m_con->setEndPoint(EndPoint::Target, m_con->widget(EndPoint::Target), m_old_target_pos);
166}
167
169{
170 m_con->setEndPoint(EndPoint::Source, m_con->widget(EndPoint::Source), m_new_source_pos);
171 m_con->setEndPoint(EndPoint::Target, m_con->widget(EndPoint::Target), m_new_target_pos);
172}
173
180
194
208
210{
211public:
212 SetEndPointCommand(ConnectionEdit *edit, Connection *con, EndPoint::Type type, QObject *object);
215private:
216 Connection *m_con;
217 const EndPoint::Type m_type;
218 QObject *m_old_widget, *m_new_widget;
219 const QPoint m_old_pos;
220 QPoint m_new_pos;
221};
222
223SetEndPointCommand::SetEndPointCommand(ConnectionEdit *edit, Connection *con,
224 EndPoint::Type type, QObject *object) :
226 m_con(con),
227 m_type(type),
231{
232 if (QWidget *widget = qobject_cast<QWidget*>(object)) {
233 m_new_pos = edit->widgetRect(widget).center();
234 }
235
236 if (m_type == EndPoint::Source)
237 setText(QApplication::translate("Command", "Change source"));
238 else
239 setText(QApplication::translate("Command", "Change target"));
240}
241
243{
244 m_con->setEndPoint(m_type, m_new_widget, m_new_pos);
245 emit edit()->connectionChanged(m_con);
246}
247
249{
250 m_con->setEndPoint(m_type, m_old_widget, m_old_pos);
251 emit edit()->connectionChanged(m_con);
252}
253
254/*******************************************************************************
255** Connection
256*/
257
259 m_source_pos(QPoint(-1, -1)),
260 m_target_pos(QPoint(-1, -1)),
261 m_source(nullptr),
262 m_target(nullptr),
263 m_edit(edit),
264 m_visible(true)
265{
266
267}
268
278
280{
281 m_visible = b;
282}
283
285{
288
289 if (source == nullptr || target == nullptr) {
290 setVisible(false);
291 return;
292 }
293
294 QWidget *w = source;
295 while (w && w->parentWidget()) {
296 if (!w->isVisibleTo(w->parentWidget())) {
297 setVisible(false);
298 return;
299 }
300 w = w->parentWidget();
301 }
302
303 w = target;
304 while (w && w->parentWidget()) {
305 if (!w->isVisibleTo(w->parentWidget())) {
306 setVisible(false);
307 return;
308 }
309 w = w->parentWidget();
310 }
311
312 setVisible(true);
313}
314
316{
317 return m_visible;
318}
319
320bool Connection::ground() const
321{
322 return m_target != nullptr && m_target == m_edit->m_bg_widget;
323}
324
329
330static QPoint lineEntryPos(const QPoint &p1, const QPoint &p2, const QRect &rect)
331{
332 QPoint result;
333
334 switch (classifyLine(p1, p2)) {
335 case CETypes::UpDir:
336 result = QPoint(p1.x(), rect.bottom());
337 break;
338 case CETypes::DownDir:
339 result = QPoint(p1.x(), rect.top());
340 break;
341 case CETypes::LeftDir:
342 result = QPoint(rect.right(), p1.y());
343 break;
344 case CETypes::RightDir:
345 result = QPoint(rect.left(), p1.y());
346 break;
347 }
348
349 return result;
350}
351
352static QPolygonF arrowHead(const QPoint &p1, const QPoint &p2)
353{
354 QPolygonF result;
355
356 switch (classifyLine(p1, p2)) {
357 case CETypes::UpDir:
358 result.append(p2 + QPoint(0, 1));
359 result.append(p2 + QPoint(LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS*2 + 1));
360 result.append(p2 + QPoint(-LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS*2 + 1));
361 break;
362 case CETypes::DownDir:
363 result.append(p2);
364 result.append(p2 + QPoint(LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS*2));
365 result.append(p2 + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS*2));
366 break;
367 case CETypes::LeftDir:
368 result.append(p2 + QPoint(1, 0));
369 result.append(p2 + QPoint(2*LINE_PROXIMITY_RADIUS + 1, -LINE_PROXIMITY_RADIUS));
370 result.append(p2 + QPoint(2*LINE_PROXIMITY_RADIUS + 1, LINE_PROXIMITY_RADIUS));
371 break;
372 case CETypes::RightDir:
373 result.append(p2);
374 result.append(p2 + QPoint(-2*LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS));
375 result.append(p2 + QPoint(-2*LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS));
376 break;
377 }
378
379 return result;
380}
381
382static CETypes::LineDir closestEdge(const QPoint &p, const QRect &r)
383{
384 CETypes::LineDir result = CETypes::UpDir;
385 int min = p.y() - r.top();
386
387 int d = p.x() - r.left();
388 if (d < min) {
389 min = d;
390 result = CETypes::LeftDir;
391 }
392
393 d = r.bottom() - p.y();
394 if (d < min) {
395 min = d;
396 result = CETypes::DownDir;
397 }
398
399 d = r.right() - p.x();
400 if (d < min) {
401 min = d;
402 result = CETypes::RightDir;
403 }
404
405 return result;
406}
407
408static bool pointAboveLine(const QPoint &l1, const QPoint &l2, const QPoint &p)
409{
410 if (l1.x() == l2.x())
411 return p.x() >= l1.x();
412 return p.y() <= l1.y() + (p.x() - l1.x())*(l2.y() - l1.y())/(l2.x() - l1.x());
413}
414
416{
419
422 const QRect sr = m_source_rect;
423 const QRect tr = m_target_rect;
424
427
428 if (m_source == nullptr || s == QPoint(-1, -1) || t == QPoint(-1, -1))
429 return;
430
431 const QRect r = sr | tr;
432
434 if (m_target == nullptr) {
435 m_knee_list.append(QPoint(t.x(), s.y()));
436 } else if (m_target == m_edit->m_bg_widget) {
437 m_knee_list.append(QPoint(s.x(), t.y()));
438 } else if (tr.contains(sr) || sr.contains(tr)) {
439/*
440 +------------------+
441 | +----------+ |
442 | | | |
443 | | o | |
444 | +---|------+ |
445 | | x |
446 +-----|-----|------+
447 +-----+
448
449 We find out which edge of the outer rectangle is closest to the target
450 point, and make a loop which exits and re-enters through that edge.
451*/
452 const LineDir dir = closestEdge(t, tr);
453 switch (dir) {
454 case UpDir:
457 break;
458 case DownDir:
461 break;
462 case LeftDir:
465 break;
466 case RightDir:
469 break;
470 }
471 } else {
472 if (r.height() < sr.height() + tr.height()) {
473 if ((s.y() >= tr.top() && s.y() <= tr.bottom()) || (t.y() >= sr.bottom() || t.y() <= sr.top())) {
474/*
475 +--------+
476 | | +--------+
477 | o--+---+--x |
478 | o | | |
479 +-----|--+ | |
480 +------+--x |
481 +--------+
482
483 When dragging one end point, move the other end point to the same y position,
484 if that does not cause it to exit it's rectangle.
485*/
486 if (m_edit->state() == ConnectionEdit::Dragging) {
488 const QPoint p(t.x(), s.y());
490 if (tr.contains(p))
491 t = m_target_pos = p;
492 } else {
493 const QPoint p(s.x(), t.y());
495 if (sr.contains(p))
496 s = m_source_pos = p;
497 }
498 } else {
499 m_knee_list.append(QPoint(s.x(), t.y()));
500 }
501 } else {
502/*
503 +--------+
504 | o----+-------+
505 | | +---|----+
506 +--------+ | | |
507 | x |
508 +--------+
509*/
510 m_knee_list.append(QPoint(t.x(), s.y()));
511 }
512 } else if (r.width() < sr.width() + tr.width()) {
513 if ((s.x() >= tr.left() && s.x() <= tr.right()) || t.x() >= sr.right() || t.x() <= sr.left()) {
514/*
515 +--------+
516 | |
517 | o o+--+
518 +----|---+ |
519 +-|------|-+
520 | x x |
521 | |
522 +----------+
523
524 When dragging one end point, move the other end point to the same x position,
525 if that does not cause it to exit it's rectangle.
526*/
527 if (m_edit->state() == ConnectionEdit::Dragging) {
529 const QPoint p(s.x(), t.y());
531 if (tr.contains(p))
532 t = m_target_pos = p;
533 } else {
534 const QPoint p(t.x(), s.y());
536 if (sr.contains(p))
537 s = m_source_pos = p;
538 }
539 } else {
540 m_knee_list.append(QPoint(t.x(), s.y()));
541 }
542 } else {
543/*
544 +--------+
545 | |
546 | o |
547 +--|-----+
548 | +--------+
549 +---+-x |
550 | |
551 +--------+
552
553*/
554 m_knee_list.append(QPoint(s.x(), t.y()));
555 }
556 } else {
557/*
558 +--------+
559 | |
560 | o o-+--------+
561 +--|-----+ |
562 | +-----|--+
563 | | x |
564 +--------+-x |
565 +--------+
566
567 The line enters the target rectangle through the closest edge.
568*/
569 if (sr.topLeft() == r.topLeft()) {
571 m_knee_list.append(QPoint(t.x(), s.y()));
572 else
573 m_knee_list.append(QPoint(s.x(), t.y()));
574 } else if (sr.topRight() == r.topRight()) {
576 m_knee_list.append(QPoint(t.x(), s.y()));
577 else
578 m_knee_list.append(QPoint(s.x(), t.y()));
579 } else if (sr.bottomRight() == r.bottomRight()) {
581 m_knee_list.append(QPoint(s.x(), t.y()));
582 else
583 m_knee_list.append(QPoint(t.x(), s.y()));
584 } else {
586 m_knee_list.append(QPoint(s.x(), t.y()));
587 else
588 m_knee_list.append(QPoint(t.x(), s.y()));
589 }
590 }
591 }
593
594 if (m_knee_list.size() == 2)
596
597 trimLine();
598
605}
606
607void Connection::trimLine()
608{
609 if (m_source == nullptr || m_source_pos == QPoint(-1, -1) || m_target_pos == QPoint(-1, -1))
610 return;
611 auto cnt = m_knee_list.size();
612 if (cnt < 2)
613 return;
614
615 const QRect sr = m_source_rect;
616 const QRect tr = m_target_rect;
617
618 if (sr.contains(m_knee_list.at(1)))
620
621 cnt = m_knee_list.size();
622 if (cnt < 2)
623 return;
624
625 if (!tr.contains(sr) && tr.contains(m_knee_list.at(cnt - 2)))
627
628 cnt = m_knee_list.size();
629 if (cnt < 2)
630 return;
631
634
635 if (tr.contains(m_knee_list.at(cnt - 1)) && !tr.contains(m_knee_list.at(cnt - 2))) {
636 m_knee_list[cnt - 1]
639 }
640}
641
643{
644 if (source == m_source && m_source_pos == pos)
645 return;
646
647 update(false);
648
654 }
655
656 update(false);
657}
658
660{
661 if (target == m_target && m_target_pos == pos)
662 return;
663
664 update(false);
665
671 }
672
673 update(false);
674}
675
676static QRect lineRect(const QPoint &a, const QPoint &b)
677{
678 const QPoint c(qMin(a.x(), b.x()), qMin(a.y(), b.y()));
679 const QPoint d(qMax(a.x(), b.x()), qMax(a.y(), b.y()));
680
681 QRect result(c, d);
682 return expand(result, LINE_PROXIMITY_RADIUS);
683}
684
686{
687 if (!ground())
688 return QRect();
689 if (m_knee_list.isEmpty())
690 return QRect();
691
692 const QPoint p = m_knee_list.last();
693 return QRect(p.x() - GROUND_W/2, p.y(), GROUND_W, GROUND_H);
694}
695
697{
699
700 for (qsizetype i = 0; i < m_knee_list.size() - 1; ++i)
702
703 if (!m_arrow_head.isEmpty()) {
705 r = expand(r, 1);
707 } else if (ground()) {
709 }
710
713
714 return result;
715}
716
718{
719 m_edit->update(region());
720 if (update_widgets) {
721 if (m_source != nullptr)
723 if (m_target != nullptr)
725 }
726
729}
730
732{
733 for (qsizetype i = 0; i < m_knee_list.size() - 1; ++i)
735
736 if (!m_arrow_head.isEmpty()) {
737 p->save();
738 p->setBrush(p->pen().color());
740 p->restore();
741 } else if (ground()) {
743 }
744}
745
746bool Connection::contains(const QPoint &pos) const
747{
748 return region().contains(pos);
749}
750
752{
753 if (type == EndPoint::Source) {
754 if (m_source_pos != QPoint(-1, -1))
756 } else {
757 if (m_target_pos != QPoint(-1, -1))
759 }
760 return QRect();
761}
762
764{
765 const auto cnt = m_knee_list.size();
766 if (cnt < 2)
767 return RightDir;
768
769 LineDir dir;
770 if (type == EndPoint::Source)
772 else
774
775 if (dir == LeftDir)
776 dir = RightDir;
777 if (dir == UpDir)
778 dir = DownDir;
779
780 return dir;
781}
782
784{
785 const auto cnt = m_knee_list.size();
786 if (cnt < 2)
787 return QRect();
788 const QString text = label(type);
789 if (text.isEmpty())
790 return QRect();
791
792 const QSize size = labelPixmap(type).size();
793 QPoint p1, p2;
794 if (type == EndPoint::Source) {
795 p1 = m_knee_list.at(0);
796 p2 = m_knee_list.at(1);
797 } else {
798 p1 = m_knee_list.at(cnt - 1);
799 p2 = m_knee_list.at(cnt - 2);
800 }
801 const LineDir dir = classifyLine(p1, p2);
802
804 switch (dir) {
805 case UpDir:
806 result = QRect(p1 + QPoint(-size.width()/2, 0), size);
807 break;
808 case DownDir:
809 result = QRect(p1 + QPoint(-size.width()/2, -size.height()), size);
810 break;
811 case LeftDir:
812 result = QRect(p1 + QPoint(0, -size.height()/2), size);
813 break;
814 case RightDir:
815 result = QRect(p1 + QPoint(-size.width(), -size.height()/2), size);
816 break;
817 }
818
819 return result;
820}
821
823{
824 if (text == label(type))
825 return;
826
827 if (type == EndPoint::Source)
829 else
831
833}
834
836{
838
839 const QString text = label(type);
840 if (text.isEmpty()) {
841 *pm = QPixmap();
842 return;
843 }
844
847 *pm = QPixmap(size);
849 color.setAlpha(190);
850 pm->fill(color);
851
852 QPainter p(pm);
855 p.end();
856
857 const LineDir dir = labelDir(type);
858
859 if (dir == DownDir)
860 *pm = pm->transformed(QTransform(0.0, -1.0, 1.0, 0.0, 0.0, 0.0));
861}
862
864{
865 bool changed = false;
866
869 if (r != m_source_rect) {
870 if (m_source_pos != QPoint(-1, -1) && !r.contains(m_source_pos)) {
873 }
876 changed = true;
877 }
878 }
879
882 if (r != m_target_rect) {
883 if (m_target_pos != QPoint(-1, -1) && !r.contains(m_target_pos)) {
886 }
889 changed = true;
890 }
891 }
892
893 if (changed) {
894 update();
896 update();
897 }
898}
899
900/*******************************************************************************
901** ConnectionEdit
902*/
903
921
926
928{
931 m_bg_widget = nullptr;
932 m_widget_under_mouse = nullptr;
933 m_tmp_con = nullptr;
934}
935
937{
938 if (background == m_bg_widget) {
939 // nothing to do
940 return;
941 }
942
945}
946
953
955{
956 // Might happen while reloading a form.
957 if (m_bg_widget == nullptr)
958 return;
959
961 return;
962
965
966 updateLines();
967 update();
968}
969
971{
972 if (m_bg_widget == nullptr)
973 return nullptr;
975 if (widget == nullptr)
977
978 return widget;
979}
980
981
983{
984 if (w == nullptr)
985 return QRect();
986 QRect r = w->geometry();
987 QPoint pos = w->mapToGlobal(QPoint(0, 0));
990 return r;
991}
992
994{
995 if (m_tmp_con != nullptr)
996 return Connecting;
998 return Dragging;
999 return Editing;
1000}
1001
1003{
1004 if (con->label(type).isEmpty())
1005 return;
1006
1007 const bool heavy = selected(con) || con == m_tmp_con;
1009 p->setBrush(Qt::NoBrush);
1010 const QRect r = con->labelRect(type);
1012 p->drawRect(fixRect(r));
1013}
1014
1018{
1021
1022 const bool heavy = selected(con) || con == m_tmp_con;
1025 con->paint(p);
1026
1027 if (source != nullptr && source != m_bg_widget)
1029
1030 if (target != nullptr && target != m_bg_widget)
1032}
1033
1035{
1036 QPainter p(this);
1037 p.setClipRegion(e->region());
1038
1040
1041 for (Connection *con : std::as_const(m_con_list)) {
1042 if (!con->isVisible())
1043 continue;
1044
1046 }
1047
1048 if (m_tmp_con != nullptr)
1050
1053
1055 p.setPen(c);
1057 p.setBrush(c);
1058
1062 }
1063
1065 p.setPen(c);
1067 p.setBrush(c);
1068
1071
1074 for (Connection *con : std::as_const(m_con_list)) {
1075 if (con->isVisible()) {
1078 }
1079 }
1080
1083
1084 for (Connection *con : std::as_const(m_con_list)) {
1085 if (!selected(con) || !con->isVisible())
1086 continue;
1087
1089
1090 if (con->widget(EndPoint::Target) != nullptr)
1092 }
1093}
1094
1096{
1097 m_tmp_con->update();
1098 delete m_tmp_con;
1099 m_tmp_con = nullptr;
1100#if QT_CONFIG(cursor)
1101 setCursor(QCursor());
1102#endif
1104 m_widget_under_mouse = nullptr;
1105}
1106
1108{
1109 // Right click only to cancel
1110 const Qt::MouseButton button = e->button();
1111 const State cstate = state();
1112 if (button != Qt::LeftButton && !(button == Qt::RightButton && cstate == Connecting)) {
1114 return;
1115 }
1116
1117 e->accept();
1118 // Prefer a non-background widget over the connection,
1119 // otherwise, widgets covered by the connection labels cannot be accessed
1120 Connection *con_under_mouse = nullptr;
1123
1126 switch (cstate) {
1127 case Connecting:
1128 if (button == Qt::RightButton)
1130 break;
1131 case Dragging:
1132 break;
1133 case Editing:
1135 if (!toggleSelection)
1137 } else if (con_under_mouse != nullptr) {
1138 if (toggleSelection) {
1140 } else {
1141 selectNone();
1143 }
1144 } else {
1145 if (!toggleSelection) {
1146 selectNone();
1149 }
1150 }
1151 break;
1152 }
1153}
1154
1156{
1157 if (e->button() != Qt::LeftButton) {
1159 return;
1160 }
1161
1162 e->accept();
1163 switch (state()) {
1164 case Connecting:
1166 break;
1167 case Dragging:
1168 break;
1169 case Editing:
1172 else if (m_sel_con_set.size() == 1)
1174 break;
1175 }
1176
1177}
1178
1180{
1181 if (e->button() != Qt::LeftButton) {
1183 return;
1184 }
1185 e->accept();
1186
1187 switch (state()) {
1188 case Connecting:
1191 else
1193#if QT_CONFIG(cursor)
1194 setCursor(QCursor());
1195#endif
1196 break;
1197 case Editing:
1198 break;
1199 case Dragging:
1200 endDrag(e->position().toPoint());
1201 break;
1202 }
1203}
1204
1205
1207{
1209
1210 QWidget *w = widgetAt(pos);
1211 // Prefer a non-background widget over the connection,
1212 // otherwise, widgets covered by the connection labels cannot be accessed
1213 if (w == m_bg_widget && con_under_mouse)
1214 w = nullptr;
1215 else
1216 con_under_mouse = nullptr;
1217
1218 if (w != m_widget_under_mouse) {
1224 }
1225
1226 const EndPoint hs = endPointAt(pos);
1227 if (hs != m_end_point_under_mouse) {
1228#if QT_CONFIG(cursor)
1231 else
1232 setCursor(QCursor());
1233#endif
1235 }
1236}
1237
1239{
1241 switch (state()) {
1242 case Connecting:
1244 break;
1245 case Editing:
1246 if ((e->buttons() & Qt::LeftButton)
1251#if QT_CONFIG(cursor)
1253#endif
1254 }
1255 break;
1256 case Dragging:
1258 break;
1259 }
1260
1261 e->accept();
1262}
1263
1265{
1266 switch (e->key()) {
1267 case Qt::Key_Delete:
1268 if (state() == Editing)
1270 break;
1271 case Qt::Key_Escape:
1272 if (state() == Connecting)
1274 break;
1275 }
1276
1277 e->accept();
1278}
1279
1281{
1282 Q_ASSERT(m_tmp_con == nullptr);
1283
1284 m_tmp_con = new Connection(this);
1286}
1287
1312
1314{
1315 Q_ASSERT(m_tmp_con != nullptr);
1316
1318}
1319
1323
1329
1330// Find all connections which in which a sequence of objects is involved
1331template <class ObjectIterator>
1332static ConnectionEdit::ConnectionSet findConnectionsOf(const ConnectionEdit::ConnectionList &cl, ObjectIterator oi1, const ObjectIterator &oi2)
1333{
1334 ConnectionEdit::ConnectionSet rc;
1335
1336 const auto ccend = cl.cend();
1337 for ( ; oi1 != oi2; ++oi1) {
1338 for (auto cit = cl.constBegin(); cit != ccend; ++cit) {
1339 Connection *con = *cit;
1340 if (con->object(ConnectionEdit::EndPoint::Source) == *oi1 || con->object(ConnectionEdit::EndPoint::Target) == *oi1)
1341 rc.insert(con, con);
1342 }
1343 }
1344 return rc;
1345}
1346
1348{
1349 // Remove all connections of that widget and its children.
1350 if (m_con_list.isEmpty())
1351 return;
1352
1355
1357
1358 if (!remove_set.isEmpty()) {
1361 }
1362
1364}
1365
1367{
1368 // Remove all connections of that object and its children (in case of action groups).
1369 if (m_con_list.isEmpty())
1370 return;
1371
1375 if (!remove_set.isEmpty()) {
1378 }
1379
1381}
1382
1384{
1385 if (!con || sel == m_sel_con_set.contains(con))
1386 return;
1387
1388 if (sel) {
1391 } else {
1393 }
1394
1395 con->update();
1396}
1397
1399{
1400 return m_sel_con_set.contains(const_cast<Connection*>(con));
1401}
1402
1404{
1406 con->update();
1407
1409}
1410
1412{
1413 if (m_sel_con_set.size() == m_con_list.size())
1414 return;
1416 setSelected(con, true);
1417}
1418
1420{
1421 for (Connection *con : m_con_list) {
1422 if (con->contains(pos))
1423 return con;
1424 }
1425 return nullptr;
1426}
1427
1429{
1430 for (Connection *con : m_con_list) {
1431 if (!selected(con))
1432 continue;
1435
1436 if (sr.contains(pos))
1437 return EndPoint(con, EndPoint::Source);
1438 if (tr.contains(pos))
1439 return EndPoint(con, EndPoint::Target);
1440 }
1441 return EndPoint();
1442}
1443
1445{
1451}
1452
1454{
1457}
1458
1459void ConnectionEdit::endDrag(const QPoint &pos)
1460{
1463
1469
1471}
1472
1474{
1477}
1478
1486
1491
1493{
1495 con->checkWidgets();
1496}
1497
1503
1505{
1506 QObject *object = nullptr;
1507 if (!obj_name.isEmpty()) {
1509 if (object == nullptr && m_bg_widget->objectName() == obj_name)
1511
1512 if (object == con->object(EndPoint::Source))
1513 return;
1514 }
1516}
1517
1519{
1520 QObject *object = nullptr;
1521 if (!obj_name.isEmpty()) {
1523 if (object == nullptr && m_bg_widget->objectName() == obj_name)
1525
1526 if (object == con->object(EndPoint::Target))
1527 return;
1528 }
1530}
1531
1533{
1534 if (!m_con_list.contains(con))
1535 return nullptr;
1537 return con;
1538}
1539
1541{
1542 delete m_tmp_con;
1543 m_tmp_con = nullptr;
1544}
1545
1562
1569
1570} // namespace qdesigner_internal
1571
1572QT_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)