📄 connectionedit.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.**** This file is part of the Qt Designer of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://www.trolltech.com/products/qt/opensource.html**** If you are unsure which license is appropriate for your use, please** review the following information:** http://www.trolltech.com/products/qt/licensing.html or contact the** sales department at sales@trolltech.com.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#include <QtGui/QPainter>#include <QtGui/QPaintEvent>#include <QtGui/QFontMetrics>#include <QtGui/QPixmap>#include <QtGui/QMatrix>#include <QtGui/qevent.h>#include <QtCore/qdebug.h>#include <QtDesigner/abstractformwindow.h>#include "connectionedit_p.h"#define BG_ALPHA 32#define LINE_PROXIMITY_RADIUS 3#define LOOP_MARGIN 20#define VLABEL_MARGIN 1#define HLABEL_MARGIN 3#define GROUND_W 20#define GROUND_H 25namespace qdesigner_internal {/********************************************************************************* Tools*/static QRect fixRect(const QRect &r){ return QRect(r.x(), r.y(), r.width() - 1, r.height() - 1);}static QRect expand(const QRect &r, int i){ return QRect(r.x() - i, r.y() - i, r.width() + 2*i, r.height() + 2*i);}static QRect endPointRect(const QPoint &pos){ QRect r(pos + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS), QSize(2*LINE_PROXIMITY_RADIUS, 2*LINE_PROXIMITY_RADIUS)); return r;}static void paintGround(QPainter *p, QRect r){ QPoint mid = r.center(); p->drawLine(mid.x(), r.top(), mid.x(), mid.y()); p->drawLine(r.left(), mid.y(), r.right(), mid.y()); int y = r.top() + 4*r.height()/6; int x = GROUND_W/6; p->drawLine(r.left() + x, y, r.right() - x, y); y = r.top() + 5*r.height()/6; x = 2*GROUND_W/6; p->drawLine(r.left() + x, y, r.right() - x, y); p->drawLine(mid.x(), r.bottom(), mid.x() + 1, r.bottom());}static void paintEndPoint(QPainter *p, const QPoint &pos){ QRect r(pos + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS), QSize(2*LINE_PROXIMITY_RADIUS, 2*LINE_PROXIMITY_RADIUS)); p->fillRect(fixRect(r), p->pen().color());}static CETypes::LineDir classifyLine(const QPoint &p1, const QPoint &p2){ if (p1.x() == p2.x()) return p1.y() < p2.y() ? CETypes::DownDir : CETypes::UpDir; Q_ASSERT(p1.y() == p2.y()); return p1.x() < p2.x() ? CETypes::RightDir : CETypes::LeftDir;}static QPoint pointInsideRect(const QRect &r, QPoint p){ if (p.x() < r.left()) p.setX(r.left()); else if (p.x() > r.right()) p.setX(r.right()); if (p.y() < r.top()) p.setY(r.top()); else if (p.y() > r.bottom()) p.setY(r.bottom()); return p;}/********************************************************************************* Commands*/AddConnectionCommand::AddConnectionCommand(ConnectionEdit *edit, Connection *con) : CECommand(edit), m_con(con){ setDescription(tr("Add connection"));}void AddConnectionCommand::redo(){ edit()->selectNone(); emit edit()->aboutToAddConnection(edit()->m_con_list.size()); edit()->m_con_list.append(m_con); m_con->inserted(); edit()->setSelected(m_con, true); emit edit()->connectionAdded(m_con);}void AddConnectionCommand::undo(){ int idx = edit()->indexOfConnection(m_con); emit edit()->aboutToRemoveConnection(m_con); edit()->setSelected(m_con, false); m_con->update(); m_con->removed(); edit()->m_con_list.removeAll(m_con); emit edit()->connectionRemoved(idx);}class AdjustConnectionCommand : public CECommand{ Q_OBJECTpublic: 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); virtual void redo(); virtual void undo();private: Connection *m_con; QPoint m_old_source_pos, m_old_target_pos, m_new_source_pos, m_new_target_pos;};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) : CECommand(edit){ setDescription(tr("Adjust connection")); m_con = con; m_old_source_pos = old_source_pos; m_old_target_pos = old_target_pos; m_new_source_pos = new_source_pos; m_new_target_pos = new_target_pos;}void AdjustConnectionCommand::undo(){ m_con->setEndPoint(EndPoint::Source, m_con->widget(EndPoint::Source), m_old_source_pos); m_con->setEndPoint(EndPoint::Target, m_con->widget(EndPoint::Target), m_old_target_pos);}void AdjustConnectionCommand::redo(){ m_con->setEndPoint(EndPoint::Source, m_con->widget(EndPoint::Source), m_new_source_pos); m_con->setEndPoint(EndPoint::Target, m_con->widget(EndPoint::Target), m_new_target_pos);}DeleteConnectionsCommand::DeleteConnectionsCommand(ConnectionEdit *edit, const ConnectionList &con_list) : CECommand(edit), m_con_list(con_list){ setDescription(tr("Delete connections"));}void DeleteConnectionsCommand::redo(){ foreach (Connection *con, m_con_list) { int idx = edit()->indexOfConnection(con); emit edit()->aboutToRemoveConnection(con); Q_ASSERT(edit()->m_con_list.contains(con)); edit()->setSelected(con, false); con->update(); con->removed(); edit()->m_con_list.removeAll(con); emit edit()->connectionRemoved(idx); }}void DeleteConnectionsCommand::undo(){ foreach (Connection *con, m_con_list) { Q_ASSERT(!edit()->m_con_list.contains(con)); emit edit()->aboutToAddConnection(edit()->m_con_list.size()); edit()->m_con_list.append(con); edit()->setSelected(con, true); con->update(); con->inserted(); emit edit()->connectionAdded(con); }}class SetEndPointCommand : public CECommand{ Q_OBJECTpublic: SetEndPointCommand(ConnectionEdit *edit, Connection *con, EndPoint::Type type, QObject *object); virtual void redo(); virtual void undo();private: Connection *m_con; EndPoint::Type m_type; QObject *m_old_widget, *m_new_widget; QPoint m_old_pos, m_new_pos;};SetEndPointCommand::SetEndPointCommand(ConnectionEdit *edit, Connection *con, EndPoint::Type type, QObject *object) : CECommand(edit){ m_con = con; m_type = type; m_old_widget = con->object(type); m_old_pos = con->endPointPos(type); m_new_widget = object; if (QWidget *widget = qobject_cast<QWidget*>(object)) { m_new_pos = edit->widgetRect(widget).center(); } setDescription(tr("Change %1").arg(m_type == EndPoint::Source ? tr("source") : tr("target")));}void SetEndPointCommand::redo(){ m_con->setEndPoint(m_type, m_new_widget, m_new_pos); emit edit()->connectionChanged(m_con);}void SetEndPointCommand::undo(){ m_con->setEndPoint(m_type, m_old_widget, m_old_pos); emit edit()->connectionChanged(m_con);}/********************************************************************************* Connection*/Connection::Connection(ConnectionEdit *edit){ m_edit = edit; m_source = 0; m_target = 0; m_visible = true; m_source_pos = QPoint(-1, -1); m_target_pos = QPoint(-1, -1);}Connection::Connection(ConnectionEdit *edit, QObject *source, QObject *target){ m_edit = edit; m_source = source; m_target = target; m_visible = true; m_source_pos = QPoint(-1, -1); m_target_pos = QPoint(-1, -1);}void Connection::setVisible(bool b){ m_visible = b;}void Connection::updateVisibility(){ QWidget *source = widget(EndPoint::Source); QWidget *target = widget(EndPoint::Target); if (source == 0 || target == 0) { setVisible(false); return; } QWidget *w = source; while (w && w->parentWidget()) { if (!w->isVisibleTo(w->parentWidget())) { setVisible(false); return; } w = w->parentWidget(); } w = target; while (w && w->parentWidget()) { if (!w->isVisibleTo(w->parentWidget())) { setVisible(false); return; } w = w->parentWidget(); } setVisible(true);}bool Connection::isVisible() const{ return m_visible;}bool Connection::ground() const{ return m_target != 0 && m_target == m_edit->m_bg_widget;}QPoint Connection::endPointPos(EndPoint::Type type) const{ if (type == EndPoint::Source) return m_source_pos; else return m_target_pos;}static QPoint lineEntryPos(const QPoint &p1, const QPoint &p2, const QRect &rect){ QPoint result; CETypes::LineDir dir = classifyLine(p1, p2); switch (dir) { case CETypes::UpDir: result = QPoint(p1.x(), rect.bottom()); break; case CETypes::DownDir: result = QPoint(p1.x(), rect.top()); break; case CETypes::LeftDir: result = QPoint(rect.right(), p1.y()); break; case CETypes::RightDir: result = QPoint(rect.left(), p1.y()); break; } return result;}static QPolygonF arrowHead(const QPoint &p1, const QPoint &p2){ QPolygonF result; CETypes::LineDir dir = classifyLine(p1, p2); switch (dir) { case CETypes::UpDir: result.append(p2 + QPoint(0, 1)); result.append(p2 + QPoint(LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS*2 + 1)); result.append(p2 + QPoint(-LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS*2 + 1)); break; case CETypes::DownDir: result.append(p2); result.append(p2 + QPoint(LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS*2)); result.append(p2 + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS*2)); break; case CETypes::LeftDir: result.append(p2 + QPoint(1, 0)); result.append(p2 + QPoint(2*LINE_PROXIMITY_RADIUS + 1, -LINE_PROXIMITY_RADIUS)); result.append(p2 + QPoint(2*LINE_PROXIMITY_RADIUS + 1, LINE_PROXIMITY_RADIUS)); break; case CETypes::RightDir: result.append(p2); result.append(p2 + QPoint(-2*LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS)); result.append(p2 + QPoint(-2*LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS)); break; } return result;}static CETypes::LineDir closestEdge(const QPoint &p, const QRect &r){ CETypes::LineDir result = CETypes::UpDir; int min = p.y() - r.top(); int d = p.x() - r.left(); if (d < min) { min = d; result = CETypes::LeftDir; } d = r.bottom() - p.y(); if (d < min) { min = d; result = CETypes::DownDir; } d = r.right() - p.x(); if (d < min) { min = d; result = CETypes::RightDir; } return result;}static bool pointAboveLine(const QPoint &l1, const QPoint &l2, const QPoint &p){ if (l1.x() == l2.x()) return p.x() >= l1.x(); return p.y() <= l1.y() + (p.x() - l1.x())*(l2.y() - l1.y())/(l2.x() - l1.x());}void Connection::updateKneeList(){ LineDir old_source_label_dir = labelDir(EndPoint::Source); LineDir old_target_label_dir = labelDir(EndPoint::Target); QPoint s = endPointPos(EndPoint::Source); QPoint t = endPointPos(EndPoint::Target); QRect sr = m_source_rect; QRect tr = m_target_rect; m_knee_list.clear(); m_arrow_head.clear(); if (m_source == 0 || s == QPoint(-1, -1) || t == QPoint(-1, -1)) return; QRect r = sr | tr; m_knee_list.append(s); if (m_target == 0) { m_knee_list.append(QPoint(t.x(), s.y())); } else if (m_target == m_edit->m_bg_widget) { m_knee_list.append(QPoint(s.x(), t.y())); } else if (tr.contains(sr) || sr.contains(tr)) {/* +------------------+ | +----------+ | | | | | | | o | | | +---|------+ | | | x | +-----|-----|------+ +-----+ We find out which edge of the outer rectangle is closest to the target point, and make a loop which exits and re-enters through that edge.*/ LineDir dir = closestEdge(t, tr); switch (dir) { case UpDir: m_knee_list.append(QPoint(s.x(), r.top() - LOOP_MARGIN)); m_knee_list.append(QPoint(t.x(), r.top() - LOOP_MARGIN)); break; case DownDir: m_knee_list.append(QPoint(s.x(), r.bottom() + LOOP_MARGIN)); m_knee_list.append(QPoint(t.x(), r.bottom() + LOOP_MARGIN)); break; case LeftDir: m_knee_list.append(QPoint(r.left() - LOOP_MARGIN, s.y())); m_knee_list.append(QPoint(r.left() - LOOP_MARGIN, t.y())); break; case RightDir: m_knee_list.append(QPoint(r.right() + LOOP_MARGIN, s.y())); m_knee_list.append(QPoint(r.right() + LOOP_MARGIN, t.y())); break; } } else { if (r.height() < sr.height() + tr.height()) { if (s.y() >= tr.top() && s.y() <= tr.bottom() || t.y() >= sr.bottom() || t.y() <= sr.top()) {/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -