From 974b7adc16b798eda66e1fff1442b73b748f12f9 Mon Sep 17 00:00:00 2001 From: Marcus Eggenberger Date: Tue, 23 Oct 2007 19:29:01 +0000 Subject: [PATCH] We finally got a nice solution to synchronize multiple views or whatevers[tm] even though those views are connected to proxymodels and therefore cannot share SelectionModels. Usage: Client::bufferModel()->selectionModelSynchronizer()->addSelectionModel(mappedSelectionModel) (maybe I'll make this a bit easier... ;)) --- src/client/buffertreemodel.cpp | 20 ++-- src/client/buffertreemodel.h | 19 +++- src/client/client.pri | 4 +- src/client/mappedselectionmodel.cpp | 129 ++++++++++++++++++++++ src/client/mappedselectionmodel.h | 67 +++++++++++ src/client/selectionmodelsynchronizer.cpp | 67 +++++++++++ src/client/selectionmodelsynchronizer.h | 54 +++++++++ src/qtui/bufferview.cpp | 14 +-- src/qtui/bufferview.h | 4 - src/qtui/bufferviewfilter.cpp | 17 --- src/qtui/bufferviewfilter.h | 9 -- src/qtui/mainwin.cpp | 10 ++ 12 files changed, 355 insertions(+), 59 deletions(-) create mode 100644 src/client/mappedselectionmodel.cpp create mode 100644 src/client/mappedselectionmodel.h create mode 100644 src/client/selectionmodelsynchronizer.cpp create mode 100644 src/client/selectionmodelsynchronizer.h diff --git a/src/client/buffertreemodel.cpp b/src/client/buffertreemodel.cpp index 633731b5..7117b07b 100644 --- a/src/client/buffertreemodel.cpp +++ b/src/client/buffertreemodel.cpp @@ -21,6 +21,7 @@ #include // FIXME Dependency on QtGui! #include "buffertreemodel.h" +#include "selectionmodelsynchronizer.h" #include "bufferinfo.h" #include "client.h" @@ -117,9 +118,11 @@ Qt::ItemFlags NetworkTreeItem::flags() const { * BufferTreeModel *****************************************/ BufferTreeModel::BufferTreeModel(QObject *parent) - : TreeModel(BufferTreeModel::defaultHeader(), parent) + : TreeModel(BufferTreeModel::defaultHeader(), parent), + _selectionModelSynchronizer(new SelectionModelSynchronizer(this)) { - Client::signalProxy()->attachSignal(this, SIGNAL(fakeUserInput(BufferInfo, QString)), SIGNAL(sendInput(BufferInfo, QString))); + connect(_selectionModelSynchronizer, SIGNAL(setCurrentIndex(QModelIndex, QItemSelectionModel::SelectionFlags)), + this, SLOT(setCurrentIndex(QModelIndex, QItemSelectionModel::SelectionFlags))); } QListBufferTreeModel::defaultHeader() { @@ -229,12 +232,13 @@ void BufferTreeModel::bufferUpdated(Buffer *buffer) { } // This Slot indicates that the user has selected a different buffer in the gui -void BufferTreeModel::changeCurrent(const QModelIndex ¤t, const QModelIndex &/*previous*/) { - if(isBufferIndex(current)) { - currentBuffer = getBufferByIndex(current); +void BufferTreeModel::setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) { + Q_UNUSED(command) + if(isBufferIndex(index)) { + currentBuffer = getBufferByIndex(index); bufferActivity(Buffer::NoActivity, currentBuffer); emit bufferSelected(currentBuffer); - emit selectionChanged(current); + emit selectionChanged(index); } } @@ -249,6 +253,6 @@ void BufferTreeModel::bufferActivity(Buffer::ActivityLevel level, Buffer *buffer void BufferTreeModel::selectBuffer(Buffer *buffer) { QModelIndex index = getOrCreateBufferItemIndex(buffer); - //emit selectionChanged(index); - changeCurrent(index, QModelIndex()); + // SUPER UGLY! + setCurrentIndex(index, 0); } diff --git a/src/client/buffertreemodel.h b/src/client/buffertreemodel.h index 602cf57d..f300d986 100644 --- a/src/client/buffertreemodel.h +++ b/src/client/buffertreemodel.h @@ -24,9 +24,14 @@ #include #include "treemodel.h" -class BufferInfo; #include "buffer.h" +#include + +class BufferInfo; +class SelectionModelSynchronizer; + + /***************************************** * Fancy Buffer Items *****************************************/ @@ -84,17 +89,18 @@ public: BufferTreeModel(QObject *parent = 0); static QList defaultHeader(); - + + inline SelectionModelSynchronizer *selectionModelSynchronizer() { return _selectionModelSynchronizer; } + public slots: - void bufferUpdated(Buffer *); - void changeCurrent(const QModelIndex &, const QModelIndex &); + void bufferUpdated(Buffer *); + void setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command); void selectBuffer(Buffer *buffer); void bufferActivity(Buffer::ActivityLevel, Buffer *buffer); signals: void bufferSelected(Buffer *); void invalidateFilter(); - void fakeUserInput(BufferInfo, QString); void selectionChanged(const QModelIndex &); private: @@ -106,7 +112,8 @@ private: QStringList mimeTypes() const; QMimeData *mimeData(const QModelIndexList &) const; bool dropMimeData(const QMimeData *, Qt::DropAction, int, int, const QModelIndex &); - + + SelectionModelSynchronizer *_selectionModelSynchronizer; Buffer *currentBuffer; }; diff --git a/src/client/client.pri b/src/client/client.pri index 3e694ea1..56442f8c 100644 --- a/src/client/client.pri +++ b/src/client/client.pri @@ -1,4 +1,4 @@ DEPMOD = common contrib/qxt QT_MOD = core network gui # gui is needed just for QColor... FIXME! -SRCS += buffer.cpp buffertreemodel.cpp client.cpp clientsettings.cpp treemodel.cpp -HDRS += buffer.h buffertreemodel.h client.h clientsettings.h quasselui.h treemodel.h +SRCS += buffer.cpp buffertreemodel.cpp client.cpp clientsettings.cpp treemodel.cpp mappedselectionmodel.cpp selectionmodelsynchronizer.cpp +HDRS += buffer.h buffertreemodel.h client.h clientsettings.h quasselui.h treemodel.h mappedselectionmodel.h selectionmodelsynchronizer.h diff --git a/src/client/mappedselectionmodel.cpp b/src/client/mappedselectionmodel.cpp new file mode 100644 index 00000000..e304aa6b --- /dev/null +++ b/src/client/mappedselectionmodel.cpp @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (C) 2005-07 by The Quassel Team * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappedselectionmodel.h" + +#include +#include +#include +#include + +MappedSelectionModel::MappedSelectionModel(QAbstractItemModel *model) + : QItemSelectionModel(model) +{ + _isProxyModel = (bool)proxyModel(); + connect(this, SIGNAL(currentChanged(QModelIndex, QModelIndex)), + this, SLOT(_currentChanged(QModelIndex, QModelIndex))); + connect(this, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + this, SLOT(_selectionChanged(QItemSelection, QItemSelection))); +} + +MappedSelectionModel::~MappedSelectionModel() { +} + +const QAbstractItemModel *MappedSelectionModel::baseModel() const { + if(isProxyModel()) + return proxyModel()->sourceModel(); + else + return model(); +} + +const QAbstractProxyModel *MappedSelectionModel::proxyModel() const { + return qobject_cast(model()); +} + +QModelIndex MappedSelectionModel::mapFromSource(const QModelIndex &sourceIndex) { + if(isProxyModel()) + return proxyModel()->mapFromSource(sourceIndex); + else + return sourceIndex; +} + +QItemSelection MappedSelectionModel::mapFromSource(const QItemSelection &sourceSelection) { + if(isProxyModel()) { + QItemSelection mappedSelection; + foreach(QItemSelectionRange range, sourceSelection) { + QModelIndex topleft = mapFromSource(range.topLeft()); + QModelIndex bottomright = mapFromSource(range.bottomRight()); + if(topleft.isValid() && bottomright.isValid()) + mappedSelection << QItemSelectionRange(topleft, bottomright); + else + Q_ASSERT(!topleft.isValid() && !bottomright.isValid()); + } + return mappedSelection; + } else { + return sourceSelection; + } +} + +QModelIndex MappedSelectionModel::mapToSource(const QModelIndex &proxyIndex) { + if(isProxyModel()) + return proxyModel()->mapToSource(proxyIndex); + else + return proxyIndex; +} + +QItemSelection MappedSelectionModel::mapToSource(const QItemSelection &proxySelection) { + if(isProxyModel()) { + QItemSelection mappedSelection; + foreach(QItemSelectionRange range, proxySelection) { + mappedSelection << QItemSelectionRange(mapToSource(range.topLeft()), mapToSource(range.bottomRight())); + } + return mappedSelection; + } else { + return proxySelection; + } +} + +void MappedSelectionModel::mappedSelect(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) { + QModelIndex mappedIndex = mapFromSource(index); + if(!isSelected(mappedIndex)) + select(mappedIndex, command); +} + +void MappedSelectionModel::mappedSelect(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) { + QItemSelection mappedSelection = mapFromSource(selection); + if(mappedSelection != QItemSelectionModel::selection()) + select(mappedSelection, command); +} + +void MappedSelectionModel::mappedSetCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) { + QModelIndex mappedIndex = mapFromSource(index); + if(mappedIndex == currentIndex()) + return; + + if(mappedIndex.isValid()) + setCurrentIndex(mappedIndex, command); + else if(hasSelection()) + setCurrentIndex(currentIndex(), QItemSelectionModel::Clear); +} + + +void MappedSelectionModel::_currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { + Q_UNUSED(previous) + emit mappedCurrentChanged(mapToSource(current)); +} + +void MappedSelectionModel::_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { + Q_UNUSED(selected) + Q_UNUSED(deselected) + emit mappedSelectionChanged(mapToSource(QItemSelectionModel::selection())); +} + diff --git a/src/client/mappedselectionmodel.h b/src/client/mappedselectionmodel.h new file mode 100644 index 00000000..226f81ed --- /dev/null +++ b/src/client/mappedselectionmodel.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2005-07 by The Quassel Team * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef _MAPPEDSELECTIONMODEL_H_ +#define _MAPPEDSELECTIONMODEL_H_ + +#include +#include +#include +#include ; + +class QAbstractProxyModel; + +class MappedSelectionModel : public QItemSelectionModel { + Q_OBJECT + +public: + MappedSelectionModel(QAbstractItemModel *model = 0); + virtual ~MappedSelectionModel(); + + inline bool isProxyModel() const { return _isProxyModel; } + + const QAbstractItemModel *baseModel() const; + const QAbstractProxyModel *proxyModel() const; + + QModelIndex mapFromSource(const QModelIndex &sourceIndex); + QItemSelection mapFromSource(const QItemSelection &sourceSelection); + + QModelIndex mapToSource(const QModelIndex &proxyIndex); + QItemSelection mapToSource(const QItemSelection &proxySelection); + +public slots: + void mappedSelect(const QModelIndex &index, QItemSelectionModel::SelectionFlags command); + void mappedSelect(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command); + void mappedSetCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command); + +private slots: + void _currentChanged(const QModelIndex ¤t, const QModelIndex &previous); + void _selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + +signals: + void mappedCurrentChanged(const QModelIndex ¤t); + void mappedSelectionChanged(const QItemSelection &selected); + +private: + bool _isProxyModel; + +}; + +#endif diff --git a/src/client/selectionmodelsynchronizer.cpp b/src/client/selectionmodelsynchronizer.cpp new file mode 100644 index 00000000..2975f797 --- /dev/null +++ b/src/client/selectionmodelsynchronizer.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2005-07 by The Quassel Team * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "selectionmodelsynchronizer.h" + + +#include +#include "mappedselectionmodel.h" + +#include + +SelectionModelSynchronizer::SelectionModelSynchronizer(QAbstractItemModel *parent) + : QObject(parent), + _model(parent) +{ +} + +SelectionModelSynchronizer::~SelectionModelSynchronizer() { +} + +void SelectionModelSynchronizer::addSelectionModel(MappedSelectionModel *selectionmodel) { + if(selectionmodel->baseModel() != model()) { + qWarning() << "cannot Syncronize SelectionModel" << selectionmodel << "which has a different baseModel()"; + return; + } + + connect(selectionmodel, SIGNAL(mappedCurrentChanged(QModelIndex)), + this, SLOT(_mappedCurrentChanged(QModelIndex))); + connect(selectionmodel, SIGNAL(mappedSelectionChanged(QItemSelection)), + this, SLOT(_mappedSelectionChanged(QItemSelection))); + + connect(this, SIGNAL(setCurrentIndex(QModelIndex, QItemSelectionModel::SelectionFlags)), + selectionmodel, SLOT(mappedSetCurrentIndex(QModelIndex, QItemSelectionModel::SelectionFlags))); + connect(this, SIGNAL(select(QItemSelection, QItemSelectionModel::SelectionFlags)), + selectionmodel, SLOT(mappedSelect(QItemSelection, QItemSelectionModel::SelectionFlags))); + +} + +void SelectionModelSynchronizer::removeSelectionModel(MappedSelectionModel *model) { + disconnect(model, 0, this, 0); + disconnect(this, 0, model, 0); +} + +void SelectionModelSynchronizer::_mappedCurrentChanged(const QModelIndex ¤t) { + emit setCurrentIndex(current, QItemSelectionModel::ClearAndSelect); +} + +void SelectionModelSynchronizer::_mappedSelectionChanged(const QItemSelection &selected) { + emit select(selected, QItemSelectionModel::ClearAndSelect); +} diff --git a/src/client/selectionmodelsynchronizer.h b/src/client/selectionmodelsynchronizer.h new file mode 100644 index 00000000..cdf24155 --- /dev/null +++ b/src/client/selectionmodelsynchronizer.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2005-07 by The Quassel Team * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef _SELECTIONMODELSYNCHRONIZER_H_ +#define _SELECTIONMODELSYNCHRONIZER_H_ + +#include +#include + +class QAbstractItemModel; +class MappedSelectionModel; + +class SelectionModelSynchronizer : public QObject { + Q_OBJECT + +public: + SelectionModelSynchronizer(QAbstractItemModel *parent = 0); + virtual ~SelectionModelSynchronizer(); + + void addSelectionModel(MappedSelectionModel *model); + void removeSelectionModel(MappedSelectionModel *model); + + inline QAbstractItemModel *model() { return _model; } + +private slots: + void _mappedCurrentChanged(const QModelIndex ¤t); + void _mappedSelectionChanged(const QItemSelection &selected); + +signals: + void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command); + void setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command); + +private: + QAbstractItemModel *_model; +}; + +#endif diff --git a/src/qtui/bufferview.cpp b/src/qtui/bufferview.cpp index c60aa80f..40995ad7 100644 --- a/src/qtui/bufferview.cpp +++ b/src/qtui/bufferview.cpp @@ -45,15 +45,6 @@ void BufferView::init() { setSortingEnabled(true); sortByColumn(0, Qt::AscendingOrder); - connect(selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), - model(), SLOT(changeCurrent(const QModelIndex &, const QModelIndex &))); - - connect(model(), SIGNAL(selectionChanged(const QModelIndex &)), - this, SLOT(select(const QModelIndex &))); - - connect(this, SIGNAL(selectionChanged(const QModelIndex &, QItemSelectionModel::SelectionFlags)), - selectionModel(), SLOT(select(const QModelIndex &, QItemSelectionModel::SelectionFlags))); - connect(this, SIGNAL(activated(QModelIndex)), this, SLOT(joinChannel(QModelIndex))); } @@ -67,10 +58,7 @@ void BufferView::setFilteredModel(QAbstractItemModel *model, BufferViewFilter::M void BufferView::setModel(QAbstractItemModel *model) { QTreeView::setModel(model); init(); -} - -void BufferView::select(const QModelIndex ¤t) { - emit selectionChanged(current, QItemSelectionModel::ClearAndSelect); + } void BufferView::dropEvent(QDropEvent *event) { diff --git a/src/qtui/bufferview.h b/src/qtui/bufferview.h index 0c06cafa..8240f8de 100644 --- a/src/qtui/bufferview.h +++ b/src/qtui/bufferview.h @@ -38,13 +38,9 @@ public: void setModel(QAbstractItemModel *model); void setFilteredModel(QAbstractItemModel *model, BufferViewFilter::Modes mode, QStringList nets); -public slots: - void select(const QModelIndex &); - signals: void eventDropped(QDropEvent *); void removeBuffer(const QModelIndex &); - void selectionChanged(const QModelIndex &, QItemSelectionModel::SelectionFlags); private slots: void dropEvent(QDropEvent *); diff --git a/src/qtui/bufferviewfilter.cpp b/src/qtui/bufferviewfilter.cpp index 4fe8fedc..6bcfb6ad 100644 --- a/src/qtui/bufferviewfilter.cpp +++ b/src/qtui/bufferviewfilter.cpp @@ -32,29 +32,12 @@ BufferViewFilter::BufferViewFilter(QAbstractItemModel *model, const Modes &filte networks = nets; connect(model, SIGNAL(invalidateFilter()), this, SLOT(invalidateMe())); - connect(model, SIGNAL(selectionChanged(const QModelIndex &)), - this, SLOT(select(const QModelIndex &))); - - connect(this, SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), - model, SLOT(changeCurrent(const QModelIndex &, const QModelIndex &))); } void BufferViewFilter::invalidateMe() { invalidateFilter(); } -void BufferViewFilter::select(const QModelIndex &index) { - emit selectionChanged(mapFromSource(index)); -} - -void BufferViewFilter::changeCurrent(const QModelIndex ¤t, const QModelIndex &previous) { - emit currentChanged(mapToSource(current), mapToSource(previous)); -} - -void BufferViewFilter::doubleClickReceived(const QModelIndex &clicked) { - emit doubleClicked(mapToSource(clicked)); -} - void BufferViewFilter::dropEvent(QDropEvent *event) { const QMimeData *data = event->mimeData(); if(!(mode & FullCustom)) diff --git a/src/qtui/bufferviewfilter.h b/src/qtui/bufferviewfilter.h index 93478c75..edda7ac8 100644 --- a/src/qtui/bufferviewfilter.h +++ b/src/qtui/bufferviewfilter.h @@ -50,18 +50,9 @@ public: public slots: void invalidateMe(); - void changeCurrent(const QModelIndex &, const QModelIndex &); - void doubleClickReceived(const QModelIndex &); - void select(const QModelIndex &); void dropEvent(QDropEvent *); void removeBuffer(const QModelIndex &); - -signals: - void currentChanged(const QModelIndex &, const QModelIndex &); - void doubleClicked(const QModelIndex &); - void selectionChanged(const QModelIndex &); - private: bool filterAcceptBuffer(const QModelIndex &) const; bool filterAcceptNetwork(const QModelIndex &) const; diff --git a/src/qtui/mainwin.cpp b/src/qtui/mainwin.cpp index f94ee4a0..ee253097 100644 --- a/src/qtui/mainwin.cpp +++ b/src/qtui/mainwin.cpp @@ -31,6 +31,9 @@ #include "topicwidget.h" +#include "selectionmodelsynchronizer.h" +#include "mappedselectionmodel.h" + MainWin::MainWin(QtUi *_gui, QWidget *parent) : QMainWindow(parent), gui(_gui) { ui.setupUi(this); setWindowTitle("Quassel IRC"); @@ -156,6 +159,13 @@ void MainWin::addBufferView(const QString &viewname, QAbstractItemModel *model, //create the view and initialize it's filter BufferView *view = new BufferView(dock); view->setFilteredModel(model, mode, nets); + + MappedSelectionModel *mappedSelectionModel = new MappedSelectionModel(view->model()); + Client::bufferModel()->selectionModelSynchronizer()->addSelectionModel(mappedSelectionModel); + Q_ASSERT(mappedSelectionModel); + delete view->selectionModel(); + view->setSelectionModel(mappedSelectionModel); + dock->setWidget(view); addDockWidget(Qt::LeftDockWidgetArea, dock); -- 2.20.1