File indexing completed on 2024-04-28 04:18:52

0001 // vim: set tabstop=4 shiftwidth=4 expandtab:
0002 /*
0003 Gwenview: an image viewer
0004 Copyright 2012 Aurélien Gâteau <agateau@kde.org>
0005 
0006 This program is free software; you can redistribute it and/or
0007 modify it under the terms of the GNU General Public License
0008 as published by the Free Software Foundation; either version 2
0009 of the License, or (at your option) any later version.
0010 
0011 This program is distributed in the hope that it will be useful,
0012 but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 GNU General Public License for more details.
0015 
0016 You should have received a copy of the GNU General Public License
0017 along with this program; if not, write to the Free Software
0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.
0019 
0020 */
0021 // Self
0022 #include "recursivedirmodel.h"
0023 
0024 // Local
0025 #include "gwenview_lib_debug.h"
0026 #include <lib/gvdebug.h>
0027 
0028 // KF
0029 #include <KDirLister>
0030 #include <KDirModel>
0031 
0032 // Qt
0033 
0034 namespace Gwenview
0035 {
0036 struct RecursiveDirModelPrivate {
0037     KDirLister *mDirLister = nullptr;
0038 
0039     int rowForUrl(const QUrl &url) const
0040     {
0041         return mRowForUrl.value(url, -1);
0042     }
0043 
0044     void removeAt(int row)
0045     {
0046         KFileItem item = mList.takeAt(row);
0047         mRowForUrl.remove(item.url());
0048 
0049         // Decrease row value for all urls after the one we removed
0050         // ("row" now points to the item after the one we removed since we used takeAt)
0051         const int count = mList.count();
0052         for (; row < count; ++row) {
0053             QUrl url = mList.at(row).url();
0054             mRowForUrl[url]--;
0055         }
0056     }
0057 
0058     void addItem(const KFileItem &item)
0059     {
0060         mRowForUrl.insert(item.url(), mList.count());
0061         mList.append(item);
0062     }
0063 
0064     void clear()
0065     {
0066         mRowForUrl.clear();
0067         mList.clear();
0068     }
0069 
0070     // RecursiveDirModel can only access mList through this read-only getter.
0071     // This ensures it cannot introduce inconsistencies between mList and mRowForUrl.
0072     const KFileItemList &list() const
0073     {
0074         return mList;
0075     }
0076 
0077 private:
0078     KFileItemList mList;
0079     QHash<QUrl, int> mRowForUrl;
0080 };
0081 
0082 RecursiveDirModel::RecursiveDirModel(QObject *parent)
0083     : QAbstractListModel(parent)
0084     , d(new RecursiveDirModelPrivate)
0085 {
0086     d->mDirLister = new KDirLister(this);
0087     connect(d->mDirLister, &KDirLister::itemsAdded, this, &RecursiveDirModel::slotItemsAdded);
0088     connect(d->mDirLister, &KDirLister::itemsDeleted, this, &RecursiveDirModel::slotItemsDeleted);
0089     connect(d->mDirLister, QOverload<>::of(&KDirLister::completed), this, &RecursiveDirModel::completed);
0090     connect(d->mDirLister, QOverload<>::of(&KDirLister::clear), this, &RecursiveDirModel::slotCleared);
0091 
0092     connect(d->mDirLister, &KDirLister::clearDir, this, &RecursiveDirModel::slotDirCleared);
0093 }
0094 
0095 RecursiveDirModel::~RecursiveDirModel()
0096 {
0097     delete d;
0098 }
0099 
0100 QUrl RecursiveDirModel::url() const
0101 {
0102     return d->mDirLister->url();
0103 }
0104 
0105 void RecursiveDirModel::setUrl(const QUrl &url)
0106 {
0107     beginResetModel();
0108     d->clear();
0109     endResetModel();
0110     d->mDirLister->openUrl(url);
0111 }
0112 
0113 int RecursiveDirModel::rowCount(const QModelIndex &parent) const
0114 {
0115     if (parent.isValid()) {
0116         return 0;
0117     } else {
0118         return d->list().count();
0119     }
0120 }
0121 
0122 QVariant RecursiveDirModel::data(const QModelIndex &index, int role) const
0123 {
0124     if (index.parent().isValid()) {
0125         return {};
0126     }
0127     KFileItem item = d->list().value(index.row());
0128     if (item.isNull()) {
0129         qCWarning(GWENVIEW_LIB_LOG) << "Invalid row" << index.row();
0130         return {};
0131     }
0132     switch (role) {
0133     case Qt::DisplayRole:
0134         return item.text();
0135     case Qt::DecorationRole:
0136         return item.iconName();
0137     case KDirModel::FileItemRole:
0138         return QVariant(item);
0139     default:
0140         qCWarning(GWENVIEW_LIB_LOG) << "Unhandled role" << role;
0141         break;
0142     }
0143     return {};
0144 }
0145 
0146 void RecursiveDirModel::slotItemsAdded(const QUrl &, const KFileItemList &newList)
0147 {
0148     QList<QUrl> dirUrls;
0149     KFileItemList fileList;
0150     for (const KFileItem &item : newList) {
0151         if (item.isFile()) {
0152             if (d->rowForUrl(item.url()) == -1) {
0153                 fileList << item;
0154             }
0155         } else {
0156             dirUrls << item.url();
0157         }
0158     }
0159 
0160     if (!fileList.isEmpty()) {
0161         beginInsertRows(QModelIndex(), d->list().count(), d->list().count() + fileList.count());
0162         for (const KFileItem &item : qAsConst(fileList)) {
0163             d->addItem(item);
0164         }
0165         endInsertRows();
0166     }
0167 
0168     for (const QUrl &url : qAsConst(dirUrls)) {
0169         d->mDirLister->openUrl(url, KDirLister::Keep);
0170     }
0171 }
0172 
0173 void RecursiveDirModel::slotItemsDeleted(const KFileItemList &list)
0174 {
0175     for (const KFileItem &item : list) {
0176         if (item.isDir()) {
0177             continue;
0178         }
0179         int row = d->rowForUrl(item.url());
0180         if (row == -1) {
0181             qCWarning(GWENVIEW_LIB_LOG) << "Received itemsDeleted for an unknown item: this should not happen!";
0182             GV_FATAL_FAILS;
0183             continue;
0184         }
0185         beginRemoveRows(QModelIndex(), row, row);
0186         d->removeAt(row);
0187         endRemoveRows();
0188     }
0189 }
0190 
0191 void RecursiveDirModel::slotCleared()
0192 {
0193     if (d->list().isEmpty()) {
0194         return;
0195     }
0196     beginResetModel();
0197     d->clear();
0198     endResetModel();
0199 }
0200 
0201 void RecursiveDirModel::slotDirCleared(const QUrl &dirUrl)
0202 {
0203     int row;
0204     for (row = d->list().count() - 1; row >= 0; --row) {
0205         const QUrl url = d->list().at(row).url();
0206         if (dirUrl.isParentOf(url)) {
0207             beginRemoveRows(QModelIndex(), row, row);
0208             d->removeAt(row);
0209             endRemoveRows();
0210         }
0211     }
0212 }
0213 
0214 } // namespace
0215 
0216 #include "moc_recursivedirmodel.cpp"