tools/designer/src/lib/shared/connectionedit.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    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