| 1 | /****************************************************************************
|
|---|
| 2 | **
|
|---|
| 3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
|---|
| 4 | ** All rights reserved.
|
|---|
| 5 | ** Contact: Nokia Corporation ([email protected])
|
|---|
| 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 [email protected].
|
|---|
| 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 |
|
|---|
| 59 | QT_BEGIN_NAMESPACE
|
|---|
| 60 |
|
|---|
| 61 | static const int BG_ALPHA = 32;
|
|---|
| 62 | static const int LINE_PROXIMITY_RADIUS = 3;
|
|---|
| 63 | static const int LOOP_MARGIN = 20;
|
|---|
| 64 | static const int VLABEL_MARGIN = 1;
|
|---|
| 65 | static const int HLABEL_MARGIN = 3;
|
|---|
| 66 | static const int GROUND_W = 20;
|
|---|
| 67 | static const int GROUND_H = 25;
|
|---|
| 68 |
|
|---|
| 69 | /*******************************************************************************
|
|---|
| 70 | ** Tools
|
|---|
| 71 | */
|
|---|
| 72 |
|
|---|
| 73 | static QRect fixRect(const QRect &r)
|
|---|
| 74 | {
|
|---|
| 75 | return QRect(r.x(), r.y(), r.width() - 1, r.height() - 1);
|
|---|
| 76 | }
|
|---|
| 77 |
|
|---|
| 78 | static 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 |
|
|---|
| 83 | static 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 |
|
|---|
| 90 | static 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 |
|
|---|
| 104 | static 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 |
|
|---|
| 111 | static 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 |
|
|---|
| 119 | static 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 |
|
|---|
| 134 | namespace qdesigner_internal {
|
|---|
| 135 |
|
|---|
| 136 | /*******************************************************************************
|
|---|
| 137 | ** Commands
|
|---|
| 138 | */
|
|---|
| 139 |
|
|---|
| 140 | AddConnectionCommand::AddConnectionCommand(ConnectionEdit *edit, Connection *con)
|
|---|
| 141 | : CECommand(edit), m_con(con)
|
|---|
| 142 | {
|
|---|
| 143 | setText(QApplication::translate("Command", "Add connection"));
|
|---|
| 144 | }
|
|---|
| 145 |
|
|---|
| 146 | void 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 |
|
|---|
| 156 | void 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 |
|
|---|
| 167 | class AdjustConnectionCommand : public CECommand
|
|---|
| 168 | {
|
|---|
| 169 | public:
|
|---|
| 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();
|
|---|
| 177 | private:
|
|---|
| 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 |
|
|---|
| 185 | AdjustConnectionCommand::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 |
|
|---|
| 200 | void 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 |
|
|---|
| 206 | void 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 |
|
|---|
| 212 | DeleteConnectionsCommand::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 |
|
|---|
| 219 | void 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 |
|
|---|
| 233 | void 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 |
|
|---|
| 246 | class SetEndPointCommand : public CECommand
|
|---|
| 247 | {
|
|---|
| 248 | public:
|
|---|
| 249 | SetEndPointCommand(ConnectionEdit *edit, Connection *con, EndPoint::Type type, QObject *object);
|
|---|
| 250 | virtual void redo();
|
|---|
| 251 | virtual void undo();
|
|---|
| 252 | private:
|
|---|
| 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 |
|
|---|
| 260 | SetEndPointCommand::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 |
|
|---|
| 279 | void SetEndPointCommand::redo()
|
|---|
| 280 | {
|
|---|
| 281 | m_con->setEndPoint(m_type, m_new_widget, m_new_pos);
|
|---|
| 282 | emit edit()->connectionChanged(m_con);
|
|---|
| 283 | }
|
|---|
| 284 |
|
|---|
| 285 | void 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 |
|
|---|
| 295 | Connection::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 |
|
|---|
| 306 | Connection::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 |
|
|---|
| 316 | void Connection::setVisible(bool b)
|
|---|
| 317 | {
|
|---|
| 318 | m_visible = b;
|
|---|
| 319 | }
|
|---|
| 320 |
|
|---|
| 321 | void 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 |
|
|---|
| 352 | bool Connection::isVisible() const
|
|---|
| 353 | {
|
|---|
| 354 | return m_visible;
|
|---|
| 355 | }
|
|---|
| 356 |
|
|---|
| 357 | bool Connection::ground() const
|
|---|
| 358 | {
|
|---|
| 359 | return m_target != 0 && m_target == m_edit->m_bg_widget;
|
|---|
| 360 | }
|
|---|
| 361 |
|
|---|
| 362 | QPoint 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 |
|
|---|
| 370 | static 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 |
|
|---|
| 392 | static 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 |
|
|---|
| 422 | static 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 |
|
|---|
| 448 | static 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 |
|
|---|
| 455 | void 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 |
|
|---|
| 647 | void 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 |
|
|---|
| 682 | void 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 |
|
|---|
| 699 | void 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 |
|
|---|
| 716 | static 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 |
|
|---|
| 725 | QRect 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 |
|
|---|
| 736 | QRegion 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 |
|
|---|
| 757 | void 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 |
|
|---|
| 771 | void 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 |
|
|---|
| 786 | bool Connection::contains(const QPoint &pos) const
|
|---|
| 787 | {
|
|---|
| 788 | return region().contains(pos);
|
|---|
| 789 | }
|
|---|
| 790 |
|
|---|
| 791 | QRect 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 |
|
|---|
| 803 | CETypes::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 |
|
|---|
| 823 | QRect 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 |
|
|---|
| 862 | void 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 |
|
|---|
| 875 | void 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 |
|
|---|
| 903 | void 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 |
|
|---|
| 946 | ConnectionEdit::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 |
|
|---|
| 964 | ConnectionEdit::~ConnectionEdit()
|
|---|
| 965 | {
|
|---|
| 966 | qDeleteAll(m_con_list);
|
|---|
| 967 | }
|
|---|
| 968 |
|
|---|
| 969 | void 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 |
|
|---|
| 978 | void 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 |
|
|---|
| 989 | void ConnectionEdit::enableUpdateBackground(bool enable)
|
|---|
| 990 | {
|
|---|
| 991 | m_enable_update_background = enable;
|
|---|
| 992 | if (enable)
|
|---|
| 993 | updateBackground();
|
|---|
| 994 | }
|
|---|
| 995 |
|
|---|
| 996 | void 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 |
|
|---|
| 1012 | QWidget *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 |
|
|---|
| 1024 | QRect 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 |
|
|---|
| 1035 | ConnectionEdit::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 |
|
|---|
| 1044 | void 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 |
|
|---|
| 1057 | void 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 |
|
|---|
| 1076 | void 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 |
|
|---|
| 1138 | void 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 |
|
|---|
| 1150 | void 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 |
|
|---|
| 1198 | void 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 |
|
|---|
| 1224 | void 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 |
|
|---|
| 1251 | void 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 |
|
|---|
| 1283 | void 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 |
|
|---|
| 1309 | void 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 |
|
|---|
| 1325 | void 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 |
|
|---|
| 1333 | void 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 |
|
|---|
| 1358 | void 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 |
|
|---|
| 1365 | void ConnectionEdit::modifyConnection(Connection *)
|
|---|
| 1366 | {
|
|---|
| 1367 | }
|
|---|
| 1368 |
|
|---|
| 1369 | Connection *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
|
|---|
| 1376 | template <class ObjectIterator>
|
|---|
| 1377 | static 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 |
|
|---|
| 1392 | void 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 |
|
|---|
| 1409 | void 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 |
|
|---|
| 1424 | void 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 |
|
|---|
| 1439 | bool ConnectionEdit::selected(const Connection *con) const
|
|---|
| 1440 | {
|
|---|
| 1441 | return m_sel_con_set.contains(const_cast<Connection*>(con));
|
|---|
| 1442 | }
|
|---|
| 1443 |
|
|---|
| 1444 | void ConnectionEdit::selectNone()
|
|---|
| 1445 | {
|
|---|
| 1446 | foreach (Connection *con, m_sel_con_set)
|
|---|
| 1447 | con->update();
|
|---|
| 1448 |
|
|---|
| 1449 | m_sel_con_set.clear();
|
|---|
| 1450 | }
|
|---|
| 1451 |
|
|---|
| 1452 | void 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 |
|
|---|
| 1460 | Connection *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 |
|
|---|
| 1469 | CETypes::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 |
|
|---|
| 1485 | void 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 |
|
|---|
| 1494 | void ConnectionEdit::continueDrag(const QPoint &pos)
|
|---|
| 1495 | {
|
|---|
| 1496 | Q_ASSERT(!m_drag_end_point.isNull());
|
|---|
| 1497 | adjustHotSopt(m_drag_end_point, pos);
|
|---|
| 1498 | }
|
|---|
| 1499 |
|
|---|
| 1500 | void 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 |
|
|---|
| 1514 | void 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 |
|
|---|
| 1520 | void 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 |
|
|---|
| 1527 | void ConnectionEdit::addConnection(Connection *con)
|
|---|
| 1528 | {
|
|---|
| 1529 | m_con_list.append(con);
|
|---|
| 1530 | }
|
|---|
| 1531 |
|
|---|
| 1532 | void ConnectionEdit::updateLines()
|
|---|
| 1533 | {
|
|---|
| 1534 | foreach (Connection *con, m_con_list)
|
|---|
| 1535 | con->checkWidgets();
|
|---|
| 1536 | }
|
|---|
| 1537 |
|
|---|
| 1538 | void ConnectionEdit::resizeEvent(QResizeEvent *e)
|
|---|
| 1539 | {
|
|---|
| 1540 | updateBackground();
|
|---|
| 1541 | QWidget::resizeEvent(e);
|
|---|
| 1542 | }
|
|---|
| 1543 |
|
|---|
| 1544 | void 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 |
|
|---|
| 1558 | void 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 |
|
|---|
| 1572 | Connection *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 |
|
|---|
| 1580 | void ConnectionEdit::clearNewlyAddedConnection()
|
|---|
| 1581 | {
|
|---|
| 1582 | delete m_tmp_con;
|
|---|
| 1583 | m_tmp_con = 0;
|
|---|
| 1584 | }
|
|---|
| 1585 |
|
|---|
| 1586 | void 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 |
|
|---|
| 1603 | void ConnectionEdit::contextMenuEvent(QContextMenuEvent * event)
|
|---|
| 1604 | {
|
|---|
| 1605 | QMenu menu;
|
|---|
| 1606 | createContextMenu(menu);
|
|---|
| 1607 | menu.exec(event->globalPos());
|
|---|
| 1608 | }
|
|---|
| 1609 |
|
|---|
| 1610 | } // namespace qdesigner_internal
|
|---|
| 1611 |
|
|---|
| 1612 | QT_END_NAMESPACE
|
|---|