File indexing completed on 2025-01-05 04:02:13

0001 /*
0002 Gwenview: an image viewer
0003 Copyright 2007 Aurélien Gâteau <agateau@kde.org>
0004 
0005 This program is free software; you can redistribute it and/or
0006 modify it under the terms of the GNU General Public License
0007 as published by the Free Software Foundation; either version 2
0008 of the License, or (at your option) any later version.
0009 
0010 This program is distributed in the hope that it will be useful,
0011 but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013 GNU General Public License for more details.
0014 
0015 You should have received a copy of the GNU General Public License
0016 along with this program; if not, write to the Free Software
0017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
0018 
0019 */
0020 #include "sorteddirmodel.h"
0021 
0022 // Qt
0023 #include <QTimer>
0024 #include <QUrl>
0025 
0026 // KF
0027 #include <KDirLister>
0028 #ifdef GWENVIEW_SEMANTICINFO_BACKEND_NONE
0029 #include <KDirModel>
0030 #endif
0031 // Local
0032 #include <lib/archiveutils.h>
0033 #include <lib/timeutils.h>
0034 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
0035 #include "abstractsemanticinfobackend.h"
0036 #include "semanticinfodirmodel.h"
0037 #include <lib/sorting.h>
0038 #endif
0039 
0040 namespace Gwenview
0041 {
0042 AbstractSortedDirModelFilter::AbstractSortedDirModelFilter(SortedDirModel *model)
0043     : QObject(model)
0044     , mModel(model)
0045 {
0046     if (mModel) {
0047         mModel->addFilter(this);
0048     }
0049 }
0050 
0051 AbstractSortedDirModelFilter::~AbstractSortedDirModelFilter()
0052 {
0053     if (mModel) {
0054         mModel->removeFilter(this);
0055     }
0056 }
0057 
0058 struct SortedDirModelPrivate {
0059 #ifdef GWENVIEW_SEMANTICINFO_BACKEND_NONE
0060     KDirModel *mSourceModel;
0061 #else
0062     SemanticInfoDirModel *mSourceModel;
0063 #endif
0064     QStringList mBlackListedExtensions;
0065     QList<AbstractSortedDirModelFilter *> mFilters;
0066     QTimer mDelayedApplyFiltersTimer;
0067     MimeTypeUtils::Kinds mKindFilter;
0068 };
0069 
0070 SortedDirModel::SortedDirModel(QObject *parent)
0071     : KDirSortFilterProxyModel(parent)
0072     , d(new SortedDirModelPrivate)
0073 {
0074 #ifdef GWENVIEW_SEMANTICINFO_BACKEND_NONE
0075     d->mSourceModel = new KDirModel(this);
0076 #else
0077     d->mSourceModel = new SemanticInfoDirModel(this);
0078 #endif
0079     setSourceModel(d->mSourceModel);
0080 
0081     d->mSourceModel->dirLister()->setRequestMimeTypeWhileListing(true);
0082 
0083     d->mDelayedApplyFiltersTimer.setInterval(0);
0084     d->mDelayedApplyFiltersTimer.setSingleShot(true);
0085     connect(&d->mDelayedApplyFiltersTimer, &QTimer::timeout, this, &SortedDirModel::doApplyFilters);
0086 }
0087 
0088 SortedDirModel::~SortedDirModel()
0089 {
0090     delete d;
0091 }
0092 
0093 MimeTypeUtils::Kinds SortedDirModel::kindFilter() const
0094 {
0095     return d->mKindFilter;
0096 }
0097 
0098 void SortedDirModel::setKindFilter(MimeTypeUtils::Kinds kindFilter)
0099 {
0100     if (d->mKindFilter == kindFilter) {
0101         return;
0102     }
0103     d->mKindFilter = kindFilter;
0104     applyFilters();
0105 }
0106 
0107 void SortedDirModel::adjustKindFilter(MimeTypeUtils::Kinds kinds, bool set)
0108 {
0109     MimeTypeUtils::Kinds kindFilter = d->mKindFilter;
0110     if (set) {
0111         kindFilter |= kinds;
0112     } else {
0113         kindFilter &= ~kinds;
0114     }
0115     setKindFilter(kindFilter);
0116 }
0117 
0118 void SortedDirModel::addFilter(AbstractSortedDirModelFilter *filter)
0119 {
0120     d->mFilters << filter;
0121     applyFilters();
0122 }
0123 
0124 void SortedDirModel::removeFilter(AbstractSortedDirModelFilter *filter)
0125 {
0126     d->mFilters.removeAll(filter);
0127     applyFilters();
0128 }
0129 
0130 KDirLister *SortedDirModel::dirLister() const
0131 {
0132     return d->mSourceModel->dirLister();
0133 }
0134 
0135 void SortedDirModel::reload()
0136 {
0137 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
0138     d->mSourceModel->clearSemanticInfoCache();
0139 #endif
0140     dirLister()->updateDirectory(dirLister()->url());
0141 }
0142 
0143 void SortedDirModel::setBlackListedExtensions(const QStringList &list)
0144 {
0145     d->mBlackListedExtensions = list;
0146 }
0147 
0148 KFileItem SortedDirModel::itemForIndex(const QModelIndex &index) const
0149 {
0150     if (!index.isValid()) {
0151         return {};
0152     }
0153 
0154     QModelIndex sourceIndex = mapToSource(index);
0155     return d->mSourceModel->itemForIndex(sourceIndex);
0156 }
0157 
0158 QUrl SortedDirModel::urlForIndex(const QModelIndex &index) const
0159 {
0160     KFileItem item = itemForIndex(index);
0161     return item.isNull() ? QUrl() : item.url();
0162 }
0163 
0164 KFileItem SortedDirModel::itemForSourceIndex(const QModelIndex &sourceIndex) const
0165 {
0166     if (!sourceIndex.isValid()) {
0167         return {};
0168     }
0169     return d->mSourceModel->itemForIndex(sourceIndex);
0170 }
0171 
0172 QModelIndex SortedDirModel::indexForItem(const KFileItem &item) const
0173 {
0174     if (item.isNull()) {
0175         return {};
0176     }
0177 
0178     QModelIndex sourceIndex = d->mSourceModel->indexForItem(item);
0179     return mapFromSource(sourceIndex);
0180 }
0181 
0182 QModelIndex SortedDirModel::indexForUrl(const QUrl &url) const
0183 {
0184     if (!url.isValid()) {
0185         return {};
0186     }
0187     QModelIndex sourceIndex = d->mSourceModel->indexForUrl(url);
0188     return mapFromSource(sourceIndex);
0189 }
0190 
0191 bool SortedDirModel::filterAcceptsRow(int row, const QModelIndex &parent) const
0192 {
0193     QModelIndex index = d->mSourceModel->index(row, 0, parent);
0194     KFileItem fileItem = d->mSourceModel->itemForIndex(index);
0195 
0196     MimeTypeUtils::Kinds kind = MimeTypeUtils::fileItemKind(fileItem);
0197     if (d->mKindFilter != MimeTypeUtils::Kinds() && !(d->mKindFilter & kind)) {
0198         return false;
0199     }
0200 
0201     if (kind != MimeTypeUtils::KIND_DIR && kind != MimeTypeUtils::KIND_ARCHIVE) {
0202         int dotPos = fileItem.name().lastIndexOf(QLatin1Char('.'));
0203         if (dotPos >= 1) {
0204             QString extension = fileItem.name().mid(dotPos + 1).toLower();
0205             if (d->mBlackListedExtensions.contains(extension)) {
0206                 return false;
0207             }
0208         }
0209 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
0210         if (!d->mSourceModel->semanticInfoAvailableForIndex(index)) {
0211             for (const AbstractSortedDirModelFilter *filter : qAsConst(d->mFilters)) {
0212                 // Make sure we have semanticinfo, otherwise retrieve it and
0213                 // return false, we will be called again later when it is
0214                 // there.
0215                 if (filter->needsSemanticInfo()) {
0216                     d->mSourceModel->retrieveSemanticInfoForIndex(index);
0217                     return false;
0218                 }
0219             }
0220         }
0221 #endif
0222 
0223         for (const AbstractSortedDirModelFilter *filter : qAsConst(d->mFilters)) {
0224             if (!filter->acceptsIndex(index)) {
0225                 return false;
0226             }
0227         }
0228     }
0229     return KDirSortFilterProxyModel::filterAcceptsRow(row, parent);
0230 }
0231 
0232 AbstractSemanticInfoBackEnd *SortedDirModel::semanticInfoBackEnd() const
0233 {
0234 #ifdef GWENVIEW_SEMANTICINFO_BACKEND_NONE
0235     return 0;
0236 #else
0237     return d->mSourceModel->semanticInfoBackEnd();
0238 #endif
0239 }
0240 
0241 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
0242 SemanticInfo SortedDirModel::semanticInfoForSourceIndex(const QModelIndex &sourceIndex) const
0243 {
0244     return d->mSourceModel->semanticInfoForIndex(sourceIndex);
0245 }
0246 #endif
0247 
0248 void SortedDirModel::applyFilters()
0249 {
0250     d->mDelayedApplyFiltersTimer.start();
0251 }
0252 
0253 void SortedDirModel::doApplyFilters()
0254 {
0255     QSortFilterProxyModel::invalidateFilter();
0256 }
0257 
0258 bool SortedDirModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
0259 {
0260     const KFileItem leftItem = itemForSourceIndex(left);
0261     const KFileItem rightItem = itemForSourceIndex(right);
0262 
0263     const bool leftIsDirOrArchive = ArchiveUtils::fileItemIsDirOrArchive(leftItem);
0264     const bool rightIsDirOrArchive = ArchiveUtils::fileItemIsDirOrArchive(rightItem);
0265 
0266     if (leftIsDirOrArchive != rightIsDirOrArchive) {
0267         return sortOrder() == Qt::AscendingOrder ? leftIsDirOrArchive : rightIsDirOrArchive;
0268     }
0269 
0270     // Apply special sort handling only to images. For folders/archives or when
0271     // a secondary criterion is needed, delegate sorting to the parent class.
0272     if (!leftIsDirOrArchive) {
0273         if (sortColumn() == KDirModel::ModifiedTime) {
0274             const QDateTime leftDate = TimeUtils::dateTimeForFileItem(leftItem);
0275             const QDateTime rightDate = TimeUtils::dateTimeForFileItem(rightItem);
0276 
0277             if (leftDate != rightDate) {
0278                 return leftDate < rightDate;
0279             }
0280         }
0281 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
0282         if (sortRole() == SemanticInfoDirModel::RatingRole) {
0283             const int leftRating = d->mSourceModel->data(left, SemanticInfoDirModel::RatingRole).toInt();
0284             const int rightRating = d->mSourceModel->data(right, SemanticInfoDirModel::RatingRole).toInt();
0285 
0286             if (leftRating != rightRating) {
0287                 return leftRating < rightRating;
0288             }
0289         }
0290 #endif
0291     }
0292 
0293     return KDirSortFilterProxyModel::lessThan(left, right);
0294 }
0295 
0296 bool SortedDirModel::hasDocuments() const
0297 {
0298     const int count = rowCount();
0299     if (count == 0) {
0300         return false;
0301     }
0302     for (int row = 0; row < count; ++row) {
0303         const QModelIndex idx = index(row, 0);
0304         const KFileItem item = itemForIndex(idx);
0305         if (!ArchiveUtils::fileItemIsDirOrArchive(item)) {
0306             return true;
0307         }
0308     }
0309     return false;
0310 }
0311 
0312 void SortedDirModel::setDirLister(KDirLister *dirLister)
0313 {
0314     d->mSourceModel->setDirLister(dirLister);
0315 }
0316 
0317 } // namespace
0318 
0319 #include "moc_sorteddirmodel.cpp"