X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fclient%2Fselectionmodelsynchronizer.cpp;h=01d74fe6d49c4fc862a25549ff31de315a948390;hp=a31572918f857bb6e33e2a4e1b1cbcedb4fa7e06;hb=73696998505c35c02bd019f78e9f502cbc36da5b;hpb=d6b056e936ec441258d291b7a8af7b83f9f53016 diff --git a/src/client/selectionmodelsynchronizer.cpp b/src/client/selectionmodelsynchronizer.cpp index a3157291..01d74fe6 100644 --- a/src/client/selectionmodelsynchronizer.cpp +++ b/src/client/selectionmodelsynchronizer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-07 by the Quassel IRC Team * + * Copyright (C) 2005-08 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -22,46 +22,223 @@ #include -#include "mappedselectionmodel.h" +#include #include SelectionModelSynchronizer::SelectionModelSynchronizer(QAbstractItemModel *parent) : QObject(parent), - _model(parent) + _model(parent), + _selectionModel(parent), + _changeCurrentEnabled(true), + _changeSelectionEnabled(true) { + connect(&_selectionModel, SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(currentChanged(const QModelIndex &, const QModelIndex &))); + connect(&_selectionModel, SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + this, SLOT(selectionChanged(const QItemSelection &, const QItemSelection &))); } -SelectionModelSynchronizer::~SelectionModelSynchronizer() { +bool SelectionModelSynchronizer::checkBaseModel(QItemSelectionModel *selectionModel) { + if(!selectionModel) + return false; + + const QAbstractItemModel *baseModel = selectionModel->model(); + const QAbstractProxyModel *proxyModel = 0; + while((proxyModel = qobject_cast(baseModel)) != 0) { + baseModel = proxyModel->sourceModel(); + if(baseModel == model()) + break; + } + return baseModel == model(); } -void SelectionModelSynchronizer::addSelectionModel(MappedSelectionModel *selectionmodel) { - if(selectionmodel->baseModel() != model()) { - qWarning() << "cannot Syncronize SelectionModel" << selectionmodel << "which has a different baseModel()"; +void SelectionModelSynchronizer::synchronizeSelectionModel(QItemSelectionModel *selectionModel) { + if(!checkBaseModel(selectionModel)) { + 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))); - + if(_selectionModels.contains(selectionModel)) { + selectionModel->setCurrentIndex(mapFromSource(currentIndex(), selectionModel), QItemSelectionModel::Current); + selectionModel->select(mapSelectionFromSource(currentSelection(), selectionModel), QItemSelectionModel::ClearAndSelect); + return; + } + + connect(selectionModel, SIGNAL(currentChanged(QModelIndex, QModelIndex)), + this, SLOT(syncedCurrentChanged(QModelIndex, QModelIndex))); + connect(selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + this, SLOT(syncedSelectionChanged(QItemSelection, QItemSelection))); + + connect(selectionModel, SIGNAL(destroyed(QObject *)), this, SLOT(selectionModelDestroyed(QObject *))); + + _selectionModels << selectionModel; } -void SelectionModelSynchronizer::removeSelectionModel(MappedSelectionModel *model) { +void SelectionModelSynchronizer::removeSelectionModel(QItemSelectionModel *model) { disconnect(model, 0, this, 0); disconnect(this, 0, model, 0); + selectionModelDestroyed(model); +} + +void SelectionModelSynchronizer::selectionModelDestroyed(QObject *object) { + QItemSelectionModel *model = static_cast(object); + QSet::iterator iter = _selectionModels.begin(); + while(iter != _selectionModels.end()) { + if(*iter == model) { + iter = _selectionModels.erase(iter); + } else { + iter++; + } + } +} + +void SelectionModelSynchronizer::syncedCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous) { + Q_UNUSED(previous); + + if(!_changeCurrentEnabled) + return; + + QItemSelectionModel *selectionModel = qobject_cast(sender()); + Q_ASSERT(selectionModel); + QModelIndex newSourceCurrent = mapToSource(current, selectionModel); + if(newSourceCurrent.isValid() && newSourceCurrent != currentIndex()) + setCurrentIndex(newSourceCurrent); } -void SelectionModelSynchronizer::_mappedCurrentChanged(const QModelIndex ¤t) { - emit setCurrentIndex(current, QItemSelectionModel::ClearAndSelect); +void SelectionModelSynchronizer::syncedSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { + Q_UNUSED(selected); + Q_UNUSED(deselected); + + if(!_changeSelectionEnabled) + return; + + QItemSelectionModel *selectionModel = qobject_cast(sender()); + Q_ASSERT(selectionModel); + + QItemSelection mappedSelection = selectionModel->selection(); + QItemSelection currentSelectionMapped = mapSelectionFromSource(currentSelection(), selectionModel); + + QItemSelection checkSelection = currentSelectionMapped; + checkSelection.merge(mappedSelection, QItemSelectionModel::Deselect); + if(checkSelection.isEmpty()) { + // that means the new selection contains the current selection (currentSel - newSel = {}) + checkSelection = mappedSelection; + checkSelection.merge(currentSelectionMapped, QItemSelectionModel::Deselect); + if(checkSelection.isEmpty()) { + // that means the current selection contains the new selection (newSel - currentSel = {}) + // -> currentSel == newSel + return; + } + } + setCurrentSelection(mapSelectionToSource(mappedSelection, selectionModel)); } -void SelectionModelSynchronizer::_mappedSelectionChanged(const QItemSelection &selected) { - emit select(selected, QItemSelectionModel::ClearAndSelect); +QModelIndex SelectionModelSynchronizer::mapFromSource(const QModelIndex &sourceIndex, const QItemSelectionModel *selectionModel) { + Q_ASSERT(selectionModel); + + QModelIndex mappedIndex = sourceIndex; + + // make a list of all involved proxies, wie have to traverse backwards + QList proxyModels; + const QAbstractItemModel *baseModel = selectionModel->model(); + const QAbstractProxyModel *proxyModel = 0; + while((proxyModel = qobject_cast(baseModel)) != 0) { + if(baseModel == model()) + break; + proxyModels << proxyModel; + baseModel = proxyModel->sourceModel(); + } + + // now traverse it; + for(int i = proxyModels.count() - 1; i >= 0; i--) { + mappedIndex = proxyModels[i]->mapFromSource(mappedIndex); + } + + return mappedIndex; +} + +QItemSelection SelectionModelSynchronizer::mapSelectionFromSource(const QItemSelection &sourceSelection, const QItemSelectionModel *selectionModel) { + Q_ASSERT(selectionModel); + + QItemSelection mappedSelection = sourceSelection; + + // make a list of all involved proxies, wie have to traverse backwards + QList proxyModels; + const QAbstractItemModel *baseModel = selectionModel->model(); + const QAbstractProxyModel *proxyModel = 0; + while((proxyModel = qobject_cast(baseModel)) != 0) { + if(baseModel == model()) + break; + proxyModels << proxyModel; + baseModel = proxyModel->sourceModel(); + } + + // now traverse it; + for(int i = proxyModels.count() - 1; i >= 0; i--) { + mappedSelection = proxyModels[i]->mapSelectionFromSource(mappedSelection); + } + return mappedSelection; +} + +QModelIndex SelectionModelSynchronizer::mapToSource(const QModelIndex &index, QItemSelectionModel *selectionModel) { + Q_ASSERT(selectionModel); + + QModelIndex sourceIndex = index; + const QAbstractItemModel *baseModel = selectionModel->model(); + const QAbstractProxyModel *proxyModel = 0; + while((proxyModel = qobject_cast(baseModel)) != 0) { + sourceIndex = proxyModel->mapToSource(sourceIndex); + baseModel = proxyModel->sourceModel(); + if(baseModel == model()) + break; + } + return sourceIndex; +} + +QItemSelection SelectionModelSynchronizer::mapSelectionToSource(const QItemSelection &selection, QItemSelectionModel *selectionModel) { + Q_ASSERT(selectionModel); + + QItemSelection sourceSelection = selection; + const QAbstractItemModel *baseModel = selectionModel->model(); + const QAbstractProxyModel *proxyModel = 0; + while((proxyModel = qobject_cast(baseModel)) != 0) { + sourceSelection = proxyModel->mapSelectionToSource(sourceSelection); + baseModel = proxyModel->sourceModel(); + if(baseModel == model()) + break; + } + return sourceSelection; +} + +void SelectionModelSynchronizer::setCurrentIndex(const QModelIndex &index) { + _selectionModel.setCurrentIndex(index, QItemSelectionModel::Current); +} +void SelectionModelSynchronizer::setCurrentSelection(const QItemSelection &selection) { + _selectionModel.select(selection, QItemSelectionModel::ClearAndSelect); +} + +void SelectionModelSynchronizer::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { + Q_UNUSED(previous); + + _changeCurrentEnabled = false; + QSet::iterator iter = _selectionModels.begin(); + while(iter != _selectionModels.end()) { + (*iter)->setCurrentIndex(mapFromSource(current, (*iter)), QItemSelectionModel::Current); + iter++; + } + _changeCurrentEnabled = true; +} + +void SelectionModelSynchronizer::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { + Q_UNUSED(selected); + Q_UNUSED(deselected); + + _changeSelectionEnabled = false; + QSet::iterator iter = _selectionModels.begin(); + while(iter != _selectionModels.end()) { + (*iter)->select(mapSelectionFromSource(currentSelection(), (*iter)), QItemSelectionModel::ClearAndSelect); + iter++; + } + _changeSelectionEnabled = true; }