/* Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kbreadcrumbselectionmodel.h" #include "kdebug.h" class KBreadcrumbSelectionModelPrivate { Q_DECLARE_PUBLIC(KBreadcrumbSelectionModel) KBreadcrumbSelectionModel * const q_ptr; public: KBreadcrumbSelectionModelPrivate(KBreadcrumbSelectionModel *breadcrumbSelector, QItemSelectionModel *selectionModel, KBreadcrumbSelectionModel::BreadcrumbTarget direction) : q_ptr(breadcrumbSelector), m_includeActualSelection(true), m_selectionDepth(-1), m_showHiddenAscendantData(false), m_selectionModel(selectionModel), m_direction(direction), m_ignoreCurrentChanged(false) { } /** Returns a selection containing the breadcrumbs for @p index */ QItemSelection getBreadcrumbSelection(const QModelIndex &index); /** Returns a selection containing the breadcrumbs for @p selection */ QItemSelection getBreadcrumbSelection(const QItemSelection &selection); void sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void init(); void syncBreadcrumbs(); bool m_includeActualSelection; int m_selectionDepth; bool m_showHiddenAscendantData; QItemSelectionModel *m_selectionModel; KBreadcrumbSelectionModel::BreadcrumbTarget m_direction; bool m_ignoreCurrentChanged; }; KBreadcrumbSelectionModel::KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, QObject* parent) : QItemSelectionModel(const_cast(selectionModel->model()), parent), d_ptr(new KBreadcrumbSelectionModelPrivate(this, selectionModel, MakeBreadcrumbSelectionInSelf)) { d_ptr->init(); } KBreadcrumbSelectionModel::KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, BreadcrumbTarget direction, QObject* parent) : QItemSelectionModel(const_cast(selectionModel->model()), parent), d_ptr(new KBreadcrumbSelectionModelPrivate(this, selectionModel, direction)) { if ( direction != MakeBreadcrumbSelectionInSelf) connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(sourceSelectionChanged(QItemSelection,QItemSelection))); d_ptr->init(); } KBreadcrumbSelectionModel::~KBreadcrumbSelectionModel() { delete d_ptr; } bool KBreadcrumbSelectionModel::isActualSelectionIncluded() const { Q_D(const KBreadcrumbSelectionModel); return d->m_includeActualSelection; } void KBreadcrumbSelectionModel::setActualSelectionIncluded(bool includeActualSelection) { Q_D(KBreadcrumbSelectionModel); d->m_includeActualSelection = includeActualSelection; } int KBreadcrumbSelectionModel::breadcrumbLength() const { Q_D(const KBreadcrumbSelectionModel); return d->m_selectionDepth; } void KBreadcrumbSelectionModel::setBreadcrumbLength(int breadcrumbLength) { Q_D(KBreadcrumbSelectionModel); d->m_selectionDepth = breadcrumbLength; } QItemSelection KBreadcrumbSelectionModelPrivate::getBreadcrumbSelection(const QModelIndex& index) { QItemSelection breadcrumbSelection; if (m_includeActualSelection) breadcrumbSelection.append(QItemSelectionRange(index)); QModelIndex parent = index.parent(); int sumBreadcrumbs = 0; bool includeAll = m_selectionDepth < 0; while (parent.isValid() && (includeAll || sumBreadcrumbs < m_selectionDepth)) { breadcrumbSelection.append(QItemSelectionRange(parent)); parent = parent.parent(); } return breadcrumbSelection; } QItemSelection KBreadcrumbSelectionModelPrivate::getBreadcrumbSelection(const QItemSelection& selection) { QItemSelection breadcrumbSelection; if (m_includeActualSelection) breadcrumbSelection = selection; QItemSelection::const_iterator it = selection.constBegin(); const QItemSelection::const_iterator end = selection.constEnd(); for ( ; it != end; ++it) { QModelIndex parent = it->parent(); if (breadcrumbSelection.contains(parent)) continue; int sumBreadcrumbs = 0; bool includeAll = m_selectionDepth < 0; while (parent.isValid() && (includeAll || sumBreadcrumbs < m_selectionDepth)) { breadcrumbSelection.append(QItemSelectionRange(parent)); parent = parent.parent(); if (breadcrumbSelection.contains(parent)) break; ++sumBreadcrumbs; } } return breadcrumbSelection; } void KBreadcrumbSelectionModelPrivate::sourceSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { Q_Q(KBreadcrumbSelectionModel); QItemSelection deselectedCrumbs = getBreadcrumbSelection(deselected); QItemSelection selectedCrumbs = getBreadcrumbSelection(selected); QItemSelection removed = deselectedCrumbs; foreach(const QItemSelectionRange &range, selectedCrumbs) { removed.removeAll(range); } QItemSelection added = selectedCrumbs; foreach(const QItemSelectionRange &range, deselectedCrumbs) { added.removeAll(range); } if (!removed.isEmpty()) { q->QItemSelectionModel::select(removed, QItemSelectionModel::Deselect); } if (!added.isEmpty()) { q->QItemSelectionModel::select(added, QItemSelectionModel::Select); } } void KBreadcrumbSelectionModel::select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) { Q_D(KBreadcrumbSelectionModel); // When an item is removed, the current index is set to the top index in the model. // That causes a selectionChanged signal with a selection which we do not want. if ( d->m_ignoreCurrentChanged ) { d->m_ignoreCurrentChanged = false; return; } if ( d->m_direction == MakeBreadcrumbSelectionInOther ) { d->m_selectionModel->select(d->getBreadcrumbSelection(index), command); QItemSelectionModel::select(index, command); } else { d->m_selectionModel->select(index, command); QItemSelectionModel::select(d->getBreadcrumbSelection(index), command); } } void KBreadcrumbSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) { Q_D(KBreadcrumbSelectionModel); QItemSelection bcc = d->getBreadcrumbSelection(selection); if ( d->m_direction == MakeBreadcrumbSelectionInOther ) { d->m_selectionModel->select(selection, command); QItemSelectionModel::select(bcc, command); } else { d->m_selectionModel->select(bcc, command); QItemSelectionModel::select(selection, command); } } void KBreadcrumbSelectionModelPrivate::init() { Q_Q(KBreadcrumbSelectionModel); q->connect(m_selectionModel->model(), SIGNAL(layoutChanged()), SLOT(syncBreadcrumbs())); q->connect(m_selectionModel->model(), SIGNAL(modelReset()), SLOT(syncBreadcrumbs())); q->connect(m_selectionModel->model(), SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), SLOT(syncBreadcrumbs())); // Don't need to handle insert & remove because they can't change the breadcrumbs on their own. } void KBreadcrumbSelectionModelPrivate::syncBreadcrumbs() { Q_Q(KBreadcrumbSelectionModel); q->select(m_selectionModel->selection(), QItemSelectionModel::ClearAndSelect); } #include "moc_kbreadcrumbselectionmodel.cpp"