File indexing completed on 2024-05-05 04:50:34

0001 /*
0002    SPDX-FileCopyrightText: 2016 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr>
0003    SPDX-FileCopyrightText: 2018 (c) Alexander Stippich <a.stippich@gmx.net>
0004 
0005    SPDX-License-Identifier: LGPL-3.0-or-later
0006  */
0007 
0008 #include "filebrowserproxymodel.h"
0009 
0010 #include "filebrowsermodel.h"
0011 #include "mediaplaylistproxymodel.h"
0012 
0013 #include "models/modelLogging.h"
0014 
0015 #include <stack>
0016 
0017 FileBrowserProxyModel::FileBrowserProxyModel(QObject *parent)
0018     : KDirSortFilterProxyModel(parent)
0019 {
0020     setFilterCaseSensitivity(Qt::CaseInsensitive);
0021     setSortFoldersFirst(true);
0022     sort(Qt::AscendingOrder);
0023 }
0024 
0025 FileBrowserProxyModel::~FileBrowserProxyModel() = default;
0026 
0027 QString FileBrowserProxyModel::filterText() const
0028 {
0029     return mFilterText;
0030 }
0031 
0032 void FileBrowserProxyModel::setFilterText(const QString &filterText)
0033 {
0034     if (mFilterText == filterText)
0035         return;
0036 
0037     mFilterText = filterText;
0038 
0039     mFilterExpression.setPattern(mFilterText.normalized(QString::NormalizationForm_KC));
0040     mFilterExpression.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
0041     mFilterExpression.optimize();
0042 
0043     setFilterRegularExpression(mFilterExpression);
0044     setFilterRole(Qt::DisplayRole);
0045 
0046     Q_EMIT filterTextChanged(mFilterText);
0047 }
0048 
0049 void FileBrowserProxyModel::listRecursiveResult(KJob *)
0050 {
0051     if (mPendingEntries.empty()) {
0052         mEnqueueInProgress = false;
0053         Q_EMIT entriesToEnqueue(mAllData, mEnqueueMode, mTriggerPlay);
0054         return;
0055     }
0056 
0057     recursiveEnqueue();
0058 }
0059 
0060 void FileBrowserProxyModel::listRecursiveNewEntries(KIO::Job *job, const KIO::UDSEntryList &list)
0061 {
0062     Q_UNUSED(job)
0063 
0064     DataTypes::EntryDataList newData;
0065 
0066     auto vNewEntries = QVector<QString>{};
0067 
0068     vNewEntries.reserve(list.size());
0069 
0070     for (const auto &oneEntry : list) {
0071         if (oneEntry.isDir()) {
0072             continue;
0073         }
0074 
0075         vNewEntries.push_back(oneEntry.stringValue(KIO::UDSEntry::UDS_NAME));
0076     }
0077 
0078     std::sort(std::begin(vNewEntries), std::end(vNewEntries), [this](auto first, auto second) {
0079         return (sortOrder() == Qt::AscendingOrder) ? (first < second) : (first > second);
0080     });
0081 
0082     for (const auto &oneEntry : vNewEntries) {
0083         auto fullPath = QString{};
0084         auto fullPathUrl = QUrl{};
0085 
0086         if (mCurentUrl.isLocalFile()) {
0087             fullPath = QStringLiteral("%0/%1").arg(mCurentUrl.toLocalFile(), oneEntry);
0088             fullPathUrl = QUrl::fromLocalFile(fullPath);
0089         } else {
0090             fullPath = QStringLiteral("%0/%1").arg(mCurentUrl.toString(), oneEntry);
0091             fullPathUrl = QUrl{fullPath};
0092         }
0093 
0094         auto mimeType = mMimeDatabase.mimeTypeForUrl(fullPathUrl);
0095 
0096         if (!mimeType.name().startsWith(QLatin1String("audio/")) || ElisaUtils::isPlayList(mimeType)) {
0097             continue;
0098         }
0099 
0100         newData.push_back({{{DataTypes::ElementTypeRole, ElisaUtils::FileName}, {DataTypes::ResourceRole, fullPathUrl}}, fullPath, {}});
0101     }
0102 
0103     mAllData.append(newData);
0104 }
0105 
0106 void FileBrowserProxyModel::genericEnqueueToPlayList(const QModelIndex &rootIndex,
0107                                                      ElisaUtils::PlayListEnqueueMode enqueueMode,
0108                                                      ElisaUtils::PlayListEnqueueTriggerPlay triggerPlay)
0109 {
0110     if (mEnqueueInProgress) {
0111         // display error
0112         return;
0113     }
0114 
0115     mPendingEntries = {};
0116     mAllData.clear();
0117 
0118     for (int rowIndex = 0, maxRowCount = rowCount(); rowIndex < maxRowCount; ++rowIndex) {
0119         auto currentIndex = index(rowIndex, 0, rootIndex);
0120         mPendingEntries.emplace(currentIndex.data(DataTypes::FilePathRole).toUrl(),
0121                                 currentIndex.data(DataTypes::ElementTypeRole).value<ElisaUtils::PlayListEntryType>() == ElisaUtils::Container);
0122     }
0123 
0124     mEnqueueInProgress = true;
0125     mEnqueueMode = enqueueMode;
0126     mTriggerPlay = triggerPlay;
0127     recursiveEnqueue();
0128 }
0129 
0130 void FileBrowserProxyModel::enqueueToPlayList(const QModelIndex &rootIndex)
0131 {
0132     genericEnqueueToPlayList(rootIndex,
0133                              ElisaUtils::AppendPlayList,
0134                              ElisaUtils::DoNotTriggerPlay);
0135 }
0136 
0137 void FileBrowserProxyModel::enqueue(const DataTypes::MusicDataType &newEntry,
0138                                     const QString &newEntryTitle,
0139                                     ElisaUtils::PlayListEnqueueMode enqueueMode,
0140                                     ElisaUtils::PlayListEnqueueTriggerPlay triggerPlay)
0141 {
0142     Q_UNUSED(newEntryTitle)
0143 
0144     if (mEnqueueInProgress) {
0145         // display error
0146         return;
0147     }
0148 
0149     mPendingEntries = {};
0150     mAllData.clear();
0151 
0152     switch (newEntry.elementType())
0153     {
0154     case ElisaUtils::Container:
0155         mPendingEntries.emplace(newEntry[DataTypes::FilePathRole].toUrl(), true);
0156         break;
0157     case ElisaUtils::FileName:
0158     case ElisaUtils::Track:
0159         mPendingEntries.emplace(newEntry[DataTypes::ResourceRole].toUrl(), false);
0160         break;
0161     case ElisaUtils::PlayList:
0162         if (mPlayList) {
0163             QMetaObject::invokeMethod(mPlayList,
0164                                       "loadPlayList",
0165                                       Q_ARG(QUrl, newEntry[DataTypes::ResourceRole].toUrl()),
0166                                       Q_ARG(ElisaUtils::PlayListEnqueueMode, enqueueMode),
0167                                       Q_ARG(ElisaUtils::PlayListEnqueueTriggerPlay, triggerPlay));
0168         }
0169         return;
0170     case ElisaUtils::Album:
0171     case ElisaUtils::Artist:
0172     case ElisaUtils::Composer:
0173     case ElisaUtils::Genre:
0174     case ElisaUtils::Lyricist:
0175     case ElisaUtils::Radio:
0176     case ElisaUtils::Unknown:
0177         break;
0178     }
0179 
0180     if (mPendingEntries.empty()) {
0181         return;
0182     }
0183 
0184     mEnqueueInProgress = true;
0185     mEnqueueMode = enqueueMode;
0186     mTriggerPlay = triggerPlay;
0187     recursiveEnqueue();
0188 }
0189 
0190 void FileBrowserProxyModel::replaceAndPlayOfPlayList(const QModelIndex &rootIndex)
0191 {
0192     genericEnqueueToPlayList(rootIndex,
0193                              ElisaUtils::ReplacePlayList,
0194                              ElisaUtils::TriggerPlay);
0195 }
0196 
0197 void FileBrowserProxyModel::disconnectPlayList()
0198 {
0199     if (mPlayList) {
0200         disconnect(this, &FileBrowserProxyModel::entriesToEnqueue,
0201                    mPlayList, static_cast<void (MediaPlayListProxyModel::*)(const DataTypes::EntryDataList &, ElisaUtils::PlayListEnqueueMode, ElisaUtils::PlayListEnqueueTriggerPlay)>(&MediaPlayListProxyModel::enqueue));
0202     }
0203 }
0204 
0205 void FileBrowserProxyModel::connectPlayList()
0206 {
0207     if (mPlayList) {
0208         connect(this, &FileBrowserProxyModel::entriesToEnqueue,
0209                 mPlayList, static_cast<void (MediaPlayListProxyModel::*)(const DataTypes::EntryDataList &, ElisaUtils::PlayListEnqueueMode, ElisaUtils::PlayListEnqueueTriggerPlay)>(&MediaPlayListProxyModel::enqueue));
0210     }
0211 }
0212 
0213 void FileBrowserProxyModel::recursiveEnqueue()
0214 {
0215     auto [rootUrl, isDirectory] = mPendingEntries.front();
0216 
0217     if (rootUrl.isEmpty()) {
0218         return;
0219     }
0220 
0221     mPendingEntries.pop();
0222     if (isDirectory) {
0223         mCurentUrl = rootUrl;
0224         mCurrentJob = KIO::listRecursive(rootUrl, { KIO::HideProgressInfo });
0225 
0226         connect(mCurrentJob, &KJob::result, this, &FileBrowserProxyModel::listRecursiveResult);
0227 
0228         connect(dynamic_cast<KIO::ListJob*>(mCurrentJob), &KIO::ListJob::entries,
0229                 this, &FileBrowserProxyModel::listRecursiveNewEntries);
0230     } else {
0231         if (mPlayList) {
0232             if (!ElisaUtils::isPlayList(mMimeDatabase.mimeTypeForUrl(rootUrl))) {
0233                 mAllData.push_back({{{DataTypes::ElementTypeRole, ElisaUtils::FileName}, {DataTypes::ResourceRole, rootUrl}}, rootUrl.toString(), rootUrl});
0234             }
0235 
0236             if (mPendingEntries.empty()) {
0237                 mEnqueueInProgress = false;
0238                 Q_EMIT entriesToEnqueue(mAllData, mEnqueueMode, mTriggerPlay);
0239                 return;
0240             }
0241 
0242             recursiveEnqueue();
0243         }
0244     }
0245 }
0246 
0247 void FileBrowserProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
0248 {
0249     KDirSortFilterProxyModel::setSourceModel(sourceModel);
0250 
0251     auto fileBrowserModel = dynamic_cast<FileBrowserModel *>(sourceModel);
0252 
0253     if (!fileBrowserModel) {
0254         return;
0255     }
0256 }
0257 
0258 MediaPlayListProxyModel *FileBrowserProxyModel::playList() const
0259 {
0260     return mPlayList;
0261 }
0262 
0263 int FileBrowserProxyModel::filterRating() const
0264 {
0265     return mFilterRating;
0266 }
0267 
0268 bool FileBrowserProxyModel::sortedAscending() const
0269 {
0270     return sortOrder() ? false : true;
0271 }
0272 
0273 void FileBrowserProxyModel::sortModel(Qt::SortOrder order)
0274 {
0275     sort(0, order);
0276     Q_EMIT sortedAscendingChanged();
0277 }
0278 
0279 void FileBrowserProxyModel::setPlayList(MediaPlayListProxyModel *playList)
0280 {
0281     disconnectPlayList();
0282 
0283     if (mPlayList == playList) {
0284         return;
0285     }
0286 
0287     mPlayList = playList;
0288     Q_EMIT playListChanged();
0289 
0290     connectPlayList();
0291 }
0292 
0293 void FileBrowserProxyModel::setFilterRating(int filterRating)
0294 {
0295     if (mFilterRating == filterRating) {
0296         return;
0297     }
0298 
0299     mFilterRating = filterRating;
0300     Q_EMIT filterRatingChanged();
0301 }
0302 
0303 #include "moc_filebrowserproxymodel.cpp"