source: trunk/tools/designer/src/lib/shared/connectionedit.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 46.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the Qt Designer of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42
43#include "connectionedit_p.h"
44
45#include <QtDesigner/abstractformwindow.h>
46
47#include <QtGui/QPainter>
48#include <QtGui/QPaintEvent>
49#include <QtGui/QFontMetrics>
50#include <QtGui/QPixmap>
51#include <QtGui/QMatrix>
52#include <QtGui/QApplication>
53#include <QtGui/QContextMenuEvent>
54#include <QtGui/QMenu>
55#include <QtGui/QAction>
56
57#include <QtCore/QMultiMap>
58
59QT_BEGIN_NAMESPACE
60
61static const int BG_ALPHA = 32;
62static const int LINE_PROXIMITY_RADIUS = 3;
63static const int LOOP_MARGIN = 20;
64static const int VLABEL_MARGIN = 1;
65static const int HLABEL_MARGIN = 3;
66static const int GROUND_W = 20;
67static const int GROUND_H = 25;
68
69/*******************************************************************************
70** Tools
71*/
72
73static QRect fixRect(const QRect &r)
74{
75 return QRect(r.x(), r.y(), r.width() - 1, r.height() - 1);
76}
77
78static QRect expand(const QRect &r, int i)
79{
80 return QRect(r.x() - i, r.y() - i, r.width() + 2*i, r.height() + 2*i);
81}
82
83static QRect endPointRectHelper(const QPoint &pos)
84{
85 const QRect r(pos + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS),
86 QSize(2*LINE_PROXIMITY_RADIUS, 2*LINE_PROXIMITY_RADIUS));
87 return r;
88}
89
90static void paintGround(QPainter *p, QRect r)
91{
92 const QPoint mid = r.center();
93 p->drawLine(mid.x(), r.top(), mid.x(), mid.y());
94 p->drawLine(r.left(), mid.y(), r.right(), mid.y());
95 int y = r.top() + 4*r.height()/6;
96 int x = GROUND_W/6;
97 p->drawLine(r.left() + x, y, r.right() - x, y);
98 y = r.top() + 5*r.height()/6;
99 x = 2*GROUND_W/6;
100 p->drawLine(r.left() + x, y, r.right() - x, y);
101 p->drawLine(mid.x(), r.bottom(), mid.x() + 1, r.bottom());
102}
103
104static void paintEndPoint(QPainter *p, const QPoint &pos)
105{
106 const QRect r(pos + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS),
107 QSize(2*LINE_PROXIMITY_RADIUS, 2*LINE_PROXIMITY_RADIUS));
108 p->fillRect(fixRect(r), p->pen().color());
109}
110
111static qdesigner_internal::CETypes::LineDir classifyLine(const QPoint &p1, const QPoint &p2)
112{
113 if (p1.x() == p2.x())
114 return p1.y() < p2.y() ? qdesigner_internal::CETypes::DownDir : qdesigner_internal::CETypes::UpDir;
115 Q_ASSERT(p1.y() == p2.y());
116 return p1.x() < p2.x() ? qdesigner_internal::CETypes::RightDir : qdesigner_internal::CETypes::LeftDir;
117}
118
119static QPoint pointInsideRect(const QRect &r, QPoint p)
120{
121 if (p.x() < r.left())
122 p.setX(r.left());
123 else if (p.x() > r.right())
124 p.setX(r.right());
125
126 if (p.y() < r.top())
127 p.setY(r.top());
128 else if (p.y() > r.bottom())
129 p.setY(r.bottom());
130
131 return p;
132}
133
134namespace qdesigner_internal {
135
136/*******************************************************************************
137** Commands
138*/
139
140AddConnectionCommand::AddConnectionCommand(ConnectionEdit *edit, Connection *con)
141 : CECommand(edit), m_con(con)
142{
143 setText(QApplication::translate("Command", "Add connection"));
144}
145
146void AddConnectionCommand::redo()
147{
148 edit()->selectNone();
149 emit edit()->aboutToAddConnection(edit()->m_con_list.size());
150 edit()->m_con_list.append(m_con);
151 m_con->inserted();
152 edit()->setSelected(m_con, true);
153 emit edit()->connectionAdded(m_con);
154}
155
156void AddConnectionCommand::undo()
157{
158 const int idx = edit()->indexOfConnection(m_con);
159 emit edit()->aboutToRemoveConnection(m_con);
160 edit()->setSelected(m_con, false);
161 m_con->update();
162 m_con->removed();
163 edit()->m_con_list.removeAll(m_con);
164 emit edit()->connectionRemoved(idx);
165}
166
167class AdjustConnectionCommand : public CECommand
168{
169public:
170 AdjustConnectionCommand(ConnectionEdit *edit, Connection *con,
171 const QPoint &old_source_pos,
172 const QPoint &old_target_pos,
173 const QPoint &new_source_pos,
174 const QPoint &new_target_pos);
175 virtual void redo();
176 virtual void undo();
177private:
178 Connection *m_con;
179 const QPoint m_old_source_pos;
180 const QPoint m_old_target_pos;
181 const QPoint m_new_source_pos;
182 const QPoint m_new_target_pos;
183};
184
185AdjustConnectionCommand::AdjustConnectionCommand(ConnectionEdit *edit, Connection *con,
186 const QPoint &old_source_pos,
187 const QPoint &old_target_pos,
188 const QPoint &new_source_pos,
189 const QPoint &new_target_pos) :
190 CECommand(edit),
191 m_con(con),
192 m_old_source_pos(old_source_pos),
193 m_old_target_pos(old_target_pos),
194 m_new_source_pos(new_source_pos),
195 m_new_target_pos(new_target_pos)
196{
197 setText(QApplication::translate("Command", "Adjust connection"));
198}
199
200void AdjustConnectionCommand::undo()
201{
202 m_con->setEndPoint(EndPoint::Source, m_con->widget(EndPoint::Source), m_old_source_pos);
203 m_con->setEndPoint(EndPoint::Target, m_con->widget(EndPoint::Target), m_old_target_pos);
204}
205
206void AdjustConnectionCommand::redo()
207{
208 m_con->setEndPoint(EndPoint::Source, m_con->widget(EndPoint::Source), m_new_source_pos);
209 m_con->setEndPoint(EndPoint::Target, m_con->widget(EndPoint::Target), m_new_target_pos);
210}
211
212DeleteConnectionsCommand::DeleteConnectionsCommand(ConnectionEdit *edit,
213 const ConnectionList &con_list)
214 : CECommand(edit), m_con_list(con_list)
215{
216 setText(QApplication::translate("Command", "Delete connections"));
217}
218
219void DeleteConnectionsCommand::redo()
220{
221 foreach (Connection *con, m_con_list) {
222 const int idx = edit()->indexOfConnection(con);
223 emit edit()->aboutToRemoveConnection(con);
224 Q_ASSERT(edit()->m_con_list.contains(con));
225 edit()->setSelected(con, false);
226 con->update();
227 con->removed();
228 edit()->m_con_list.removeAll(con);
229 emit edit()->connectionRemoved(idx);
230 }
231}
232
233void DeleteConnectionsCommand::undo()
234{
235 foreach (Connection *con, m_con_list) {
236 Q_ASSERT(!edit()->m_con_list.contains(con));
237 emit edit()->aboutToAddConnection(edit()->m_con_list.size());
238 edit()->m_con_list.append(con);
239 edit()->setSelected(con, true);
240 con->update();
241 con->inserted();
242 emit edit()->connectionAdded(con);
243 }
244}
245
246class SetEndPointCommand : public CECommand
247{
248public:
249 SetEndPointCommand(ConnectionEdit *edit, Connection *con, EndPoint::Type type, QObject *object);
250 virtual void redo();
251 virtual void undo();
252private:
253 Connection *m_con;
254 const EndPoint::Type m_type;
255 QObject *m_old_widget, *m_new_widget;
256 const QPoint m_old_pos;
257 QPoint m_new_pos;
258};
259
260SetEndPointCommand::SetEndPointCommand(ConnectionEdit *edit, Connection *con,
261 EndPoint::Type type, QObject *object) :
262 CECommand(edit),
263 m_con(con),
264 m_type(type),
265 m_old_widget(con->object(type)),
266 m_new_widget(object),
267 m_old_pos(con->endPointPos(type))
268{
269 if (QWidget *widget = qobject_cast<QWidget*>(object)) {
270 m_new_pos = edit->widgetRect(widget).center();
271 }
272
273 if (m_type == EndPoint::Source)
274 setText(QApplication::translate("Command", "Change source"));
275 else
276 setText(QApplication::translate("Command", "Change target"));
277}
278
279void SetEndPointCommand::redo()
280{
281 m_con->setEndPoint(m_type, m_new_widget, m_new_pos);
282 emit edit()->connectionChanged(m_con);
283}
284
285void SetEndPointCommand::undo()
286{
287 m_con->setEndPoint(m_type, m_old_widget, m_old_pos);
288 emit edit()->connectionChanged(m_con);
289}
290
291/*******************************************************************************
292** Connection
293*/
294
295Connection::Connection(ConnectionEdit *edit) :
296 m_source_pos(QPoint(-1, -1)),
297 m_target_pos(QPoint(-1, -1)),
298 m_source(0),
299 m_target(0),
300 m_edit(edit),
301 m_visible(true)
302{
303
304}
305
306Connection::Connection(ConnectionEdit *edit, QObject *source, QObject *target) :
307 m_source_pos(QPoint(-1, -1)),
308 m_target_pos(QPoint(-1, -1)),
309 m_source(source),
310 m_target(target),
311 m_edit(edit),
312 m_visible(true)
313{
314}
315
316void Connection::setVisible(bool b)
317{
318 m_visible = b;
319}
320
321void Connection::updateVisibility()
322{
323 QWidget *source = widget(EndPoint::Source);
324 QWidget *target = widget(EndPoint::Target);
325
326 if (source == 0 || target == 0) {
327 setVisible(false);
328 return;
329 }
330
331 QWidget *w = source;
332 while (w && w->parentWidget()) {
333 if (!w->isVisibleTo(w->parentWidget())) {
334 setVisible(false);
335 return;
336 }
337 w = w->parentWidget();
338 }
339
340 w = target;
341 while (w && w->parentWidget()) {
342 if (!w->isVisibleTo(w->parentWidget())) {
343 setVisible(false);
344 return;
345 }
346 w = w->parentWidget();
347 }
348
349 setVisible(true);
350}
351
352bool Connection::isVisible() const
353{
354 return m_visible;
355}
356
357bool Connection::ground() const
358{
359 return m_target != 0 && m_target == m_edit->m_bg_widget;
360}
361
362QPoint Connection::endPointPos(EndPoint::Type type) const
363{
364 if (type == EndPoint::Source)
365 return m_source_pos;
366 else
367 return m_target_pos;
368}
369
370static QPoint lineEntryPos(const QPoint &p1, const QPoint &p2, const QRect &rect)
371{
372 QPoint result;
373
374 switch (classifyLine(p1, p2)) {
375 case CETypes::UpDir:
376 result = QPoint(p1.x(), rect.bottom());
377 break;
378 case CETypes::DownDir:
379 result = QPoint(p1.x(), rect.top());
380 break;
381 case CETypes::LeftDir:
382 result = QPoint(rect.right(), p1.y());
383 break;
384 case CETypes::RightDir:
385 result = QPoint(rect.left(), p1.y());
386 break;
387 }
388
389 return result;
390}
391
392static QPolygonF arrowHead(const QPoint &p1, const QPoint &p2)
393{
394 QPolygonF result;
395
396 switch (classifyLine(p1, p2)) {
397 case CETypes::UpDir:
398 result.append(p2 + QPoint(0, 1));
399 result.append(p2 + QPoint(LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS*2 + 1));
400 result.append(p2 + QPoint(-LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS*2 + 1));
401 break;
402 case CETypes::DownDir:
403 result.append(p2);
404 result.append(p2 + QPoint(LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS*2));
405 result.append(p2 + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS*2));
406 break;
407 case CETypes::LeftDir:
408 result.append(p2 + QPoint(1, 0));
409 result.append(p2 + QPoint(2*LINE_PROXIMITY_RADIUS + 1, -LINE_PROXIMITY_RADIUS));
410 result.append(p2 + QPoint(2*LINE_PROXIMITY_RADIUS + 1, LINE_PROXIMITY_RADIUS));
411 break;
412 case CETypes::RightDir:
413 result.append(p2);
414 result.append(p2 + QPoint(-2*LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS));
415 result.append(p2 + QPoint(-2*LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS));
416 break;
417 }
418
419 return result;
420}
421
422static CETypes::LineDir closestEdge(const QPoint &p, const QRect &r)
423{
424 CETypes::LineDir result = CETypes::UpDir;
425 int min = p.y() - r.top();
426
427 int d = p.x() - r.left();
428 if (d < min) {
429 min = d;
430 result = CETypes::LeftDir;
431 }
432
433 d = r.bottom() - p.y();
434 if (d < min) {
435 min = d;
436 result = CETypes::DownDir;
437 }
438
439 d = r.right() - p.x();
440 if (d < min) {
441 min = d;
442 result = CETypes::RightDir;
443 }
444
445 return result;
446}
447
448static bool pointAboveLine(const QPoint &l1, const QPoint &l2, const QPoint &p)
449{
450 if (l1.x() == l2.x())
451 return p.x() >= l1.x();
452 return p.y() <= l1.y() + (p.x() - l1.x())*(l2.y() - l1.y())/(l2.x() - l1.x());
453}
454
455void Connection::updateKneeList()
456{
457 const LineDir old_source_label_dir = labelDir(EndPoint::Source);
458 const LineDir old_target_label_dir = labelDir(EndPoint::Target);
459
460 QPoint s = endPointPos(EndPoint::Source);
461 QPoint t = endPointPos(EndPoint::Target);
462 const QRect sr = m_source_rect;
463 const QRect tr = m_target_rect;
464
465 m_knee_list.clear();
466 m_arrow_head.clear();
467
468 if (m_source == 0 || s == QPoint(-1, -1) || t == QPoint(-1, -1))
469 return;
470
471 const QRect r = sr | tr;
472
473 m_knee_list.append(s);
474 if (m_target == 0) {
475 m_knee_list.append(QPoint(t.x(), s.y()));
476 } else if (m_target == m_edit->m_bg_widget) {
477 m_knee_list.append(QPoint(s.x(), t.y()));
478 } else if (tr.contains(sr) || sr.contains(tr)) {
479/*
480 +------------------+
481 | +----------+ |
482 | | | |
483 | | o | |
484 | +---|------+ |
485 | | x |
486 +-----|-----|------+
487 +-----+
488
489 We find out which edge of the outer rectangle is closest to the target
490 point, and make a loop which exits and re-enters through that edge.
491*/
492 const LineDir dir = closestEdge(t, tr);
493 switch (dir) {
494 case UpDir:
495 m_knee_list.append(QPoint(s.x(), r.top() - LOOP_MARGIN));
496 m_knee_list.append(QPoint(t.x(), r.top() - LOOP_MARGIN));
497 break;
498 case DownDir:
499 m_knee_list.append(QPoint(s.x(), r.bottom() + LOOP_MARGIN));
500 m_knee_list.append(QPoint(t.x(), r.bottom() + LOOP_MARGIN));
501 break;
502 case LeftDir:
503 m_knee_list.append(QPoint(r.left() - LOOP_MARGIN, s.y()));
504 m_knee_list.append(QPoint(r.left() - LOOP_MARGIN, t.y()));
505 break;
506 case RightDir:
507 m_knee_list.append(QPoint(r.right() + LOOP_MARGIN, s.y()));
508 m_knee_list.append(QPoint(r.right() + LOOP_MARGIN, t.y()));
509 break;
510 }
511 } else {
512 if (r.height() < sr.height() + tr.height()) {
513 if ((s.y() >= tr.top() && s.y() <= tr.bottom()) || (t.y() >= sr.bottom() || t.y() <= sr.top())) {
514/*
515 +--------+
516 | | +--------+
517 | o--+---+--x |
518 | o | | |
519 +-----|--+ | |
520 +------+--x |
521 +--------+
522
523 When dragging one end point, move the other end point to the same y position,
524 if that does not cause it to exit it's rectangle.
525*/
526 if (m_edit->state() == ConnectionEdit::Dragging) {
527 if (m_edit->m_drag_end_point.type == EndPoint::Source) {
528 const QPoint p(t.x(), s.y());
529 m_knee_list.append(p);
530 if (tr.contains(p))
531 t = m_target_pos = p;
532 } else {
533 const QPoint p(s.x(), t.y());
534 m_knee_list.append(p);
535 if (sr.contains(p))
536 s = m_source_pos = p;
537 }
538 } else {
539 m_knee_list.append(QPoint(s.x(), t.y()));
540 }
541 } else {
542/*
543 +--------+
544 | o----+-------+
545 | | +---|----+
546 +--------+ | | |
547 | x |
548 +--------+
549*/
550 m_knee_list.append(QPoint(t.x(), s.y()));
551 }
552 } else if (r.width() < sr.width() + tr.width()) {
553 if ((s.x() >= tr.left() && s.x() <= tr.right()) || t.x() >= sr.right() || t.x() <= sr.left()) {
554/*
555 +--------+
556 | |
557 | o o+--+
558 +----|---+ |
559 +-|------|-+
560 | x x |
561 | |
562 +----------+
563
564 When dragging one end point, move the other end point to the same x position,
565 if that does not cause it to exit it's rectangle.
566*/
567 if (m_edit->state() == ConnectionEdit::Dragging) {
568 if (m_edit->m_drag_end_point.type == EndPoint::Source) {
569 const QPoint p(s.x(), t.y());
570 m_knee_list.append(p);
571 if (tr.contains(p))
572 t = m_target_pos = p;
573 } else {
574 const QPoint p(t.x(), s.y());
575 m_knee_list.append(p);
576 if (sr.contains(p))
577 s = m_source_pos = p;
578 }
579 } else {
580 m_knee_list.append(QPoint(t.x(), s.y()));
581 }
582 } else {
583/*
584 +--------+
585 | |
586 | o |
587 +--|-----+
588 | +--------+
589 +---+-x |
590 | |
591 +--------+
592
593*/
594 m_knee_list.append(QPoint(s.x(), t.y()));
595 }
596 } else {
597/*
598 +--------+
599 | |
600 | o o-+--------+
601 +--|-----+ |
602 | +-----|--+
603 | | x |
604 +--------+-x |
605 +--------+
606
607 The line enters the target rectangle through the closest edge.
608*/
609 if (sr.topLeft() == r.topLeft()) {
610 if (pointAboveLine(tr.topLeft(), tr.bottomRight(), t))
611 m_knee_list.append(QPoint(t.x(), s.y()));
612 else
613 m_knee_list.append(QPoint(s.x(), t.y()));
614 } else if (sr.topRight() == r.topRight()) {
615 if (pointAboveLine(tr.bottomLeft(), tr.topRight(), t))
616 m_knee_list.append(QPoint(t.x(), s.y()));
617 else
618 m_knee_list.append(QPoint(s.x(), t.y()));
619 } else if (sr.bottomRight() == r.bottomRight()) {
620 if (pointAboveLine(tr.topLeft(), tr.bottomRight(), t))
621 m_knee_list.append(QPoint(s.x(), t.y()));
622 else
623 m_knee_list.append(QPoint(t.x(), s.y()));
624 } else {
625 if (pointAboveLine(tr.bottomLeft(), tr.topRight(), t))
626 m_knee_list.append(QPoint(s.x(), t.y()));
627 else
628 m_knee_list.append(QPoint(t.x(), s.y()));
629 }
630 }
631 }
632 m_knee_list.append(t);
633
634 if (m_knee_list.size() == 2)
635 m_knee_list.clear();
636
637 trimLine();
638
639 const LineDir new_source_label_dir = labelDir(EndPoint::Source);
640 const LineDir new_target_label_dir = labelDir(EndPoint::Target);
641 if (new_source_label_dir != old_source_label_dir)
642 updatePixmap(EndPoint::Source);
643 if (new_target_label_dir != old_target_label_dir)
644 updatePixmap(EndPoint::Target);
645}
646
647void Connection::trimLine()
648{
649 if (m_source == 0 || m_source_pos == QPoint(-1, -1) || m_target_pos == QPoint(-1, -1))
650 return;
651 int cnt = m_knee_list.size();
652 if (cnt < 2)
653 return;
654
655 const QRect sr = m_source_rect;
656 const QRect tr = m_target_rect;
657
658 if (sr.contains(m_knee_list.at(1)))
659 m_knee_list.removeFirst();
660
661 cnt = m_knee_list.size();
662 if (cnt < 2)
663 return;
664
665 if (!tr.contains(sr) && tr.contains(m_knee_list.at(cnt - 2)))
666 m_knee_list.removeLast();
667
668 cnt = m_knee_list.size();
669 if (cnt < 2)
670 return;
671
672 if (sr.contains(m_knee_list.at(0)) && !sr.contains(m_knee_list.at(1)))
673 m_knee_list[0] = lineEntryPos(m_knee_list.at(1), m_knee_list.at(0), sr);
674
675 if (tr.contains(m_knee_list.at(cnt - 1)) && !tr.contains(m_knee_list.at(cnt - 2))) {
676 m_knee_list[cnt - 1]
677 = lineEntryPos(m_knee_list.at(cnt - 2), m_knee_list.at(cnt - 1), tr);
678 m_arrow_head = arrowHead(m_knee_list.at(cnt - 2), m_knee_list.at(cnt - 1));
679 }
680}
681
682void Connection::setSource(QObject *source, const QPoint &pos)
683{
684 if (source == m_source && m_source_pos == pos)
685 return;
686
687 update(false);
688
689 m_source = source;
690 if (QWidget *widget = qobject_cast<QWidget*>(source)) {
691 m_source_pos = pos;
692 m_source_rect = m_edit->widgetRect(widget);
693 updateKneeList();
694 }
695
696 update(false);
697}
698
699void Connection::setTarget(QObject *target, const QPoint &pos)
700{
701 if (target == m_target && m_target_pos == pos)
702 return;
703
704 update(false);
705
706 m_target = target;
707 if (QWidget *widget = qobject_cast<QWidget*>(target)) {
708 m_target_pos = pos;
709 m_target_rect = m_edit->widgetRect(widget);
710 updateKneeList();
711 }
712
713 update(false);
714}
715
716static QRect lineRect(const QPoint &a, const QPoint &b)
717{
718 const QPoint c(qMin(a.x(), b.x()), qMin(a.y(), b.y()));
719 const QPoint d(qMax(a.x(), b.x()), qMax(a.y(), b.y()));
720
721 QRect result(c, d);
722 return expand(result, LINE_PROXIMITY_RADIUS);
723}
724
725QRect Connection::groundRect() const
726{
727 if (!ground())
728 return QRect();
729 if (m_knee_list.isEmpty())
730 return QRect();
731
732 const QPoint p = m_knee_list.last();
733 return QRect(p.x() - GROUND_W/2, p.y(), GROUND_W, GROUND_H);
734}
735
736QRegion Connection::region() const
737{
738 QRegion result;
739
740 for (int i = 0; i < m_knee_list.size() - 1; ++i)
741 result = result.unite(lineRect(m_knee_list.at(i), m_knee_list.at(i + 1)));
742
743 if (!m_arrow_head.isEmpty()) {
744 QRect r = m_arrow_head.boundingRect().toRect();
745 r = expand(r, 1);
746 result = result.unite(r);
747 } else if (ground()) {
748 result = result.unite(groundRect());
749 }
750
751 result = result.unite(labelRect(EndPoint::Source));
752 result = result.unite(labelRect(EndPoint::Target));
753
754 return result;
755}
756
757void Connection::update(bool update_widgets) const
758{
759 m_edit->update(region());
760 if (update_widgets) {
761 if (m_source != 0)
762 m_edit->update(m_source_rect);
763 if (m_target != 0)
764 m_edit->update(m_target_rect);
765 }
766
767 m_edit->update(endPointRect(EndPoint::Source));
768 m_edit->update(endPointRect(EndPoint::Target));
769}
770
771void Connection::paint(QPainter *p) const
772{
773 for (int i = 0; i < m_knee_list.size() - 1; ++i)
774 p->drawLine(m_knee_list.at(i), m_knee_list.at(i + 1));
775
776 if (!m_arrow_head.isEmpty()) {
777 p->save();
778 p->setBrush(p->pen().color());
779 p->drawPolygon(m_arrow_head);
780 p->restore();
781 } else if (ground()) {
782 paintGround(p, groundRect());
783 }
784}
785
786bool Connection::contains(const QPoint &pos) const
787{
788 return region().contains(pos);
789}
790
791QRect Connection::endPointRect(EndPoint::Type type) const
792{
793 if (type == EndPoint::Source) {
794 if (m_source_pos != QPoint(-1, -1))
795 return endPointRectHelper(m_source_pos);
796 } else {
797 if (m_target_pos != QPoint(-1, -1))
798 return endPointRectHelper(m_target_pos);
799 }
800 return QRect();
801}
802
803CETypes::LineDir Connection::labelDir(EndPoint::Type type) const
804{
805 const int cnt = m_knee_list.size();
806 if (cnt < 2)
807 return RightDir;
808
809 LineDir dir;
810 if (type == EndPoint::Source)
811 dir = classifyLine(m_knee_list.at(0), m_knee_list.at(1));
812 else
813 dir = classifyLine(m_knee_list.at(cnt - 2), m_knee_list.at(cnt - 1));
814
815 if (dir == LeftDir)
816 dir = RightDir;
817 if (dir == UpDir)
818 dir = DownDir;
819
820 return dir;
821}
822
823QRect Connection::labelRect(EndPoint::Type type) const
824{
825 const int cnt = m_knee_list.size();
826 if (cnt < 2)
827 return QRect();
828 const QString text = label(type);
829 if (text.isEmpty())
830 return QRect();
831
832 const QSize size = labelPixmap(type).size();
833 QPoint p1, p2;
834 if (type == EndPoint::Source) {
835 p1 = m_knee_list.at(0);
836 p2 = m_knee_list.at(1);
837 } else {
838 p1 = m_knee_list.at(cnt - 1);
839 p2 = m_knee_list.at(cnt - 2);
840 }
841 const LineDir dir = classifyLine(p1, p2);
842
843 QRect result;
844 switch (dir) {
845 case UpDir:
846 result = QRect(p1 + QPoint(-size.width()/2, 0), size);
847 break;
848 case DownDir:
849 result = QRect(p1 + QPoint(-size.width()/2, -size.height()), size);
850 break;
851 case LeftDir:
852 result = QRect(p1 + QPoint(0, -size.height()/2), size);
853 break;
854 case RightDir:
855 result = QRect(p1 + QPoint(-size.width(), -size.height()/2), size);
856 break;
857 }
858
859 return result;
860}
861
862void Connection::setLabel(EndPoint::Type type, const QString &text)
863{
864 if (text == label(type))
865 return;
866
867 if (type == EndPoint::Source)
868 m_source_label = text;
869 else
870 m_target_label = text;
871
872 updatePixmap(type);
873}
874
875void Connection::updatePixmap(EndPoint::Type type)
876{
877 QPixmap *pm = type == EndPoint::Source ? &m_source_label_pm : &m_target_label_pm;
878
879 const QString text = label(type);
880 if (text.isEmpty()) {
881 *pm = QPixmap();
882 return;
883 }
884
885 const QFontMetrics fm = m_edit->fontMetrics();
886 const QSize size = fm.size(Qt::TextSingleLine, text) + QSize(HLABEL_MARGIN*2, VLABEL_MARGIN*2);
887 *pm = QPixmap(size);
888 QColor color = m_edit->palette().color(QPalette::Normal, QPalette::Base);
889 color.setAlpha(190);
890 pm->fill(color);
891
892 QPainter p(pm);
893 p.setPen(m_edit->palette().color(QPalette::Normal, QPalette::Text));
894 p.drawText(-fm.leftBearing(text.at(0)) + HLABEL_MARGIN, fm.ascent() + VLABEL_MARGIN, text);
895 p.end();
896
897 const LineDir dir = labelDir(type);
898
899 if (dir == DownDir)
900 *pm = pm->transformed(QMatrix(0.0, -1.0, 1.0, 0.0, 0.0, 0.0));
901}
902
903void Connection::checkWidgets()
904{
905 bool changed = false;
906
907 if (QWidget *sourceWidget = qobject_cast<QWidget*>(m_source)) {
908 const QRect r = m_edit->widgetRect(sourceWidget);
909 if (r != m_source_rect) {
910 if (m_source_pos != QPoint(-1, -1) && !r.contains(m_source_pos)) {
911 QPoint offset = m_source_pos - m_source_rect.topLeft();
912 QPoint old_pos = m_source_pos;
913 m_source_pos = pointInsideRect(r, r.topLeft() + offset);
914 }
915 m_edit->update(m_source_rect);
916 m_source_rect = r;
917 changed = true;
918 }
919 }
920
921 if (QWidget *targetWidget = qobject_cast<QWidget*>(m_target)) {
922 const QRect r = m_edit->widgetRect(targetWidget);
923 if (r != m_target_rect) {
924 if (m_target_pos != QPoint(-1, -1) && !r.contains(m_target_pos)) {
925 const QPoint offset = m_target_pos - m_target_rect.topLeft();
926 const QPoint old_pos = m_target_pos;
927 m_target_pos = pointInsideRect(r, r.topLeft() + offset);
928 }
929 m_edit->update(m_target_rect);
930 m_target_rect = r;
931 changed = true;
932 }
933 }
934
935 if (changed) {
936 update();
937 updateKneeList();
938 update();
939 }
940}
941
942/*******************************************************************************
943** ConnectionEdit
944*/
945
946ConnectionEdit::ConnectionEdit(QWidget *parent, QDesignerFormWindowInterface *form) :
947 QWidget(parent),
948 m_bg_widget(0),
949 m_undo_stack(form->commandHistory()),
950 m_enable_update_background(false),
951 m_tmp_con(0),
952 m_start_connection_on_drag(true),
953 m_widget_under_mouse(0),
954 m_inactive_color(Qt::blue),
955 m_active_color(Qt::red)
956{
957 setAttribute(Qt::WA_MouseTracking, true);
958 setFocusPolicy(Qt::ClickFocus);
959
960 connect(form, SIGNAL(widgetRemoved(QWidget*)), this, SLOT(widgetRemoved(QWidget*)));
961 connect(form, SIGNAL(objectRemoved(QObject*)), this, SLOT(objectRemoved(QObject*)));
962}
963
964ConnectionEdit::~ConnectionEdit()
965{
966 qDeleteAll(m_con_list);
967}
968
969void ConnectionEdit::clear()
970{
971 m_con_list.clear();
972 m_sel_con_set.clear();
973 m_bg_widget = 0;
974 m_widget_under_mouse = 0;
975 m_tmp_con = 0;
976}
977
978void ConnectionEdit::setBackground(QWidget *background)
979{
980 if (background == m_bg_widget) {
981 // nothing to do
982 return;
983 }
984
985 m_bg_widget = background;
986 updateBackground();
987}
988
989void ConnectionEdit::enableUpdateBackground(bool enable)
990{
991 m_enable_update_background = enable;
992 if (enable)
993 updateBackground();
994}
995
996void ConnectionEdit::updateBackground()
997{
998 // Might happen while reloading a form.
999 if (m_bg_widget == 0)
1000 return;
1001
1002 if (!m_enable_update_background)
1003 return;
1004
1005 foreach(Connection *c, m_con_list)
1006 c->updateVisibility();
1007
1008 updateLines();
1009 update();
1010}
1011
1012QWidget *ConnectionEdit::widgetAt(const QPoint &pos) const
1013{
1014 if (m_bg_widget == 0)
1015 return 0;
1016 QWidget *widget = m_bg_widget->childAt(pos);
1017 if (widget == 0)
1018 widget = m_bg_widget;
1019
1020 return widget;
1021}
1022
1023
1024QRect ConnectionEdit::widgetRect(QWidget *w) const
1025{
1026 if (w == 0)
1027 return QRect();
1028 QRect r = w->geometry();
1029 QPoint pos = w->mapToGlobal(QPoint(0, 0));
1030 pos = mapFromGlobal(pos);
1031 r.moveTopLeft(pos);
1032 return r;
1033}
1034
1035ConnectionEdit::State ConnectionEdit::state() const
1036{
1037 if (m_tmp_con != 0)
1038 return Connecting;
1039 if (!m_drag_end_point.isNull())
1040 return Dragging;
1041 return Editing;
1042}
1043
1044void ConnectionEdit::paintLabel(QPainter *p, EndPoint::Type type, Connection *con)
1045{
1046 if (con->label(type).isEmpty())
1047 return;
1048
1049 const bool heavy = selected(con) || con == m_tmp_con;
1050 p->setPen(heavy ? m_active_color : m_inactive_color);
1051 p->setBrush(Qt::NoBrush);
1052 const QRect r = con->labelRect(type);
1053 p->drawPixmap(r.topLeft(), con->labelPixmap(type));
1054 p->drawRect(fixRect(r));
1055}
1056
1057void ConnectionEdit::paintConnection(QPainter *p, Connection *con,
1058 WidgetSet *heavy_highlight_set,
1059 WidgetSet *light_highlight_set) const
1060{
1061 QWidget *source = con->widget(EndPoint::Source);
1062 QWidget *target = con->widget(EndPoint::Target);
1063
1064 const bool heavy = selected(con) || con == m_tmp_con;
1065 WidgetSet *set = heavy ? heavy_highlight_set : light_highlight_set;
1066 p->setPen(heavy ? m_active_color : m_inactive_color);
1067 con->paint(p);
1068
1069 if (source != 0 && source != m_bg_widget)
1070 set->insert(source, source);
1071
1072 if (target != 0 && target != m_bg_widget)
1073 set->insert(target, target);
1074}
1075
1076void ConnectionEdit::paintEvent(QPaintEvent *e)
1077{
1078 QPainter p(this);
1079 p.setClipRegion(e->region());
1080
1081 WidgetSet heavy_highlight_set, light_highlight_set;
1082
1083 foreach (Connection *con, m_con_list) {
1084 if (!con->isVisible())
1085 continue;
1086
1087 paintConnection(&p, con, &heavy_highlight_set, &light_highlight_set);
1088 }
1089
1090 if (m_tmp_con != 0)
1091 paintConnection(&p, m_tmp_con, &heavy_highlight_set, &light_highlight_set);
1092
1093 if (!m_widget_under_mouse.isNull() && m_widget_under_mouse != m_bg_widget)
1094 heavy_highlight_set.insert(m_widget_under_mouse, m_widget_under_mouse);
1095
1096 QColor c = m_active_color;
1097 p.setPen(c);
1098 c.setAlpha(BG_ALPHA);
1099 p.setBrush(c);
1100
1101 foreach (QWidget *w, heavy_highlight_set) {
1102 p.drawRect(fixRect(widgetRect(w)));
1103 light_highlight_set.remove(w);
1104 }
1105
1106 c = m_inactive_color;
1107 p.setPen(c);
1108 c.setAlpha(BG_ALPHA);
1109 p.setBrush(c);
1110
1111 foreach (QWidget *w, light_highlight_set)
1112 p.drawRect(fixRect(widgetRect(w)));
1113
1114 p.setBrush(palette().color(QPalette::Base));
1115 p.setPen(palette().color(QPalette::Text));
1116 foreach (Connection *con, m_con_list) {
1117 if (!con->isVisible())
1118 continue;
1119
1120 paintLabel(&p, EndPoint::Source, con);
1121 paintLabel(&p, EndPoint::Target, con);
1122 }
1123
1124 p.setPen(m_active_color);
1125 p.setBrush(m_active_color);
1126
1127 foreach (Connection *con, m_con_list) {
1128 if (!selected(con) || !con->isVisible())
1129 continue;
1130
1131 paintEndPoint(&p, con->endPointPos(EndPoint::Source));
1132
1133 if (con->widget(EndPoint::Target) != 0)
1134 paintEndPoint(&p, con->endPointPos(EndPoint::Target));
1135 }
1136}
1137
1138void ConnectionEdit::abortConnection()
1139{
1140 m_tmp_con->update();
1141 delete m_tmp_con;
1142 m_tmp_con = 0;
1143#ifndef QT_NO_CURSOR
1144 setCursor(QCursor());
1145#endif
1146 if (m_widget_under_mouse == m_bg_widget)
1147 m_widget_under_mouse = 0;
1148}
1149
1150void ConnectionEdit::mousePressEvent(QMouseEvent *e)
1151{
1152 // Right click only to cancel
1153 const Qt::MouseButton button = e->button();
1154 const State cstate = state();
1155 if (button != Qt::LeftButton && !(button == Qt::RightButton && cstate == Connecting)) {
1156 QWidget::mousePressEvent(e);
1157 return;
1158 }
1159
1160 e->accept();
1161 // Prefer a non-background widget over the connection,
1162 // otherwise, widgets covered by the connection labels cannot be accessed
1163 Connection *con_under_mouse = 0;
1164 if (!m_widget_under_mouse || m_widget_under_mouse == m_bg_widget)
1165 con_under_mouse = connectionAt(e->pos());
1166
1167 m_start_connection_on_drag = false;
1168 switch (cstate) {
1169 case Connecting:
1170 if (button == Qt::RightButton)
1171 abortConnection();
1172 break;
1173 case Dragging:
1174 break;
1175 case Editing:
1176 if (!m_end_point_under_mouse.isNull()) {
1177 if (!(e->modifiers() & Qt::ShiftModifier)) {
1178 startDrag(m_end_point_under_mouse, e->pos());
1179 }
1180 } else if (con_under_mouse != 0) {
1181 if (!(e->modifiers() & Qt::ShiftModifier)) {
1182 selectNone();
1183 setSelected(con_under_mouse, true);
1184 } else {
1185 setSelected(con_under_mouse, !selected(con_under_mouse));
1186 }
1187 } else {
1188 if (!(e->modifiers() & Qt::ShiftModifier)) {
1189 selectNone();
1190 if (!m_widget_under_mouse.isNull())
1191 m_start_connection_on_drag = true;
1192 }
1193 }
1194 break;
1195 }
1196}
1197
1198void ConnectionEdit::mouseDoubleClickEvent(QMouseEvent *e)
1199{
1200 if (e->button() != Qt::LeftButton) {
1201 QWidget::mouseDoubleClickEvent(e);
1202 return;
1203 }
1204
1205 e->accept();
1206 switch (state()) {
1207 case Connecting:
1208 abortConnection();
1209 break;
1210 case Dragging:
1211 break;
1212 case Editing:
1213 if (!m_widget_under_mouse.isNull()) {
1214 emit widgetActivated(m_widget_under_mouse);
1215 } else if (m_sel_con_set.size() == 1) {
1216 Connection *con = m_sel_con_set.keys().first();
1217 modifyConnection(con);
1218 }
1219 break;
1220 }
1221
1222}
1223
1224void ConnectionEdit::mouseReleaseEvent(QMouseEvent *e)
1225{
1226 if (e->button() != Qt::LeftButton) {
1227 QWidget::mouseReleaseEvent(e);
1228 return;
1229 }
1230 e->accept();
1231
1232 switch (state()) {
1233 case Connecting:
1234 if (m_widget_under_mouse.isNull())
1235 abortConnection();
1236 else
1237 endConnection(m_widget_under_mouse, e->pos());
1238#ifndef QT_NO_CURSOR
1239 setCursor(QCursor());
1240#endif
1241 break;
1242 case Editing:
1243 break;
1244 case Dragging:
1245 endDrag(e->pos());
1246 break;
1247 }
1248}
1249
1250
1251void ConnectionEdit::findObjectsUnderMouse(const QPoint &pos)
1252{
1253 Connection *con_under_mouse = connectionAt(pos);
1254
1255 QWidget *w = widgetAt(pos);
1256 // Prefer a non-background widget over the connection,
1257 // otherwise, widgets covered by the connection labels cannot be accessed
1258 if (w == m_bg_widget && con_under_mouse)
1259 w = 0;
1260 else
1261 con_under_mouse = 0;
1262
1263 if (w != m_widget_under_mouse) {
1264 if (!m_widget_under_mouse.isNull())
1265 update(widgetRect(m_widget_under_mouse));
1266 m_widget_under_mouse = w;
1267 if (!m_widget_under_mouse.isNull())
1268 update(widgetRect(m_widget_under_mouse));
1269 }
1270
1271 const EndPoint hs = endPointAt(pos);
1272 if (hs != m_end_point_under_mouse) {
1273#ifndef QT_NO_CURSOR
1274 if (m_end_point_under_mouse.isNull())
1275 setCursor(Qt::PointingHandCursor);
1276 else
1277 setCursor(QCursor());
1278#endif
1279 m_end_point_under_mouse = hs;
1280 }
1281}
1282
1283void ConnectionEdit::mouseMoveEvent(QMouseEvent *e)
1284{
1285 findObjectsUnderMouse(e->pos());
1286 switch (state()) {
1287 case Connecting:
1288 continueConnection(m_widget_under_mouse, e->pos());
1289 break;
1290 case Editing:
1291 if ((e->buttons() & Qt::LeftButton)
1292 && m_start_connection_on_drag
1293 && !m_widget_under_mouse.isNull()) {
1294 m_start_connection_on_drag = false;
1295 startConnection(m_widget_under_mouse, e->pos());
1296#ifndef QT_NO_CURSOR
1297 setCursor(Qt::CrossCursor);
1298#endif
1299 }
1300 break;
1301 case Dragging:
1302 continueDrag(e->pos());
1303 break;
1304 }
1305
1306 e->accept();
1307}
1308
1309void ConnectionEdit::keyPressEvent(QKeyEvent *e)
1310{
1311 switch (e->key()) {
1312 case Qt::Key_Delete:
1313 if (state() == Editing)
1314 deleteSelected();
1315 break;
1316 case Qt::Key_Escape:
1317 if (state() == Connecting)
1318 abortConnection();
1319 break;
1320 }
1321
1322 e->accept();
1323}
1324
1325void ConnectionEdit::startConnection(QWidget *source, const QPoint &pos)
1326{
1327 Q_ASSERT(m_tmp_con == 0);
1328
1329 m_tmp_con = new Connection(this);
1330 m_tmp_con->setEndPoint(EndPoint::Source, source, pos);
1331}
1332
1333void ConnectionEdit::endConnection(QWidget *target, const QPoint &pos)
1334{
1335 Q_ASSERT(m_tmp_con != 0);
1336
1337 m_tmp_con->setEndPoint(EndPoint::Target, target, pos);
1338
1339 QWidget *source = m_tmp_con->widget(EndPoint::Source);
1340 Q_ASSERT(source != 0);
1341 Q_ASSERT(target != 0);
1342 setEnabled(false);
1343 Connection *new_con = createConnection(source, target);
1344 setEnabled(true);
1345 if (new_con != 0) {
1346 new_con->setEndPoint(EndPoint::Source, source, m_tmp_con->endPointPos(EndPoint::Source));
1347 new_con->setEndPoint(EndPoint::Target, target, m_tmp_con->endPointPos(EndPoint::Target));
1348 m_undo_stack->push(new AddConnectionCommand(this, new_con));
1349 emit connectionChanged(new_con);
1350 }
1351
1352 delete m_tmp_con;
1353 m_tmp_con = 0;
1354
1355 findObjectsUnderMouse(mapFromGlobal(QCursor::pos()));
1356}
1357
1358void ConnectionEdit::continueConnection(QWidget *target, const QPoint &pos)
1359{
1360 Q_ASSERT(m_tmp_con != 0);
1361
1362 m_tmp_con->setEndPoint(EndPoint::Target, target, pos);
1363}
1364
1365void ConnectionEdit::modifyConnection(Connection *)
1366{
1367}
1368
1369Connection *ConnectionEdit::createConnection(QWidget *source, QWidget *target)
1370{
1371 Connection *con = new Connection(this, source, target);
1372 return con;
1373}
1374
1375// Find all connections which in which a sequence of objects is involved
1376template <class ObjectIterator>
1377static ConnectionEdit::ConnectionSet findConnectionsOf(const ConnectionEdit::ConnectionList &cl, ObjectIterator oi1, const ObjectIterator &oi2)
1378{
1379 ConnectionEdit::ConnectionSet rc;
1380
1381 const ConnectionEdit::ConnectionList::const_iterator ccend = cl.constEnd();
1382 for ( ; oi1 != oi2; ++oi1) {
1383 for (ConnectionEdit::ConnectionList::const_iterator cit = cl.constBegin(); cit != ccend; ++cit) {
1384 Connection *con = *cit;
1385 if (con->object(ConnectionEdit::EndPoint::Source) == *oi1 || con->object(ConnectionEdit::EndPoint::Target) == *oi1)
1386 rc.insert(con, con);
1387 }
1388 }
1389 return rc;
1390}
1391
1392void ConnectionEdit::widgetRemoved(QWidget *widget)
1393{
1394 // Remove all connections of that widget and its children.
1395 if (m_con_list.empty())
1396 return;
1397
1398 QWidgetList child_list = qFindChildren<QWidget*>(widget);
1399 child_list.prepend(widget);
1400
1401 const ConnectionSet remove_set = findConnectionsOf(m_con_list, child_list.constBegin(), child_list.constEnd());
1402
1403 if (!remove_set.isEmpty())
1404 m_undo_stack->push(new DeleteConnectionsCommand(this, remove_set.keys()));
1405
1406 updateBackground();
1407}
1408
1409void ConnectionEdit::objectRemoved(QObject *o)
1410{
1411 // Remove all connections of that object and its children (in case of action groups).
1412 if (m_con_list.empty())
1413 return;
1414
1415 QObjectList child_list = o->children();
1416 child_list.prepend(o);
1417 const ConnectionSet remove_set = findConnectionsOf(m_con_list, child_list.constBegin(), child_list.constEnd());
1418 if (!remove_set.isEmpty())
1419 m_undo_stack->push(new DeleteConnectionsCommand(this, remove_set.keys()));
1420
1421 updateBackground();
1422}
1423
1424void ConnectionEdit::setSelected(Connection *con, bool sel)
1425{
1426 if (!con || sel == m_sel_con_set.contains(con))
1427 return;
1428
1429 if (sel) {
1430 m_sel_con_set.insert(con, con);
1431 emit connectionSelected(con);
1432 } else {
1433 m_sel_con_set.remove(con);
1434 }
1435
1436 con->update();
1437}
1438
1439bool ConnectionEdit::selected(const Connection *con) const
1440{
1441 return m_sel_con_set.contains(const_cast<Connection*>(con));
1442}
1443
1444void ConnectionEdit::selectNone()
1445{
1446 foreach (Connection *con, m_sel_con_set)
1447 con->update();
1448
1449 m_sel_con_set.clear();
1450}
1451
1452void ConnectionEdit::selectAll()
1453{
1454 if (m_sel_con_set.size() == m_con_list.size())
1455 return;
1456 foreach (Connection *con, m_con_list)
1457 setSelected(con, true);
1458}
1459
1460Connection *ConnectionEdit::connectionAt(const QPoint &pos) const
1461{
1462 foreach (Connection *con, m_con_list) {
1463 if (con->contains(pos))
1464 return con;
1465 }
1466 return 0;
1467}
1468
1469CETypes::EndPoint ConnectionEdit::endPointAt(const QPoint &pos) const
1470{
1471 foreach (Connection *con, m_con_list) {
1472 if (!selected(con))
1473 continue;
1474 const QRect sr = con->endPointRect(EndPoint::Source);
1475 const QRect tr = con->endPointRect(EndPoint::Target);
1476
1477 if (sr.contains(pos))
1478 return EndPoint(con, EndPoint::Source);
1479 if (tr.contains(pos))
1480 return EndPoint(con, EndPoint::Target);
1481 }
1482 return EndPoint();
1483}
1484
1485void ConnectionEdit::startDrag(const EndPoint &end_point, const QPoint &pos)
1486{
1487 Q_ASSERT(m_drag_end_point.isNull());
1488 m_drag_end_point = end_point;
1489 m_old_source_pos = m_drag_end_point.con->endPointPos(EndPoint::Source);
1490 m_old_target_pos = m_drag_end_point.con->endPointPos(EndPoint::Target);
1491 adjustHotSopt(m_drag_end_point, pos);
1492}
1493
1494void ConnectionEdit::continueDrag(const QPoint &pos)
1495{
1496 Q_ASSERT(!m_drag_end_point.isNull());
1497 adjustHotSopt(m_drag_end_point, pos);
1498}
1499
1500void ConnectionEdit::endDrag(const QPoint &pos)
1501{
1502 Q_ASSERT(!m_drag_end_point.isNull());
1503 adjustHotSopt(m_drag_end_point, pos);
1504
1505 Connection *con = m_drag_end_point.con;
1506 const QPoint new_source_pos = con->endPointPos(EndPoint::Source);
1507 const QPoint new_target_pos = con->endPointPos(EndPoint::Target);
1508 m_undo_stack->push(new AdjustConnectionCommand(this, con, m_old_source_pos, m_old_target_pos,
1509 new_source_pos, new_target_pos));
1510
1511 m_drag_end_point = EndPoint();
1512}
1513
1514void ConnectionEdit::adjustHotSopt(const EndPoint &end_point, const QPoint &pos)
1515{
1516 QWidget *w = end_point.con->widget(end_point.type);
1517 end_point.con->setEndPoint(end_point.type, w, pointInsideRect(widgetRect(w), pos));
1518}
1519
1520void ConnectionEdit::deleteSelected()
1521{
1522 if (m_sel_con_set.isEmpty())
1523 return;
1524 m_undo_stack->push(new DeleteConnectionsCommand(this, m_sel_con_set.keys()));
1525}
1526
1527void ConnectionEdit::addConnection(Connection *con)
1528{
1529 m_con_list.append(con);
1530}
1531
1532void ConnectionEdit::updateLines()
1533{
1534 foreach (Connection *con, m_con_list)
1535 con->checkWidgets();
1536}
1537
1538void ConnectionEdit::resizeEvent(QResizeEvent *e)
1539{
1540 updateBackground();
1541 QWidget::resizeEvent(e);
1542}
1543
1544void ConnectionEdit::setSource(Connection *con, const QString &obj_name)
1545{
1546 QObject *object = 0;
1547 if (!obj_name.isEmpty()) {
1548 object = qFindChild<QObject*>(m_bg_widget, obj_name);
1549 if (object == 0 && m_bg_widget->objectName() == obj_name)
1550 object = m_bg_widget;
1551
1552 if (object == con->object(EndPoint::Source))
1553 return;
1554 }
1555 m_undo_stack->push(new SetEndPointCommand(this, con, EndPoint::Source, object));
1556}
1557
1558void ConnectionEdit::setTarget(Connection *con, const QString &obj_name)
1559{
1560 QObject *object = 0;
1561 if (!obj_name.isEmpty()) {
1562 object = qFindChild<QObject*>(m_bg_widget, obj_name);
1563 if (object == 0 && m_bg_widget->objectName() == obj_name)
1564 object = m_bg_widget;
1565
1566 if (object == con->object(EndPoint::Target))
1567 return;
1568 }
1569 m_undo_stack->push(new SetEndPointCommand(this, con, EndPoint::Target, object));
1570}
1571
1572Connection *ConnectionEdit::takeConnection(Connection *con)
1573{
1574 if (!m_con_list.contains(con))
1575 return 0;
1576 m_con_list.removeAll(con);
1577 return con;
1578}
1579
1580void ConnectionEdit::clearNewlyAddedConnection()
1581{
1582 delete m_tmp_con;
1583 m_tmp_con = 0;
1584}
1585
1586void ConnectionEdit::createContextMenu(QMenu &menu)
1587{
1588 // Select
1589 QAction *selectAllAction = menu.addAction(tr("Select All"));
1590 selectAllAction->setEnabled(connectionList().size());
1591 connect(selectAllAction, SIGNAL(triggered()), this, SLOT(selectAll()));
1592 QAction *deselectAllAction = menu.addAction(tr("Deselect All"));
1593 deselectAllAction->setEnabled(selection().size());
1594 connect(deselectAllAction, SIGNAL(triggered()), this, SLOT(selectNone()));
1595 menu.addSeparator();
1596 // Delete
1597 QAction *deleteAction = menu.addAction(tr("Delete"));
1598 deleteAction->setShortcut(QKeySequence::Delete);
1599 deleteAction->setEnabled(!selection().isEmpty());
1600 connect(deleteAction, SIGNAL(triggered()), this, SLOT(deleteSelected()));
1601}
1602
1603void ConnectionEdit::contextMenuEvent(QContextMenuEvent * event)
1604{
1605 QMenu menu;
1606 createContextMenu(menu);
1607 menu.exec(event->globalPos());
1608}
1609
1610} // namespace qdesigner_internal
1611
1612QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.