Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
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
6
#
include
"connectionedit_p.h"
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
22
QT_BEGIN_NAMESPACE
23
24
static
const
int
BG_ALPHA
= 32;
25
static
const
int
LINE_PROXIMITY_RADIUS
= 3;
26
static
const
int
LOOP_MARGIN
= 20;
27
static
const
int
VLABEL_MARGIN
= 1;
28
static
const
int
HLABEL_MARGIN
= 3;
29
static
const
int
GROUND_W
= 20;
30
static
const
int
GROUND_H
= 25;
31
32
/*******************************************************************************
33
** Tools
34
*/
35
36
static
QRect
fixRect
(
const
QRect &r)
37
{
38
return
QRect(r.x(), r.y(), r.width() - 1, r.height() - 1);
39
}
40
41
static
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
46
static
QRect
endPointRectHelper
(
const
QPoint &pos)
47
{
48
const
QRect r(pos + QPoint(-
LINE_PROXIMITY_RADIUS
, -
LINE_PROXIMITY_RADIUS
),
49
QSize(2*
LINE_PROXIMITY_RADIUS
, 2*
LINE_PROXIMITY_RADIUS
));
50
return
r;
51
}
52
53
static
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
67
static
void
paintEndPoint
(QPainter *p,
const
QPoint &pos)
68
{
69
const
QRect r(pos + QPoint(-
LINE_PROXIMITY_RADIUS
, -
LINE_PROXIMITY_RADIUS
),
70
QSize(2*
LINE_PROXIMITY_RADIUS
, 2*
LINE_PROXIMITY_RADIUS
));
71
p->fillRect(fixRect(r), p->pen().color());
72
}
73
74
static
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
82
static
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
97
namespace
qdesigner_internal
{
98
99
/*******************************************************************************
100
** Commands
101
*/
102
103
AddConnectionCommand
::
AddConnectionCommand
(
ConnectionEdit
*
edit
,
Connection
*
con
)
104
:
CECommand
(
edit
),
m_con
(
con
)
105
{
106
setText
(
QApplication
::
translate
(
"Command"
,
"Add connection"
));
107
}
108
109
void
AddConnectionCommand
::
redo
()
110
{
111
edit
()->
selectNone
();
112
emit
edit
()->
aboutToAddConnection
(
edit
()->
m_con_list
.
size
());
113
edit
()->
m_con_list
.
append
(
m_con
);
114
m_con
->
inserted
();
115
emit
edit
()->
connectionAdded
(
m_con
);
116
edit
()->
setSelected
(
m_con
,
true
);
117
}
118
119
void
AddConnectionCommand
::
undo
()
120
{
121
const
int
idx
=
edit
()->
indexOfConnection
(
m_con
);
122
emit
edit
()->
aboutToRemoveConnection
(
m_con
);
123
edit
()->
setSelected
(
m_con
,
false
);
124
m_con
->
update
();
125
m_con
->
removed
();
126
edit
()->
m_con_list
.
removeAll
(
m_con
);
127
emit
edit
()->
connectionRemoved
(
idx
);
128
}
129
130
class
AdjustConnectionCommand
:
public
CECommand
131
{
132
public
:
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);
138
void
redo
()
override
;
139
void
undo
()
override
;
140
private
:
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
148
AdjustConnectionCommand
::
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) :
153
CECommand
(
edit
),
154
m_con(con),
155
m_old_source_pos
(
old_source_pos
),
156
m_old_target_pos
(
old_target_pos
),
157
m_new_source_pos
(
new_source_pos
),
158
m_new_target_pos
(
new_target_pos
)
159
{
160
setText(QApplication::translate(
"Command"
,
"Adjust connection"
));
161
}
162
163
void
AdjustConnectionCommand
::
undo
()
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
169
void
AdjustConnectionCommand
::
redo
()
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
175
DeleteConnectionsCommand
::
DeleteConnectionsCommand
(
ConnectionEdit
*
edit
,
176
const
ConnectionList
&
con_list
)
177
:
CECommand
(
edit
),
m_con_list
(
con_list
)
178
{
179
setText
(
QApplication
::
translate
(
"Command"
,
"Delete connections"
));
180
}
181
182
void
DeleteConnectionsCommand
::
redo
()
183
{
184
for
(
Connection
*
con
:
std
::
as_const
(
m_con_list
)) {
185
const
int
idx
=
edit
()->
indexOfConnection
(
con
);
186
emit
edit
()->
aboutToRemoveConnection
(
con
);
187
Q_ASSERT
(
edit
()->
m_con_list
.
contains
(
con
));
188
edit
()->
setSelected
(
con
,
false
);
189
con
->
update
();
190
con
->
removed
();
191
edit
()->
m_con_list
.
removeAll
(
con
);
192
emit
edit
()->
connectionRemoved
(
idx
);
193
}
194
}
195
196
void
DeleteConnectionsCommand
::
undo
()
197
{
198
for
(
Connection
*
con
:
std
::
as_const
(
m_con_list
)) {
199
Q_ASSERT
(!
edit
()->
m_con_list
.
contains
(
con
));
200
emit
edit
()->
aboutToAddConnection
(
edit
()->
m_con_list
.
size
());
201
edit
()->
m_con_list
.
append
(
con
);
202
edit
()->
selectNone
();
203
con
->
update
();
204
con
->
inserted
();
205
emit
edit
()->
connectionAdded
(
con
);
206
edit
()->
setSelected
(
con
,
true
);
207
}
208
}
209
210
class
SetEndPointCommand
:
public
CECommand
211
{
212
public
:
213
SetEndPointCommand
(ConnectionEdit *edit, Connection *con, EndPoint::Type type, QObject *object);
214
void
redo
()
override
;
215
void
undo
()
override
;
216
private
:
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
224
SetEndPointCommand
::
SetEndPointCommand
(ConnectionEdit *edit, Connection *con,
225
EndPoint::Type type, QObject *object) :
226
CECommand
(
edit
),
227
m_con(con),
228
m_type
(
type
),
229
m_old_widget
(
con
->
object
(
type
)),
230
m_new_widget
(
object
),
231
m_old_pos
(
con
->
endPointPos
(
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
243
void
SetEndPointCommand
::
redo
()
244
{
245
m_con->setEndPoint(m_type, m_new_widget, m_new_pos);
246
emit edit()->connectionChanged(m_con);
247
}
248
249
void
SetEndPointCommand
::
undo
()
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
259
Connection
::
Connection
(
ConnectionEdit
*
edit
) :
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
270
Connection
::
Connection
(
ConnectionEdit
*
edit
,
QObject
*
source
,
QObject
*
target
) :
271
m_source_pos
(
QPoint
(-1, -1)),
272
m_target_pos
(
QPoint
(-1, -1)),
273
m_source
(
source
),
274
m_target
(
target
),
275
m_edit
(
edit
),
276
m_visible
(
true
)
277
{
278
}
279
280
void
Connection
::
setVisible
(
bool
b
)
281
{
282
m_visible
=
b
;
283
}
284
285
void
Connection
::
updateVisibility
()
286
{
287
QWidget
*
source
=
widget
(
EndPoint
::
Source
);
288
QWidget
*
target
=
widget
(
EndPoint
::
Target
);
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
316
bool
Connection
::
isVisible
()
const
317
{
318
return
m_visible
;
319
}
320
321
bool
Connection
::
ground
()
const
322
{
323
return
m_target
!=
nullptr
&&
m_target
==
m_edit
->
m_bg_widget
;
324
}
325
326
QPoint
Connection
::
endPointPos
(
EndPoint
::
Type
type
)
const
327
{
328
return
type
==
EndPoint
::
Source
?
m_source_pos
:
m_target_pos
;
329
}
330
331
static
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
353
static
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
383
static
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
409
static
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
416
void
Connection
::
updateKneeList
()
417
{
418
const
LineDir
old_source_label_dir
=
labelDir
(
EndPoint
::
Source
);
419
const
LineDir
old_target_label_dir
=
labelDir
(
EndPoint
::
Target
);
420
421
QPoint
s
=
endPointPos
(
EndPoint
::
Source
);
422
QPoint
t
=
endPointPos
(
EndPoint
::
Target
);
423
const
QRect
sr
=
m_source_rect
;
424
const
QRect
tr
=
m_target_rect
;
425
426
m_knee_list
.
clear
();
427
m_arrow_head
.
clear
();
428
429
if
(
m_source
==
nullptr
||
s
==
QPoint
(-1, -1) ||
t
==
QPoint
(-1, -1))
430
return
;
431
432
const
QRect
r
=
sr
|
tr
;
433
434
m_knee_list
.
append
(
s
);
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
:
456
m_knee_list
.
append
(
QPoint
(
s
.
x
(),
r
.
top
() -
LOOP_MARGIN
));
457
m_knee_list
.
append
(
QPoint
(
t
.
x
(),
r
.
top
() -
LOOP_MARGIN
));
458
break
;
459
case
DownDir
:
460
m_knee_list
.
append
(
QPoint
(
s
.
x
(),
r
.
bottom
() +
LOOP_MARGIN
));
461
m_knee_list
.
append
(
QPoint
(
t
.
x
(),
r
.
bottom
() +
LOOP_MARGIN
));
462
break
;
463
case
LeftDir
:
464
m_knee_list
.
append
(
QPoint
(
r
.
left
() -
LOOP_MARGIN
,
s
.
y
()));
465
m_knee_list
.
append
(
QPoint
(
r
.
left
() -
LOOP_MARGIN
,
t
.
y
()));
466
break
;
467
case
RightDir
:
468
m_knee_list
.
append
(
QPoint
(
r
.
right
() +
LOOP_MARGIN
,
s
.
y
()));
469
m_knee_list
.
append
(
QPoint
(
r
.
right
() +
LOOP_MARGIN
,
t
.
y
()));
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
) {
488
if
(
m_edit
->
m_drag_end_point
.
type
==
EndPoint
::
Source
) {
489
const
QPoint
p
(
t
.
x
(),
s
.
y
());
490
m_knee_list
.
append
(
p
);
491
if
(
tr
.
contains
(
p
))
492
t
=
m_target_pos
=
p
;
493
}
else
{
494
const
QPoint
p
(
s
.
x
(),
t
.
y
());
495
m_knee_list
.
append
(
p
);
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
) {
529
if
(
m_edit
->
m_drag_end_point
.
type
==
EndPoint
::
Source
) {
530
const
QPoint
p
(
s
.
x
(),
t
.
y
());
531
m_knee_list
.
append
(
p
);
532
if
(
tr
.
contains
(
p
))
533
t
=
m_target_pos
=
p
;
534
}
else
{
535
const
QPoint
p
(
t
.
x
(),
s
.
y
());
536
m_knee_list
.
append
(
p
);
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
()) {
571
if
(
pointAboveLine
(
tr
.
topLeft
(),
tr
.
bottomRight
(),
t
))
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
()) {
576
if
(
pointAboveLine
(
tr
.
bottomLeft
(),
tr
.
topRight
(),
t
))
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
()) {
581
if
(
pointAboveLine
(
tr
.
topLeft
(),
tr
.
bottomRight
(),
t
))
582
m_knee_list
.
append
(
QPoint
(
s
.
x
(),
t
.
y
()));
583
else
584
m_knee_list
.
append
(
QPoint
(
t
.
x
(),
s
.
y
()));
585
}
else
{
586
if
(
pointAboveLine
(
tr
.
bottomLeft
(),
tr
.
topRight
(),
t
))
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
}
593
m_knee_list
.
append
(
t
);
594
595
if
(
m_knee_list
.
size
() == 2)
596
m_knee_list
.
clear
();
597
598
trimLine
();
599
600
const
LineDir
new_source_label_dir
=
labelDir
(
EndPoint
::
Source
);
601
const
LineDir
new_target_label_dir
=
labelDir
(
EndPoint
::
Target
);
602
if
(
new_source_label_dir
!=
old_source_label_dir
)
603
updatePixmap
(
EndPoint
::
Source
);
604
if
(
new_target_label_dir
!=
old_target_label_dir
)
605
updatePixmap
(
EndPoint
::
Target
);
606
}
607
608
void
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)))
620
m_knee_list
.
removeFirst
();
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)))
627
m_knee_list
.
removeLast
();
628
629
cnt
=
m_knee_list
.
size
();
630
if
(
cnt
< 2)
631
return
;
632
633
if
(
sr
.
contains
(
m_knee_list
.
at
(0)) && !
sr
.
contains
(
m_knee_list
.
at
(1)))
634
m_knee_list
[0] =
lineEntryPos
(
m_knee_list
.
at
(1),
m_knee_list
.
at
(0),
sr
);
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]
638
=
lineEntryPos
(
m_knee_list
.
at
(
cnt
- 2),
m_knee_list
.
at
(
cnt
- 1),
tr
);
639
m_arrow_head
=
arrowHead
(
m_knee_list
.
at
(
cnt
- 2),
m_knee_list
.
at
(
cnt
- 1));
640
}
641
}
642
643
void
Connection
::
setSource
(
QObject
*
source
,
const
QPoint
&
pos
)
644
{
645
if
(
source
==
m_source
&&
m_source_pos
==
pos
)
646
return
;
647
648
update
(
false
);
649
650
m_source
=
source
;
651
if
(
QWidget
*
widget
=
qobject_cast
<
QWidget
*>(
source
)) {
652
m_source_pos
=
pos
;
653
m_source_rect
=
m_edit
->
widgetRect
(
widget
);
654
updateKneeList
();
655
}
656
657
update
(
false
);
658
}
659
660
void
Connection
::
setTarget
(
QObject
*
target
,
const
QPoint
&
pos
)
661
{
662
if
(
target
==
m_target
&&
m_target_pos
==
pos
)
663
return
;
664
665
update
(
false
);
666
667
m_target
=
target
;
668
if
(
QWidget
*
widget
=
qobject_cast
<
QWidget
*>(
target
)) {
669
m_target_pos
=
pos
;
670
m_target_rect
=
m_edit
->
widgetRect
(
widget
);
671
updateKneeList
();
672
}
673
674
update
(
false
);
675
}
676
677
static
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
686
QRect
Connection
::
groundRect
()
const
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
697
QRegion
Connection
::
region
()
const
698
{
699
QRegion
result
;
700
701
for
(
qsizetype
i
= 0;
i
<
m_knee_list
.
size
() - 1; ++
i
)
702
result
=
result
.
united
(
lineRect
(
m_knee_list
.
at
(
i
),
m_knee_list
.
at
(
i
+ 1)));
703
704
if
(!
m_arrow_head
.
isEmpty
()) {
705
QRect
r
=
m_arrow_head
.
boundingRect
().
toRect
();
706
r
=
expand
(
r
, 1);
707
result
=
result
.
united
(
r
);
708
}
else
if
(
ground
()) {
709
result
=
result
.
united
(
groundRect
());
710
}
711
712
result
=
result
.
united
(
labelRect
(
EndPoint
::
Source
));
713
result
=
result
.
united
(
labelRect
(
EndPoint
::
Target
));
714
715
return
result
;
716
}
717
718
void
Connection
::
update
(
bool
update_widgets
)
const
719
{
720
m_edit
->
update
(
region
());
721
if
(
update_widgets
) {
722
if
(
m_source
!=
nullptr
)
723
m_edit
->
update
(
m_source_rect
);
724
if
(
m_target
!=
nullptr
)
725
m_edit
->
update
(
m_target_rect
);
726
}
727
728
m_edit
->
update
(
endPointRect
(
EndPoint
::
Source
));
729
m_edit
->
update
(
endPointRect
(
EndPoint
::
Target
));
730
}
731
732
void
Connection
::
paint
(
QPainter
*
p
)
const
733
{
734
for
(
qsizetype
i
= 0;
i
<
m_knee_list
.
size
() - 1; ++
i
)
735
p
->
drawLine
(
m_knee_list
.
at
(
i
),
m_knee_list
.
at
(
i
+ 1));
736
737
if
(!
m_arrow_head
.
isEmpty
()) {
738
p
->
save
();
739
p
->
setBrush
(
p
->
pen
().
color
());
740
p
->
drawPolygon
(
m_arrow_head
);
741
p
->
restore
();
742
}
else
if
(
ground
()) {
743
paintGround
(
p
,
groundRect
());
744
}
745
}
746
747
bool
Connection
::
contains
(
const
QPoint
&
pos
)
const
748
{
749
return
region
().
contains
(
pos
);
750
}
751
752
QRect
Connection
::
endPointRect
(
EndPoint
::
Type
type
)
const
753
{
754
if
(
type
==
EndPoint
::
Source
) {
755
if
(
m_source_pos
!=
QPoint
(-1, -1))
756
return
endPointRectHelper
(
m_source_pos
);
757
}
else
{
758
if
(
m_target_pos
!=
QPoint
(-1, -1))
759
return
endPointRectHelper
(
m_target_pos
);
760
}
761
return
QRect
();
762
}
763
764
CETypes
::
LineDir
Connection
::
labelDir
(
EndPoint
::
Type
type
)
const
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
)
772
dir
=
classifyLine
(
m_knee_list
.
at
(0),
m_knee_list
.
at
(1));
773
else
774
dir
=
classifyLine
(
m_knee_list
.
at
(
cnt
- 2),
m_knee_list
.
at
(
cnt
- 1));
775
776
if
(
dir
==
LeftDir
)
777
dir
=
RightDir
;
778
if
(
dir
==
UpDir
)
779
dir
=
DownDir
;
780
781
return
dir
;
782
}
783
784
QRect
Connection
::
labelRect
(
EndPoint
::
Type
type
)
const
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
804
QRect
result
;
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
823
void
Connection
::
setLabel
(
EndPoint
::
Type
type
,
const
QString
&
text
)
824
{
825
if
(
text
==
label
(
type
))
826
return
;
827
828
if
(
type
==
EndPoint
::
Source
)
829
m_source_label
=
text
;
830
else
831
m_target_label
=
text
;
832
833
updatePixmap
(
type
);
834
}
835
836
void
Connection
::
updatePixmap
(
EndPoint
::
Type
type
)
837
{
838
QPixmap
*
pm
=
type
==
EndPoint
::
Source
? &
m_source_label_pm
: &
m_target_label_pm
;
839
840
const
QString
text
=
label
(
type
);
841
if
(
text
.
isEmpty
()) {
842
*
pm
=
QPixmap
();
843
return
;
844
}
845
846
const
QFontMetrics
fm
=
m_edit
->
fontMetrics
();
847
const
QSize
size
=
fm
.
size
(
Qt
::
TextSingleLine
,
text
) +
QSize
(
HLABEL_MARGIN
*2,
VLABEL_MARGIN
*2);
848
*
pm
=
QPixmap
(
size
);
849
QColor
color
=
m_edit
->
palette
().
color
(
QPalette
::
Normal
,
QPalette
::
Base
);
850
color
.
setAlpha
(190);
851
pm
->
fill
(
color
);
852
853
QPainter
p
(
pm
);
854
p
.
setPen
(
m_edit
->
palette
().
color
(
QPalette
::
Normal
,
QPalette
::
Text
));
855
p
.
drawText
(-
fm
.
leftBearing
(
text
.
at
(0)) +
HLABEL_MARGIN
,
fm
.
ascent
() +
VLABEL_MARGIN
,
text
);
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
864
void
Connection
::
checkWidgets
()
865
{
866
bool
changed
=
false
;
867
868
if
(
QWidget
*
sourceWidget
=
qobject_cast
<
QWidget
*>(
m_source
)) {
869
const
QRect
r
=
m_edit
->
widgetRect
(
sourceWidget
);
870
if
(
r
!=
m_source_rect
) {
871
if
(
m_source_pos
!=
QPoint
(-1, -1) && !
r
.
contains
(
m_source_pos
)) {
872
QPoint
offset
=
m_source_pos
-
m_source_rect
.
topLeft
();
873
m_source_pos
=
pointInsideRect
(
r
,
r
.
topLeft
() +
offset
);
874
}
875
m_edit
->
update
(
m_source_rect
);
876
m_source_rect
=
r
;
877
changed
=
true
;
878
}
879
}
880
881
if
(
QWidget
*
targetWidget
=
qobject_cast
<
QWidget
*>(
m_target
)) {
882
const
QRect
r
=
m_edit
->
widgetRect
(
targetWidget
);
883
if
(
r
!=
m_target_rect
) {
884
if
(
m_target_pos
!=
QPoint
(-1, -1) && !
r
.
contains
(
m_target_pos
)) {
885
const
QPoint
offset
=
m_target_pos
-
m_target_rect
.
topLeft
();
886
m_target_pos
=
pointInsideRect
(
r
,
r
.
topLeft
() +
offset
);
887
}
888
m_edit
->
update
(
m_target_rect
);
889
m_target_rect
=
r
;
890
changed
=
true
;
891
}
892
}
893
894
if
(
changed
) {
895
update
();
896
updateKneeList
();
897
update
();
898
}
899
}
900
901
/*******************************************************************************
902
** ConnectionEdit
903
*/
904
905
ConnectionEdit
::
ConnectionEdit
(
QWidget
*
parent
,
QDesignerFormWindowInterface
*
form
) :
906
QWidget
(
parent
),
907
m_bg_widget
(
nullptr
),
908
m_undo_stack
(
form
->
commandHistory
()),
909
m_enable_update_background
(
false
),
910
m_tmp_con
(
nullptr
),
911
m_start_connection_on_drag
(
true
),
912
m_widget_under_mouse
(
nullptr
),
913
m_inactive_color
(
Qt
::
blue
),
914
m_active_color
(
Qt
::
red
)
915
{
916
setAttribute
(
Qt
::
WA_MouseTracking
,
true
);
917
setFocusPolicy
(
Qt
::
ClickFocus
);
918
919
connect
(
form
, &
QDesignerFormWindowInterface
::
widgetRemoved
,
this
, &
ConnectionEdit
::
widgetRemoved
);
920
connect
(
form
, &
QDesignerFormWindowInterface
::
objectRemoved
,
this
, &
ConnectionEdit
::
objectRemoved
);
921
}
922
923
ConnectionEdit
::~
ConnectionEdit
()
924
{
925
qDeleteAll
(
m_con_list
);
926
}
927
928
void
ConnectionEdit
::
clear
()
929
{
930
m_con_list
.
clear
();
931
m_sel_con_set
.
clear
();
932
m_bg_widget
=
nullptr
;
933
m_widget_under_mouse
=
nullptr
;
934
m_tmp_con
=
nullptr
;
935
}
936
937
void
ConnectionEdit
::
setBackground
(
QWidget
*
background
)
938
{
939
if
(
background
==
m_bg_widget
) {
940
// nothing to do
941
return
;
942
}
943
944
m_bg_widget
=
background
;
945
updateBackground
();
946
}
947
948
void
ConnectionEdit
::
enableUpdateBackground
(
bool
enable
)
949
{
950
m_enable_update_background
=
enable
;
951
if
(
enable
)
952
updateBackground
();
953
}
954
955
void
ConnectionEdit
::
updateBackground
()
956
{
957
// Might happen while reloading a form.
958
if
(
m_bg_widget
==
nullptr
)
959
return
;
960
961
if
(!
m_enable_update_background
)
962
return
;
963
964
for
(
Connection
*
c
:
std
::
as_const
(
m_con_list
))
965
c
->
updateVisibility
();
966
967
updateLines
();
968
update
();
969
}
970
971
QWidget
*
ConnectionEdit
::
widgetAt
(
const
QPoint
&
pos
)
const
972
{
973
if
(
m_bg_widget
==
nullptr
)
974
return
nullptr
;
975
QWidget
*
widget
=
m_bg_widget
->
childAt
(
pos
);
976
if
(
widget
==
nullptr
)
977
widget
=
m_bg_widget
;
978
979
return
widget
;
980
}
981
982
983
QRect
ConnectionEdit
::
widgetRect
(
QWidget
*
w
)
const
984
{
985
if
(
w
==
nullptr
)
986
return
QRect
();
987
QRect
r
=
w
->
geometry
();
988
QPoint
pos
=
w
->
mapToGlobal
(
QPoint
(0, 0));
989
pos
=
mapFromGlobal
(
pos
);
990
r
.
moveTopLeft
(
pos
);
991
return
r
;
992
}
993
994
ConnectionEdit
::
State
ConnectionEdit
::
state
()
const
995
{
996
if
(
m_tmp_con
!=
nullptr
)
997
return
Connecting
;
998
if
(!
m_drag_end_point
.
isNull
())
999
return
Dragging
;
1000
return
Editing
;
1001
}
1002
1003
void
ConnectionEdit
::
paintLabel
(
QPainter
*
p
,
EndPoint
::
Type
type
,
Connection
*
con
)
1004
{
1005
if
(
con
->
label
(
type
).
isEmpty
())
1006
return
;
1007
1008
const
bool
heavy
=
selected
(
con
) ||
con
==
m_tmp_con
;
1009
p
->
setPen
(
heavy
?
m_active_color
:
m_inactive_color
);
1010
p
->
setBrush
(
Qt
::
NoBrush
);
1011
const
QRect
r
=
con
->
labelRect
(
type
);
1012
p
->
drawPixmap
(
r
.
topLeft
(),
con
->
labelPixmap
(
type
));
1013
p
->
drawRect
(
fixRect
(
r
));
1014
}
1015
1016
void
ConnectionEdit
::
paintConnection
(
QPainter
*
p
,
Connection
*
con
,
1017
WidgetSet
*
heavy_highlight_set
,
1018
WidgetSet
*
light_highlight_set
)
const
1019
{
1020
QWidget
*
source
=
con
->
widget
(
EndPoint
::
Source
);
1021
QWidget
*
target
=
con
->
widget
(
EndPoint
::
Target
);
1022
1023
const
bool
heavy
=
selected
(
con
) ||
con
==
m_tmp_con
;
1024
WidgetSet
*
set
=
heavy
?
heavy_highlight_set
:
light_highlight_set
;
1025
p
->
setPen
(
heavy
?
m_active_color
:
m_inactive_color
);
1026
con
->
paint
(
p
);
1027
1028
if
(
source
!=
nullptr
&&
source
!=
m_bg_widget
)
1029
set
->
insert
(
source
,
source
);
1030
1031
if
(
target
!=
nullptr
&&
target
!=
m_bg_widget
)
1032
set
->
insert
(
target
,
target
);
1033
}
1034
1035
void
ConnectionEdit
::
paintEvent
(
QPaintEvent
*
e
)
1036
{
1037
QPainter
p
(
this
);
1038
p
.
setClipRegion
(
e
->
region
());
1039
1040
WidgetSet
heavy_highlight_set
,
light_highlight_set
;
1041
1042
for
(
Connection
*
con
:
std
::
as_const
(
m_con_list
)) {
1043
if
(!
con
->
isVisible
())
1044
continue
;
1045
1046
paintConnection
(&
p
,
con
, &
heavy_highlight_set
, &
light_highlight_set
);
1047
}
1048
1049
if
(
m_tmp_con
!=
nullptr
)
1050
paintConnection
(&
p
,
m_tmp_con
, &
heavy_highlight_set
, &
light_highlight_set
);
1051
1052
if
(!
m_widget_under_mouse
.
isNull
() &&
m_widget_under_mouse
!=
m_bg_widget
)
1053
heavy_highlight_set
.
insert
(
m_widget_under_mouse
,
m_widget_under_mouse
);
1054
1055
QColor
c
=
m_active_color
;
1056
p
.
setPen
(
c
);
1057
c
.
setAlpha
(
BG_ALPHA
);
1058
p
.
setBrush
(
c
);
1059
1060
for
(
QWidget
*
w
:
std
::
as_const
(
heavy_highlight_set
)) {
1061
p
.
drawRect
(
fixRect
(
widgetRect
(
w
)));
1062
light_highlight_set
.
remove
(
w
);
1063
}
1064
1065
c
=
m_inactive_color
;
1066
p
.
setPen
(
c
);
1067
c
.
setAlpha
(
BG_ALPHA
);
1068
p
.
setBrush
(
c
);
1069
1070
for
(
QWidget
*
w
:
std
::
as_const
(
light_highlight_set
))
1071
p
.
drawRect
(
fixRect
(
widgetRect
(
w
)));
1072
1073
p
.
setBrush
(
palette
().
color
(
QPalette
::
Base
));
1074
p
.
setPen
(
palette
().
color
(
QPalette
::
Text
));
1075
for
(
Connection
*
con
:
std
::
as_const
(
m_con_list
)) {
1076
if
(
con
->
isVisible
()) {
1077
paintLabel
(&
p
,
EndPoint
::
Source
,
con
);
1078
paintLabel
(&
p
,
EndPoint
::
Target
,
con
);
1079
}
1080
}
1081
1082
p
.
setPen
(
m_active_color
);
1083
p
.
setBrush
(
m_active_color
);
1084
1085
for
(
Connection
*
con
:
std
::
as_const
(
m_con_list
)) {
1086
if
(!
selected
(
con
) || !
con
->
isVisible
())
1087
continue
;
1088
1089
paintEndPoint
(&
p
,
con
->
endPointPos
(
EndPoint
::
Source
));
1090
1091
if
(
con
->
widget
(
EndPoint
::
Target
) !=
nullptr
)
1092
paintEndPoint
(&
p
,
con
->
endPointPos
(
EndPoint
::
Target
));
1093
}
1094
}
1095
1096
void
ConnectionEdit
::
abortConnection
()
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
1104
if
(
m_widget_under_mouse
==
m_bg_widget
)
1105
m_widget_under_mouse
=
nullptr
;
1106
}
1107
1108
void
ConnectionEdit
::
mousePressEvent
(
QMouseEvent
*
e
)
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
)) {
1114
QWidget
::
mousePressEvent
(
e
);
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
;
1122
if
(!
m_widget_under_mouse
||
m_widget_under_mouse
==
m_bg_widget
)
1123
con_under_mouse
=
connectionAt
(
e
->
position
().
toPoint
());
1124
1125
m_start_connection_on_drag
=
false
;
1126
const
bool
toggleSelection
=
e
->
modifiers
().
testFlag
(
Qt
::
ControlModifier
);
1127
switch
(
cstate
) {
1128
case
Connecting
:
1129
if
(
button
==
Qt
::
RightButton
)
1130
abortConnection
();
1131
break
;
1132
case
Dragging
:
1133
break
;
1134
case
Editing
:
1135
if
(!
m_end_point_under_mouse
.
isNull
()) {
1136
if
(!
toggleSelection
)
1137
startDrag
(
m_end_point_under_mouse
,
e
->
position
().
toPoint
());
1138
}
else
if
(
con_under_mouse
!=
nullptr
) {
1139
if
(
toggleSelection
) {
1140
setSelected
(
con_under_mouse
, !
selected
(
con_under_mouse
));
1141
}
else
{
1142
selectNone
();
1143
setSelected
(
con_under_mouse
,
true
);
1144
}
1145
}
else
{
1146
if
(!
toggleSelection
) {
1147
selectNone
();
1148
if
(!
m_widget_under_mouse
.
isNull
())
1149
m_start_connection_on_drag
=
true
;
1150
}
1151
}
1152
break
;
1153
}
1154
}
1155
1156
void
ConnectionEdit
::
mouseDoubleClickEvent
(
QMouseEvent
*
e
)
1157
{
1158
if
(
e
->
button
() !=
Qt
::
LeftButton
) {
1159
QWidget
::
mouseDoubleClickEvent
(
e
);
1160
return
;
1161
}
1162
1163
e
->
accept
();
1164
switch
(
state
()) {
1165
case
Connecting
:
1166
abortConnection
();
1167
break
;
1168
case
Dragging
:
1169
break
;
1170
case
Editing
:
1171
if
(!
m_widget_under_mouse
.
isNull
())
1172
emit
widgetActivated
(
m_widget_under_mouse
);
1173
else
if
(
m_sel_con_set
.
size
() == 1)
1174
modifyConnection
(
m_sel_con_set
.
constBegin
().
key
());
1175
break
;
1176
}
1177
1178
}
1179
1180
void
ConnectionEdit
::
mouseReleaseEvent
(
QMouseEvent
*
e
)
1181
{
1182
if
(
e
->
button
() !=
Qt
::
LeftButton
) {
1183
QWidget
::
mouseReleaseEvent
(
e
);
1184
return
;
1185
}
1186
e
->
accept
();
1187
1188
switch
(
state
()) {
1189
case
Connecting
:
1190
if
(
m_widget_under_mouse
.
isNull
())
1191
abortConnection
();
1192
else
1193
endConnection
(
m_widget_under_mouse
,
e
->
position
().
toPoint
());
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
1207
void
ConnectionEdit
::
findObjectsUnderMouse
(
const
QPoint
&
pos
)
1208
{
1209
Connection
*
con_under_mouse
=
connectionAt
(
pos
);
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
) {
1220
if
(!
m_widget_under_mouse
.
isNull
())
1221
update
(
widgetRect
(
m_widget_under_mouse
));
1222
m_widget_under_mouse
=
w
;
1223
if
(!
m_widget_under_mouse
.
isNull
())
1224
update
(
widgetRect
(
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
)
1230
if
(
m_end_point_under_mouse
.
isNull
())
1231
setCursor
(
Qt
::
PointingHandCursor
);
1232
else
1233
setCursor
(
QCursor
());
1234
#
endif
1235
m_end_point_under_mouse
=
hs
;
1236
}
1237
}
1238
1239
void
ConnectionEdit
::
mouseMoveEvent
(
QMouseEvent
*
e
)
1240
{
1241
findObjectsUnderMouse
(
e
->
position
().
toPoint
());
1242
switch
(
state
()) {
1243
case
Connecting
:
1244
continueConnection
(
m_widget_under_mouse
,
e
->
position
().
toPoint
());
1245
break
;
1246
case
Editing
:
1247
if
((
e
->
buttons
() &
Qt
::
LeftButton
)
1248
&&
m_start_connection_on_drag
1249
&& !
m_widget_under_mouse
.
isNull
()) {
1250
m_start_connection_on_drag
=
false
;
1251
startConnection
(
m_widget_under_mouse
,
e
->
position
().
toPoint
());
1252
#
if
QT_CONFIG
(
cursor
)
1253
setCursor
(
Qt
::
CrossCursor
);
1254
#
endif
1255
}
1256
break
;
1257
case
Dragging
:
1258
continueDrag
(
e
->
position
().
toPoint
());
1259
break
;
1260
}
1261
1262
e
->
accept
();
1263
}
1264
1265
void
ConnectionEdit
::
keyPressEvent
(
QKeyEvent
*
e
)
1266
{
1267
switch
(
e
->
key
()) {
1268
case
Qt
::
Key_Delete
:
1269
if
(
state
() ==
Editing
)
1270
deleteSelected
();
1271
break
;
1272
case
Qt
::
Key_Escape
:
1273
if
(
state
() ==
Connecting
)
1274
abortConnection
();
1275
break
;
1276
}
1277
1278
e
->
accept
();
1279
}
1280
1281
void
ConnectionEdit
::
startConnection
(
QWidget
*
source
,
const
QPoint
&
pos
)
1282
{
1283
Q_ASSERT
(
m_tmp_con
==
nullptr
);
1284
1285
m_tmp_con
=
new
Connection
(
this
);
1286
m_tmp_con
->
setEndPoint
(
EndPoint
::
Source
,
source
,
pos
);
1287
}
1288
1289
void
ConnectionEdit
::
endConnection
(
QWidget
*
target
,
const
QPoint
&
pos
)
1290
{
1291
Q_ASSERT
(
m_tmp_con
!=
nullptr
);
1292
1293
m_tmp_con
->
setEndPoint
(
EndPoint
::
Target
,
target
,
pos
);
1294
1295
QWidget
*
source
=
m_tmp_con
->
widget
(
EndPoint
::
Source
);
1296
Q_ASSERT
(
source
!=
nullptr
);
1297
Q_ASSERT
(
target
!=
nullptr
);
1298
setEnabled
(
false
);
1299
Connection
*
new_con
=
createConnection
(
source
,
target
);
1300
setEnabled
(
true
);
1301
if
(
new_con
!=
nullptr
) {
1302
new_con
->
setEndPoint
(
EndPoint
::
Source
,
source
,
m_tmp_con
->
endPointPos
(
EndPoint
::
Source
));
1303
new_con
->
setEndPoint
(
EndPoint
::
Target
,
target
,
m_tmp_con
->
endPointPos
(
EndPoint
::
Target
));
1304
m_undo_stack
->
push
(
new
AddConnectionCommand
(
this
,
new_con
));
1305
emit
connectionChanged
(
new_con
);
1306
}
1307
1308
delete
m_tmp_con
;
1309
m_tmp_con
=
nullptr
;
1310
1311
findObjectsUnderMouse
(
mapFromGlobal
(
QCursor
::
pos
()));
1312
}
1313
1314
void
ConnectionEdit
::
continueConnection
(
QWidget
*
target
,
const
QPoint
&
pos
)
1315
{
1316
Q_ASSERT
(
m_tmp_con
!=
nullptr
);
1317
1318
m_tmp_con
->
setEndPoint
(
EndPoint
::
Target
,
target
,
pos
);
1319
}
1320
1321
void
ConnectionEdit
::
modifyConnection
(
Connection
*)
1322
{
1323
}
1324
1325
Connection
*
ConnectionEdit
::
createConnection
(
QWidget
*
source
,
QWidget
*
target
)
1326
{
1327
Connection
*
con
=
new
Connection
(
this
,
source
,
target
);
1328
return
con
;
1329
}
1330
1331
// Find all connections which in which a sequence of objects is involved
1332
template
<
class
ObjectIterator>
1333
static
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
1348
void
ConnectionEdit
::
widgetRemoved
(
QWidget
*
widget
)
1349
{
1350
// Remove all connections of that widget and its children.
1351
if
(
m_con_list
.
isEmpty
())
1352
return
;
1353
1354
QWidgetList
child_list
=
widget
->
findChildren
<
QWidget
*>();
1355
child_list
.
prepend
(
widget
);
1356
1357
const
ConnectionSet
remove_set
=
findConnectionsOf
(
m_con_list
,
child_list
.
constBegin
(),
child_list
.
constEnd
());
1358
1359
if
(!
remove_set
.
isEmpty
()) {
1360
auto
cmd
=
new
DeleteConnectionsCommand
(
this
,
ConnectionList
(
remove_set
.
cbegin
(),
remove_set
.
cend
()));
1361
m_undo_stack
->
push
(
cmd
);
1362
}
1363
1364
updateBackground
();
1365
}
1366
1367
void
ConnectionEdit
::
objectRemoved
(
QObject
*
o
)
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
1373
QObjectList
child_list
=
o
->
children
();
1374
child_list
.
prepend
(
o
);
1375
const
ConnectionSet
remove_set
=
findConnectionsOf
(
m_con_list
,
child_list
.
constBegin
(),
child_list
.
constEnd
());
1376
if
(!
remove_set
.
isEmpty
()) {
1377
auto
cmd
=
new
DeleteConnectionsCommand
(
this
,
ConnectionList
(
remove_set
.
cbegin
(),
remove_set
.
cend
()));
1378
m_undo_stack
->
push
(
cmd
);
1379
}
1380
1381
updateBackground
();
1382
}
1383
1384
void
ConnectionEdit
::
setSelected
(
Connection
*
con
,
bool
sel
)
1385
{
1386
if
(!
con
||
sel
==
m_sel_con_set
.
contains
(
con
))
1387
return
;
1388
1389
if
(
sel
) {
1390
m_sel_con_set
.
insert
(
con
,
con
);
1391
emit
connectionSelected
(
con
);
1392
}
else
{
1393
m_sel_con_set
.
remove
(
con
);
1394
}
1395
1396
con
->
update
();
1397
}
1398
1399
bool
ConnectionEdit
::
selected
(
const
Connection
*
con
)
const
1400
{
1401
return
m_sel_con_set
.
contains
(
const_cast
<
Connection
*>(
con
));
1402
}
1403
1404
void
ConnectionEdit
::
selectNone
()
1405
{
1406
for
(
Connection
*
con
:
std
::
as_const
(
m_sel_con_set
))
1407
con
->
update
();
1408
1409
m_sel_con_set
.
clear
();
1410
}
1411
1412
void
ConnectionEdit
::
selectAll
()
1413
{
1414
if
(
m_sel_con_set
.
size
() ==
m_con_list
.
size
())
1415
return
;
1416
for
(
Connection
*
con
:
std
::
as_const
(
m_con_list
))
1417
setSelected
(
con
,
true
);
1418
}
1419
1420
Connection
*
ConnectionEdit
::
connectionAt
(
const
QPoint
&
pos
)
const
1421
{
1422
for
(
Connection
*
con
:
m_con_list
) {
1423
if
(
con
->
contains
(
pos
))
1424
return
con
;
1425
}
1426
return
nullptr
;
1427
}
1428
1429
CETypes
::
EndPoint
ConnectionEdit
::
endPointAt
(
const
QPoint
&
pos
)
const
1430
{
1431
for
(
Connection
*
con
:
m_con_list
) {
1432
if
(!
selected
(
con
))
1433
continue
;
1434
const
QRect
sr
=
con
->
endPointRect
(
EndPoint
::
Source
);
1435
const
QRect
tr
=
con
->
endPointRect
(
EndPoint
::
Target
);
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
1445
void
ConnectionEdit
::
startDrag
(
const
EndPoint
&
end_point
,
const
QPoint
&
pos
)
1446
{
1447
Q_ASSERT
(
m_drag_end_point
.
isNull
());
1448
m_drag_end_point
=
end_point
;
1449
m_old_source_pos
=
m_drag_end_point
.
con
->
endPointPos
(
EndPoint
::
Source
);
1450
m_old_target_pos
=
m_drag_end_point
.
con
->
endPointPos
(
EndPoint
::
Target
);
1451
adjustHotSopt
(
m_drag_end_point
,
pos
);
1452
}
1453
1454
void
ConnectionEdit
::
continueDrag
(
const
QPoint
&
pos
)
1455
{
1456
Q_ASSERT
(!
m_drag_end_point
.
isNull
());
1457
adjustHotSopt
(
m_drag_end_point
,
pos
);
1458
}
1459
1460
void
ConnectionEdit
::
endDrag
(
const
QPoint
&
pos
)
1461
{
1462
Q_ASSERT
(!
m_drag_end_point
.
isNull
());
1463
adjustHotSopt
(
m_drag_end_point
,
pos
);
1464
1465
Connection
*
con
=
m_drag_end_point
.
con
;
1466
const
QPoint
new_source_pos
=
con
->
endPointPos
(
EndPoint
::
Source
);
1467
const
QPoint
new_target_pos
=
con
->
endPointPos
(
EndPoint
::
Target
);
1468
m_undo_stack
->
push
(
new
AdjustConnectionCommand
(
this
,
con
,
m_old_source_pos
,
m_old_target_pos
,
1469
new_source_pos
,
new_target_pos
));
1470
1471
m_drag_end_point
=
EndPoint
();
1472
}
1473
1474
void
ConnectionEdit
::
adjustHotSopt
(
const
EndPoint
&
end_point
,
const
QPoint
&
pos
)
1475
{
1476
QWidget
*
w
=
end_point
.
con
->
widget
(
end_point
.
type
);
1477
end_point
.
con
->
setEndPoint
(
end_point
.
type
,
w
,
pointInsideRect
(
widgetRect
(
w
),
pos
));
1478
}
1479
1480
void
ConnectionEdit
::
deleteSelected
()
1481
{
1482
if
(
m_sel_con_set
.
isEmpty
())
1483
return
;
1484
auto
cmd
=
new
DeleteConnectionsCommand
(
this
,
ConnectionList
(
m_sel_con_set
.
cbegin
(),
m_sel_con_set
.
cend
()));
1485
m_undo_stack
->
push
(
cmd
);
1486
}
1487
1488
void
ConnectionEdit
::
addConnection
(
Connection
*
con
)
1489
{
1490
m_con_list
.
append
(
con
);
1491
}
1492
1493
void
ConnectionEdit
::
updateLines
()
1494
{
1495
for
(
Connection
*
con
:
std
::
as_const
(
m_con_list
))
1496
con
->
checkWidgets
();
1497
}
1498
1499
void
ConnectionEdit
::
resizeEvent
(
QResizeEvent
*
e
)
1500
{
1501
updateBackground
();
1502
QWidget
::
resizeEvent
(
e
);
1503
}
1504
1505
void
ConnectionEdit
::
setSource
(
Connection
*
con
,
const
QString
&
obj_name
)
1506
{
1507
QObject
*
object
=
nullptr
;
1508
if
(!
obj_name
.
isEmpty
()) {
1509
object
=
m_bg_widget
->
findChild
<
QObject
*>(
obj_name
);
1510
if
(
object
==
nullptr
&&
m_bg_widget
->
objectName
() ==
obj_name
)
1511
object
=
m_bg_widget
;
1512
1513
if
(
object
==
con
->
object
(
EndPoint
::
Source
))
1514
return
;
1515
}
1516
m_undo_stack
->
push
(
new
SetEndPointCommand
(
this
,
con
,
EndPoint
::
Source
,
object
));
1517
}
1518
1519
void
ConnectionEdit
::
setTarget
(
Connection
*
con
,
const
QString
&
obj_name
)
1520
{
1521
QObject
*
object
=
nullptr
;
1522
if
(!
obj_name
.
isEmpty
()) {
1523
object
=
m_bg_widget
->
findChild
<
QObject
*>(
obj_name
);
1524
if
(
object
==
nullptr
&&
m_bg_widget
->
objectName
() ==
obj_name
)
1525
object
=
m_bg_widget
;
1526
1527
if
(
object
==
con
->
object
(
EndPoint
::
Target
))
1528
return
;
1529
}
1530
m_undo_stack
->
push
(
new
SetEndPointCommand
(
this
,
con
,
EndPoint
::
Target
,
object
));
1531
}
1532
1533
Connection
*
ConnectionEdit
::
takeConnection
(
Connection
*
con
)
1534
{
1535
if
(!
m_con_list
.
contains
(
con
))
1536
return
nullptr
;
1537
m_con_list
.
removeAll
(
con
);
1538
return
con
;
1539
}
1540
1541
void
ConnectionEdit
::
clearNewlyAddedConnection
()
1542
{
1543
delete
m_tmp_con
;
1544
m_tmp_con
=
nullptr
;
1545
}
1546
1547
void
ConnectionEdit
::
createContextMenu
(
QMenu
&
menu
)
1548
{
1549
// Select
1550
QAction
*
selectAllAction
=
menu
.
addAction
(
tr
(
"Select All"
));
1551
selectAllAction
->
setEnabled
(!
connectionList
().
isEmpty
());
1552
connect
(
selectAllAction
, &
QAction
::
triggered
,
this
, &
ConnectionEdit
::
selectAll
);
1553
QAction
*
deselectAllAction
=
menu
.
addAction
(
tr
(
"Deselect All"
));
1554
deselectAllAction
->
setEnabled
(!
selection
().
isEmpty
());
1555
connect
(
deselectAllAction
, &
QAction
::
triggered
,
this
, &
ConnectionEdit
::
selectNone
);
1556
menu
.
addSeparator
();
1557
// Delete
1558
QAction
*
deleteAction
=
menu
.
addAction
(
tr
(
"Delete"
));
1559
deleteAction
->
setShortcut
(
QKeySequence
::
Delete
);
1560
deleteAction
->
setEnabled
(!
selection
().
isEmpty
());
1561
connect
(
deleteAction
, &
QAction
::
triggered
,
this
, &
ConnectionEdit
::
deleteSelected
);
1562
}
1563
1564
void
ConnectionEdit
::
contextMenuEvent
(
QContextMenuEvent
*
event
)
1565
{
1566
QMenu
menu
;
1567
createContextMenu
(
menu
);
1568
menu
.
exec
(
event
->
globalPos
());
1569
}
1570
1571
}
// namespace qdesigner_internal
1572
1573
QT_END_NAMESPACE
qdesigner_internal::AdjustConnectionCommand
Definition
connectionedit.cpp:131
qdesigner_internal::AdjustConnectionCommand::redo
void redo() override
Applies a change to the document.
Definition
connectionedit.cpp:169
qdesigner_internal::AdjustConnectionCommand::AdjustConnectionCommand
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)
Definition
connectionedit.cpp:148
qdesigner_internal::AdjustConnectionCommand::undo
void undo() override
Reverts a change to the document.
Definition
connectionedit.cpp:163
qdesigner_internal::SetEndPointCommand
Definition
connectionedit.cpp:211
qdesigner_internal::SetEndPointCommand::undo
void undo() override
Reverts a change to the document.
Definition
connectionedit.cpp:249
qdesigner_internal::SetEndPointCommand::SetEndPointCommand
SetEndPointCommand(ConnectionEdit *edit, Connection *con, EndPoint::Type type, QObject *object)
Definition
connectionedit.cpp:224
qdesigner_internal::SetEndPointCommand::redo
void redo() override
Applies a change to the document.
Definition
connectionedit.cpp:243
VLABEL_MARGIN
static const int VLABEL_MARGIN
Definition
connectionedit.cpp:27
BG_ALPHA
static QT_BEGIN_NAMESPACE const int BG_ALPHA
Definition
connectionedit.cpp:24
classifyLine
static qdesigner_internal::CETypes::LineDir classifyLine(const QPoint &p1, const QPoint &p2)
Definition
connectionedit.cpp:74
fixRect
static QRect fixRect(const QRect &r)
Definition
connectionedit.cpp:36
pointInsideRect
static QPoint pointInsideRect(const QRect &r, QPoint p)
Definition
connectionedit.cpp:82
expand
static QRect expand(const QRect &r, int i)
Definition
connectionedit.cpp:41
LINE_PROXIMITY_RADIUS
static const int LINE_PROXIMITY_RADIUS
Definition
connectionedit.cpp:25
LOOP_MARGIN
static const int LOOP_MARGIN
Definition
connectionedit.cpp:26
GROUND_H
static const int GROUND_H
Definition
connectionedit.cpp:30
HLABEL_MARGIN
static const int HLABEL_MARGIN
Definition
connectionedit.cpp:28
endPointRectHelper
static QRect endPointRectHelper(const QPoint &pos)
Definition
connectionedit.cpp:46
GROUND_W
static const int GROUND_W
Definition
connectionedit.cpp:29
paintGround
static void paintGround(QPainter *p, QRect r)
Definition
connectionedit.cpp:53
paintEndPoint
static void paintEndPoint(QPainter *p, const QPoint &pos)
Definition
connectionedit.cpp:67
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
qdesigner_internal
Auxiliary methods to store/retrieve settings.
Definition
buddyeditor.cpp:67
qdesigner_internal::pointAboveLine
static bool pointAboveLine(const QPoint &l1, const QPoint &l2, const QPoint &p)
Definition
connectionedit.cpp:409
qdesigner_internal::arrowHead
static QPolygonF arrowHead(const QPoint &p1, const QPoint &p2)
Definition
connectionedit.cpp:353
qdesigner_internal::lineRect
static QRect lineRect(const QPoint &a, const QPoint &b)
Definition
connectionedit.cpp:677
qdesigner_internal::lineEntryPos
static QPoint lineEntryPos(const QPoint &p1, const QPoint &p2, const QRect &rect)
Definition
connectionedit.cpp:331
qdesigner_internal::findConnectionsOf
static ConnectionEdit::ConnectionSet findConnectionsOf(const ConnectionEdit::ConnectionList &cl, ObjectIterator oi1, const ObjectIterator &oi2)
Definition
connectionedit.cpp:1333
qdesigner_internal::closestEdge
static CETypes::LineDir closestEdge(const QPoint &p, const QRect &r)
Definition
connectionedit.cpp:383
qttools
src
designer
src
lib
shared
connectionedit.cpp
Generated on
for Qt by
1.18.0