From 07ff24d8b66d4f32a395370afb5a19c80296a3ac Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@qt.io>
Date: Fri, 7 Dec 2018 14:38:06 +0100
Subject: [PATCH 2/2] Qt Designer: Fix selections of signal slot editor and
 signal slot list getting out of sync

The signal slot editor had a synchronization logic keeping the list
and editor selections in sync. Extract a SelectionModel interface from
the ConnectionEdit base class which allows for using a
QItemSelectionModel-based implementation, removing the need to sync.

The signal slot list is set to extended selection for this to work
consistently.

Change-Id: Ied1ce875269786eed29e8307482137b76049bf21
Fixes: QTBUG-72342
---
 .../components/buddyeditor/buddyeditor.cpp    |   2 +-
 .../buddyeditor/buddyeditor_tool.cpp          |   1 +
 .../signalsloteditor/signalsloteditor_p.h     |  32 ++++
 .../signalsloteditorwindow.cpp                | 138 +++++++++++-------
 .../signalsloteditor/signalsloteditorwindow.h |   6 +-
 .../src/lib/shared/connectionedit.cpp         | 118 +++++++++++----
 .../src/lib/shared/connectionedit_p.h         |  47 +++++-
 7 files changed, 257 insertions(+), 87 deletions(-)

diff --git a/src/designer/src/components/buddyeditor/buddyeditor.cpp b/src/designer/src/components/buddyeditor/buddyeditor.cpp
index 7593326d..56aa4314 100644
--- a/src/designer/src/components/buddyeditor/buddyeditor.cpp
+++ b/src/designer/src/components/buddyeditor/buddyeditor.cpp
@@ -304,7 +304,7 @@ void BuddyEditor::widgetRemoved(QWidget *widget)
 
 void BuddyEditor::deleteSelected()
 {
-    const ConnectionSet selectedConnections = selection(); // want copy for unselect
+    const auto selectedConnections = selection(); // want copy for unselect
     if (selectedConnections.isEmpty())
         return;
 
diff --git a/src/designer/src/components/buddyeditor/buddyeditor_tool.cpp b/src/designer/src/components/buddyeditor/buddyeditor_tool.cpp
index 51b6e498..19d23521 100644
--- a/src/designer/src/components/buddyeditor/buddyeditor_tool.cpp
+++ b/src/designer/src/components/buddyeditor/buddyeditor_tool.cpp
@@ -70,6 +70,7 @@ QWidget *BuddyEditorTool::editor() const
     if (!m_editor) {
         Q_ASSERT(formWindow() != nullptr);
         m_editor = new BuddyEditor(formWindow(), nullptr);
+        m_editor->setSelectionModel(new ConnectionSelectionModel(m_editor));
         connect(formWindow(), &QDesignerFormWindowInterface::mainContainerChanged,
                 m_editor.data(), &BuddyEditor::setBackground);
         connect(formWindow(), &QDesignerFormWindowInterface::changed,
diff --git a/src/designer/src/components/signalsloteditor/signalsloteditor_p.h b/src/designer/src/components/signalsloteditor/signalsloteditor_p.h
index 15d0563c..46331a95 100644
--- a/src/designer/src/components/signalsloteditor/signalsloteditor_p.h
+++ b/src/designer/src/components/signalsloteditor/signalsloteditor_p.h
@@ -50,6 +50,10 @@
 
 QT_BEGIN_NAMESPACE
 
+class QAbstractProxyModel;
+class QItemSelection;
+class QItemSelectionModel;
+
 class QDesignerFormWindowInterface;
 class QDesignerFormEditorInterface;
 class DomConnection;
@@ -116,6 +120,34 @@ private:
     QPointer<SignalSlotEditor> m_editor;
 };
 
+class ItemViewConnectionSelectionModel : public AbstractConnectionSelectionModel
+{
+    Q_OBJECT
+public:
+    explicit ItemViewConnectionSelectionModel(ConnectionModel *m,
+                                              QAbstractProxyModel *proxyModel,
+                                              QItemSelectionModel *selectionModel,
+                                              QObject *parent = nullptr);
+
+    void clear() override;
+    bool hasSelection() const override;
+    bool isSelected(const Connection *c) const override;
+    void setSelected(Connection *c, bool sel) override;
+    ConnectionList selection() const override;
+
+private slots:
+    void slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
+
+private:
+    void emitSelectionChanged(const QModelIndexList &range, bool v);
+    QModelIndex connectionToIndex(Connection *con) const;
+    Connection *indexToConnection(const QModelIndex &index) const;
+
+    ConnectionModel *m_model;
+    QAbstractProxyModel *m_proxyModel;
+    QItemSelectionModel *m_selectionModel;
+};
+
 } // namespace qdesigner_internal
 
 QT_END_NAMESPACE
diff --git a/src/designer/src/components/signalsloteditor/signalsloteditorwindow.cpp b/src/designer/src/components/signalsloteditor/signalsloteditorwindow.cpp
index f44c3cd5..a35c60fb 100644
--- a/src/designer/src/components/signalsloteditor/signalsloteditorwindow.cpp
+++ b/src/designer/src/components/signalsloteditor/signalsloteditorwindow.cpp
@@ -393,8 +393,88 @@ void ConnectionModel::updateAll()
 {
     emit dataChanged(index(0, 0), index(rowCount(), columnCount()));
 }
+
+// ------------  ItemViewConnectionSelectionModel:
+// Implements AbstractConnectionSelectionModel wrapping the QItemSelectionModel of the window.
+ItemViewConnectionSelectionModel::ItemViewConnectionSelectionModel(ConnectionModel *m,
+                                                                   QAbstractProxyModel *proxyModel,
+                                                                   QItemSelectionModel *selectionModel,
+                                                                   QObject *parent) :
+      AbstractConnectionSelectionModel(parent),
+      m_model(m), m_proxyModel(proxyModel),
+      m_selectionModel(selectionModel)
+{
+    Q_ASSERT(selectionModel);
+    connect(m_selectionModel, &QItemSelectionModel::selectionChanged,
+            this, &ItemViewConnectionSelectionModel::slotSelectionChanged);
+}
+
+void ItemViewConnectionSelectionModel::clear()
+{
+    m_selectionModel->clear();
+}
+
+bool ItemViewConnectionSelectionModel::hasSelection() const
+{
+    return m_selectionModel->hasSelection();
+}
+
+bool ItemViewConnectionSelectionModel::isSelected(const Connection *c) const
+{
+    return c && m_selectionModel->isSelected(connectionToIndex(const_cast<Connection *>(c)));
+}
+
+void ItemViewConnectionSelectionModel::setSelected(Connection *con, bool sel)
+{
+    if (!con)
+        return;
+    const auto index = connectionToIndex(con);
+    if (m_selectionModel->isSelected(index) == sel)
+        return;
+
+    QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::Rows;
+    if (sel && !m_selectionModel->hasSelection())
+        flags |= QItemSelectionModel::Current;
+    flags |= sel ? QItemSelectionModel::Select : QItemSelectionModel::Deselect;
+    m_selectionModel->select(index, flags);
+}
+
+AbstractConnectionSelectionModel::ConnectionList ItemViewConnectionSelectionModel::selection() const
+{
+    ConnectionList result;
+    const auto rows = m_selectionModel->selectedRows();
+    for (const auto &row : rows)
+        result.append(indexToConnection(row));
+    return result;
+}
+
+void ItemViewConnectionSelectionModel::emitSelectionChanged(const QModelIndexList &range, bool v)
+{
+    for (const auto &m : range) {
+        if (m.column() == 0) // First column only
+            emit selectionChanged(indexToConnection(m), v);
+    }
+}
+
+void ItemViewConnectionSelectionModel::slotSelectionChanged(const QItemSelection &selected,
+                                                            const QItemSelection &deselected)
+{
+    emitSelectionChanged(deselected.indexes(), false);
+    emitSelectionChanged(selected.indexes(), true);
 }
 
+QModelIndex ItemViewConnectionSelectionModel::connectionToIndex(Connection *con) const
+{
+    return m_proxyModel->mapFromSource(m_model->connectionToIndex(con));
+}
+
+Connection *ItemViewConnectionSelectionModel::indexToConnection(const QModelIndex &index) const
+{
+    return m_model->indexToConnection(m_proxyModel->mapToSource(index));
+}
+
+} // namespace qdesigner_internal
+
 namespace {
 // ---------------------- InlineEditorModel
 
@@ -684,19 +764,21 @@ SignalSlotEditorWindow::SignalSlotEditorWindow(QDesignerFormEditorInterface *cor
     m_remove_button(new QToolButton),
     m_core(core),
     m_model(new ConnectionModel(this)),
-    m_proxy_model(new QSortFilterProxyModel(this)),
-    m_handling_selection_change(false)
+    m_proxy_model(new QSortFilterProxyModel(this))
 {
     m_proxy_model->setSourceModel(m_model);
     m_view->setModel(m_proxy_model);
+    m_view->setSelectionMode(QAbstractItemView::ExtendedSelection);
+    m_selectionModel = new ItemViewConnectionSelectionModel(m_model, m_proxy_model,
+                                                            m_view->selectionModel());
+    connect(m_selectionModel, &AbstractConnectionSelectionModel::selectionChanged,
+            this, &SignalSlotEditorWindow::updateUi);
     m_view->setSortingEnabled(true);
     m_view->setItemDelegate(new ConnectionDelegate(this));
     m_view->setEditTriggers(QAbstractItemView::DoubleClicked
                                 | QAbstractItemView::EditKeyPressed);
     m_view->setRootIsDecorated(false);
     m_view->setTextElideMode (Qt::ElideMiddle);
-    connect(m_view->selectionModel(), &QItemSelectionModel::currentChanged,
-            this, &SignalSlotEditorWindow::updateUi);
     connect(m_view->header(), &QHeaderView::sectionDoubleClicked,
             m_view, &QTreeView::resizeColumnToContents);
 
@@ -729,11 +811,7 @@ void SignalSlotEditorWindow::setActiveFormWindow(QDesignerFormWindowInterface *f
     QDesignerIntegrationInterface *integration = m_core->integration();
 
     if (!m_editor.isNull()) {
-        disconnect(m_view->selectionModel(),
-                    &QItemSelectionModel::currentChanged,
-                    this, &SignalSlotEditorWindow::updateEditorSelection);
-        disconnect(m_editor.data(), &SignalSlotEditor::connectionSelected,
-                   this, &SignalSlotEditorWindow::updateDialogSelection);
+        m_editor->setSelectionModel(nullptr);
         if (integration) {
             disconnect(integration, &QDesignerIntegrationInterface::objectNameChanged,
                        this, &SignalSlotEditorWindow::objectNameChanged);
@@ -743,16 +821,12 @@ void SignalSlotEditorWindow::setActiveFormWindow(QDesignerFormWindowInterface *f
     m_editor = form->findChild<SignalSlotEditor*>();
     m_model->setEditor(m_editor);
     if (!m_editor.isNull()) {
+        m_editor->setSelectionModel(m_selectionModel);
         ConnectionDelegate *delegate
             = qobject_cast<ConnectionDelegate*>(m_view->itemDelegate());
         if (delegate != nullptr)
             delegate->setForm(form);
 
-        connect(m_view->selectionModel(),
-                &QItemSelectionModel::currentChanged,
-                this, &SignalSlotEditorWindow::updateEditorSelection);
-        connect(m_editor.data(), &SignalSlotEditor::connectionSelected,
-                this, &SignalSlotEditorWindow::updateDialogSelection);
         if (integration) {
             connect(integration, &QDesignerIntegrationInterface::objectNameChanged,
                     this, &SignalSlotEditorWindow::objectNameChanged);
@@ -762,40 +836,6 @@ void SignalSlotEditorWindow::setActiveFormWindow(QDesignerFormWindowInterface *f
     updateUi();
 }
 
-void SignalSlotEditorWindow::updateDialogSelection(Connection *con)
-{
-    if (m_handling_selection_change || m_editor == nullptr)
-        return;
-
-    QModelIndex index = m_proxy_model->mapFromSource(m_model->connectionToIndex(con));
-    if (index == m_view->currentIndex())
-        return;
-    m_handling_selection_change = true;
-    m_view->setCurrentIndex(index);
-    m_handling_selection_change = false;
-
-    updateUi();
-}
-
-void SignalSlotEditorWindow::updateEditorSelection(const QModelIndex &index)
-{
-    if (m_handling_selection_change || m_editor == nullptr)
-        return;
-
-    if (m_editor == nullptr)
-        return;
-
-    Connection *con = m_model->indexToConnection(m_proxy_model->mapToSource(index));
-    if (m_editor->selected(con))
-        return;
-    m_handling_selection_change = true;
-    m_editor->selectNone();
-    m_editor->setSelected(con, true);
-    m_handling_selection_change = false;
-
-    updateUi();
-}
-
 void SignalSlotEditorWindow::objectNameChanged(QDesignerFormWindowInterface *, QObject *, const QString &, const QString &)
 {
     if (m_editor)
@@ -823,7 +863,7 @@ void SignalSlotEditorWindow::removeConnection()
 void SignalSlotEditorWindow::updateUi()
 {
     m_add_button->setEnabled(!m_editor.isNull());
-    m_remove_button->setEnabled(!m_editor.isNull() && m_view->currentIndex().isValid());
+    m_remove_button->setEnabled(!m_editor.isNull() && m_selectionModel->hasSelection());
 }
 
 } // namespace qdesigner_internal
diff --git a/src/designer/src/components/signalsloteditor/signalsloteditorwindow.h b/src/designer/src/components/signalsloteditor/signalsloteditorwindow.h
index 8a51a8ca..c2f6dc3e 100644
--- a/src/designer/src/components/signalsloteditor/signalsloteditorwindow.h
+++ b/src/designer/src/components/signalsloteditor/signalsloteditorwindow.h
@@ -46,6 +46,7 @@ namespace qdesigner_internal {
 class SignalSlotEditor;
 class ConnectionModel;
 class Connection;
+class ItemViewConnectionSelectionModel;
 
 class SignalSlotEditorWindow : public QWidget
 {
@@ -57,9 +58,6 @@ public slots:
     void setActiveFormWindow(QDesignerFormWindowInterface *form);
 
 private slots:
-    void updateDialogSelection(Connection *con);
-    void updateEditorSelection(const QModelIndex &index);
-
     void objectNameChanged(QDesignerFormWindowInterface *formWindow, QObject *object, const QString &newName, const QString &oldName);
 
     void addConnection();
@@ -73,7 +71,7 @@ private:
     QDesignerFormEditorInterface *m_core;
     ConnectionModel *m_model;
     QSortFilterProxyModel *m_proxy_model;
-    bool m_handling_selection_change;
+    ItemViewConnectionSelectionModel *m_selectionModel = nullptr;
 };
 
 } // namespace qdesigner_internal
diff --git a/src/designer/src/lib/shared/connectionedit.cpp b/src/designer/src/lib/shared/connectionedit.cpp
index 6d7749e8..6382c27b 100644
--- a/src/designer/src/lib/shared/connectionedit.cpp
+++ b/src/designer/src/lib/shared/connectionedit.cpp
@@ -920,6 +920,49 @@ void Connection::checkWidgets()
     }
 }
 
+// AbstractConnectionSelectionModel: Interface for the selection in ConnectionEdit
+// which allows to select/deselect connections and emits signals accordingly
+// for updating the connections (red/blue). For the use in the buddy editor,
+// ConnectionSelectionModel is a simple, list-based implementation. For use
+// in the signal-slot editor, which in addition has a an item view to keep in sync,
+// an implementation wrapping the item view's QItemSelectionModel is used.
+
+AbstractConnectionSelectionModel::AbstractConnectionSelectionModel(QObject *parent) :
+    QObject(parent)
+{
+}
+
+// Simple, list-based selection model for use in the buddy editor
+ConnectionSelectionModel::ConnectionSelectionModel(QObject *parent) :
+    AbstractConnectionSelectionModel(parent)
+{
+}
+
+void ConnectionSelectionModel::clear()
+{
+    const auto selected = m_selected;
+    m_selected.clear();
+    for (auto c : selected)
+        emit selectionChanged(c, false);
+}
+
+void ConnectionSelectionModel::setSelected(Connection *con, bool sel)
+{
+    if (!con || sel == m_selected.contains(con))
+        return;
+
+    if (sel)
+        m_selected.append(con);
+    else
+        m_selected.removeOne(con);
+    emit selectionChanged(con, sel);
+}
+
+bool ConnectionSelectionModel::isSelected(const Connection *con) const
+{
+     return m_selected.contains(const_cast<Connection *>(con));
+}
+
 /*******************************************************************************
 ** ConnectionEdit
 */
@@ -947,10 +990,24 @@ ConnectionEdit::~ConnectionEdit()
     qDeleteAll(m_con_list);
 }
 
+void ConnectionEdit::setSelectionModel(AbstractConnectionSelectionModel *m)
+{
+    if (m_selectionModel == m)
+        return;
+    if (m_selectionModel)
+        m_selectionModel->disconnect(this);
+    m_selectionModel = m;
+    if (m_selectionModel) {
+        connect(m_selectionModel, &AbstractConnectionSelectionModel::selectionChanged,
+                this, &ConnectionEdit::slotSelectionChanged);
+    }
+}
+
 void ConnectionEdit::clear()
 {
     m_con_list.clear();
-    m_sel_con_set.clear();
+    if (m_selectionModel)
+        m_selectionModel->clear();
     m_bg_widget = nullptr;
     m_widget_under_mouse = nullptr;
     m_tmp_con = nullptr;
@@ -1027,7 +1084,7 @@ void ConnectionEdit::paintLabel(QPainter *p, EndPoint::Type type, Connection *co
     if (con->label(type).isEmpty())
         return;
 
-    const bool heavy = selected(con) || con == m_tmp_con;
+    const bool heavy = isSelected(con) || con == m_tmp_con;
     p->setPen(heavy ? m_active_color : m_inactive_color);
     p->setBrush(Qt::NoBrush);
     const QRect r = con->labelRect(type);
@@ -1042,7 +1099,7 @@ void ConnectionEdit::paintConnection(QPainter *p, Connection *con,
     QWidget *source = con->widget(EndPoint::Source);
     QWidget *target = con->widget(EndPoint::Target);
 
-    const bool heavy = selected(con) || con == m_tmp_con;
+    const bool heavy = isSelected(con) || con == m_tmp_con;
     WidgetSet *set = heavy ? heavy_highlight_set : light_highlight_set;
     p->setPen(heavy ? m_active_color : m_inactive_color);
     con->paint(p);
@@ -1105,7 +1162,7 @@ void ConnectionEdit::paintEvent(QPaintEvent *e)
     p.setBrush(m_active_color);
 
     for (Connection *con : qAsConst(m_con_list)) {
-        if (!selected(con) || !con->isVisible())
+        if (!isSelected(con) || !con->isVisible())
             continue;
 
         paintEndPoint(&p, con->endPointPos(EndPoint::Source));
@@ -1159,7 +1216,7 @@ void ConnectionEdit::mousePressEvent(QMouseEvent *e)
                     startDrag(m_end_point_under_mouse, e->pos());
             } else if (con_under_mouse != nullptr) {
                 if (toggleSelection) {
-                    setSelected(con_under_mouse, !selected(con_under_mouse));
+                    setSelected(con_under_mouse, !isSelected(con_under_mouse));
                 } else {
                     selectNone();
                     setSelected(con_under_mouse, true);
@@ -1190,10 +1247,11 @@ void ConnectionEdit::mouseDoubleClickEvent(QMouseEvent *e)
         case Dragging:
             break;
         case Editing:
+            Q_ASSERT(m_selectionModel);
             if (!m_widget_under_mouse.isNull())
                 emit widgetActivated(m_widget_under_mouse);
-            else if (m_sel_con_set.size() == 1)
-                modifyConnection(m_sel_con_set.constBegin().key());
+            else if (auto current = m_selectionModel->selection().value(0))
+                modifyConnection(current);
             break;
     }
 
@@ -1300,6 +1358,11 @@ void ConnectionEdit::keyPressEvent(QKeyEvent *e)
     e->accept();
 }
 
+void ConnectionEdit::slotSelectionChanged(Connection *con, bool /* selected */)
+{
+    con->update();
+}
+
 void ConnectionEdit::startConnection(QWidget *source, const QPoint &pos)
 {
     Q_ASSERT(m_tmp_con == nullptr);
@@ -1401,35 +1464,30 @@ void ConnectionEdit::objectRemoved(QObject *o)
 
 void ConnectionEdit::setSelected(Connection *con, bool sel)
 {
-    if (!con || sel == m_sel_con_set.contains(con))
-        return;
 
-    if (sel) {
-        m_sel_con_set.insert(con, con);
-        emit connectionSelected(con);
-    } else {
-        m_sel_con_set.remove(con);
-    }
+    if (m_selectionModel)
+        m_selectionModel->setSelected(con, sel);
+}
 
-    con->update();
+bool ConnectionEdit::isSelected(const Connection *con) const
+{
+    return m_selectionModel && m_selectionModel->isSelected(con);
 }
 
-bool ConnectionEdit::selected(const Connection *con) const
+ConnectionEdit::ConnectionList ConnectionEdit::selection() const
 {
-    return m_sel_con_set.contains(const_cast<Connection*>(con));
+    return m_selectionModel ? m_selectionModel->selection() : ConnectionList();
 }
 
 void ConnectionEdit::selectNone()
 {
-    for (Connection *con : qAsConst(m_sel_con_set))
-        con->update();
-
-    m_sel_con_set.clear();
+    if (m_selectionModel)
+        m_selectionModel->clear();
 }
 
 void ConnectionEdit::selectAll()
 {
-    if (m_sel_con_set.size() == m_con_list.size())
+    if (!m_selectionModel || m_selectionModel->selection().size() == m_con_list.size())
         return;
     for (Connection *con : qAsConst(m_con_list))
         setSelected(con, true);
@@ -1447,7 +1505,7 @@ Connection *ConnectionEdit::connectionAt(const QPoint &pos) const
 CETypes::EndPoint ConnectionEdit::endPointAt(const QPoint &pos) const
 {
     for (Connection *con : m_con_list) {
-        if (!selected(con))
+        if (!isSelected(con))
             continue;
         const QRect sr = con->endPointRect(EndPoint::Source);
         const QRect tr = con->endPointRect(EndPoint::Target);
@@ -1497,9 +1555,10 @@ void ConnectionEdit::adjustHotSopt(const EndPoint &end_point, const QPoint &pos)
 
 void ConnectionEdit::deleteSelected()
 {
-    if (m_sel_con_set.isEmpty())
+    const auto selected = selection();
+    if (selected.isEmpty())
         return;
-    m_undo_stack->push(new DeleteConnectionsCommand(this, m_sel_con_set.keys()));
+    m_undo_stack->push(new DeleteConnectionsCommand(this, selected));
 }
 
 void ConnectionEdit::addConnection(Connection *con)
@@ -1563,18 +1622,19 @@ void ConnectionEdit::clearNewlyAddedConnection()
 
 void ConnectionEdit::createContextMenu(QMenu &menu)
 {
+    const auto selected = selection();
     // Select
     QAction *selectAllAction = menu.addAction(tr("Select All"));
-    selectAllAction->setEnabled(!connectionList().isEmpty());
+    selectAllAction->setEnabled(!m_con_list.isEmpty() && m_con_list.size() > selected.size());
     connect(selectAllAction, &QAction::triggered, this, &ConnectionEdit::selectAll);
     QAction *deselectAllAction = menu.addAction(tr("Deselect All"));
-    deselectAllAction->setEnabled(!selection().isEmpty());
+    deselectAllAction->setEnabled(!selected.isEmpty());
     connect(deselectAllAction, &QAction::triggered, this, &ConnectionEdit::selectNone);
     menu.addSeparator();
     // Delete
     QAction *deleteAction = menu.addAction(tr("Delete"));
     deleteAction->setShortcut(QKeySequence::Delete);
-    deleteAction->setEnabled(!selection().isEmpty());
+    deleteAction->setEnabled(!selected.isEmpty());
     connect(deleteAction, &QAction::triggered, this, &ConnectionEdit::deleteSelected);
 }
 
diff --git a/src/designer/src/lib/shared/connectionedit_p.h b/src/designer/src/lib/shared/connectionedit_p.h
index 6fd62345..23dd13ad 100644
--- a/src/designer/src/lib/shared/connectionedit_p.h
+++ b/src/designer/src/lib/shared/connectionedit_p.h
@@ -150,6 +150,41 @@ private:
     QRect groundRect() const;
 };
 
+class QDESIGNER_SHARED_EXPORT AbstractConnectionSelectionModel : public QObject
+{
+    Q_OBJECT
+public:
+    using ConnectionList = QList<Connection *>;
+
+    virtual void clear() = 0;
+    virtual bool hasSelection() const = 0;
+    virtual void setSelected(Connection *con, bool sel) = 0;
+    virtual bool isSelected(const Connection *con) const = 0;
+    virtual ConnectionList selection() const = 0;
+
+signals:
+    void selectionChanged(Connection *con, bool selected);
+
+protected:
+    explicit AbstractConnectionSelectionModel(QObject *parent);
+};
+
+class QDESIGNER_SHARED_EXPORT ConnectionSelectionModel : public AbstractConnectionSelectionModel
+{
+    Q_OBJECT
+public:
+    explicit ConnectionSelectionModel(QObject *parent);
+
+    void clear() override;
+    bool hasSelection() const override { return !m_selected.isEmpty(); }
+    void setSelected(Connection *con, bool sel) override;
+    bool isSelected(const Connection *con) const override;
+    ConnectionList selection() const override { return m_selected; }
+
+private:
+    ConnectionList m_selected;
+};
+
 class QDESIGNER_SHARED_EXPORT ConnectionEdit : public QWidget, public CETypes
 {
     Q_OBJECT
@@ -157,10 +192,13 @@ public:
     ConnectionEdit(QWidget *parent, QDesignerFormWindowInterface *form);
     ~ConnectionEdit() override;
 
+    void setSelectionModel(AbstractConnectionSelectionModel *m);
+
     inline const QPointer<QWidget> &background() const { return m_bg_widget; }
 
     void setSelected(Connection *con, bool sel);
-    bool selected(const Connection *con) const;
+    bool isSelected(const Connection *con) const;
+    ConnectionList selection() const;
 
     int connectionCount() const { return m_con_list.size(); }
     Connection *connection(int i) const { return m_con_list.at(i); }
@@ -183,7 +221,6 @@ signals:
     void connectionAdded(Connection *con);
     void aboutToRemoveConnection(Connection *con);
     void connectionRemoved(int idx);
-    void connectionSelected(Connection *con);
     void widgetActivated(QWidget *wgt);
     void connectionChanged(Connection *con);
 
@@ -223,13 +260,15 @@ protected:
     virtual void endConnection(QWidget *target, const QPoint &pos);
 
     const ConnectionList &connectionList() const { return m_con_list; }
-    const ConnectionSet &selection()  const      { return m_sel_con_set; }
     Connection *takeConnection(Connection *con);
     Connection *newlyAddedConnection()           { return m_tmp_con; }
     void clearNewlyAddedConnection();
 
     void findObjectsUnderMouse(const QPoint &pos);
 
+private:
+    void slotSelectionChanged(Connection *con, bool selected);
+
 private:
     void startConnection(QWidget *source, const QPoint &pos);
     void continueConnection(QWidget *target, const QPoint &pos);
@@ -259,7 +298,7 @@ private:
 
     EndPoint m_drag_end_point;
     QPoint m_old_source_pos, m_old_target_pos;
-    ConnectionSet m_sel_con_set;
+    AbstractConnectionSelectionModel *m_selectionModel = nullptr;
     const QColor m_inactive_color;
     const QColor m_active_color;
 
-- 
2.20.1

