diff --git a/src/mediaplaylistproxymodel.cpp b/src/mediaplaylistproxymodel.cpp index 49aab627..9c8f22d9 100644 --- a/src/mediaplaylistproxymodel.cpp +++ b/src/mediaplaylistproxymodel.cpp @@ -1,806 +1,806 @@ /* SPDX-FileCopyrightText: 2015 (c) Matthieu Gallien SPDX-FileCopyrightText: 2019 (c) Alexander Stippich SPDX-License-Identifier: LGPL-3.0-or-later */ #include "mediaplaylistproxymodel.h" #include "mediaplaylist.h" #include "playListLogging.h" #include #include #include #include #include #include #include #include #include class MediaPlayListProxyModelPrivate { public: MediaPlayList* mPlayListModel; QPersistentModelIndex mPreviousTrack; QPersistentModelIndex mCurrentTrack; QPersistentModelIndex mNextTrack; QMediaPlaylist mLoadPlaylist; QList mRandomMapping; QVariantMap mPersistentSettingsForUndo; QRandomGenerator mRandomGenerator; QMimeDatabase mMimeDb; ElisaUtils::PlayListEnqueueTriggerPlay mTriggerPlay = ElisaUtils::DoNotTriggerPlay; int mCurrentPlayListPosition = -1; bool mRepeatPlay = false; bool mShufflePlayList = false; }; MediaPlayListProxyModel::MediaPlayListProxyModel(QObject *parent) : QAbstractProxyModel (parent), d(std::make_unique()) { connect(&d->mLoadPlaylist, &QMediaPlaylist::loaded, this, &MediaPlayListProxyModel::loadPlayListLoaded); connect(&d->mLoadPlaylist, &QMediaPlaylist::loadFailed, this, &MediaPlayListProxyModel::loadPlayListLoadFailed); d->mRandomGenerator.seed(static_cast(QTime::currentTime().msec())); } MediaPlayListProxyModel::~MediaPlayListProxyModel() =default; QModelIndex MediaPlayListProxyModel::index(int row, int column, const QModelIndex &parent) const { if (row < 0 || column < 0 || row > rowCount() - 1) { return QModelIndex(); } return createIndex(row, column); Q_UNUSED(parent); } QModelIndex MediaPlayListProxyModel::mapFromSource(const QModelIndex &sourceIndex) const { if (!sourceIndex.isValid()) { return QModelIndex(); } return d->mPlayListModel->index(mapRowFromSource(sourceIndex.row()), sourceIndex.column()); } QItemSelection MediaPlayListProxyModel::mapSelectionFromSource(const QItemSelection &sourceSelection) const { QItemSelection proxySelection; for (const QItemSelectionRange &range : sourceSelection) { QModelIndex proxyTopLeft = mapFromSource(range.topLeft()); QModelIndex proxyBottomRight = mapFromSource(range.bottomRight()); proxySelection.append(QItemSelectionRange(proxyTopLeft, proxyBottomRight)); } return proxySelection; } QItemSelection MediaPlayListProxyModel::mapSelectionToSource(const QItemSelection &proxySelection) const { QItemSelection sourceSelection; for (const QItemSelectionRange &range : proxySelection) { QModelIndex sourceTopLeft = mapToSource(range.topLeft()); QModelIndex sourceBottomRight = mapToSource(range.bottomRight()); sourceSelection.append(QItemSelectionRange(sourceTopLeft, sourceBottomRight)); } return sourceSelection; } QModelIndex MediaPlayListProxyModel::mapToSource(const QModelIndex &proxyIndex) const { if (!proxyIndex.isValid()) { return QModelIndex(); } return d->mPlayListModel->index(mapRowToSource(proxyIndex.row()), proxyIndex.column()); } int MediaPlayListProxyModel::mapRowToSource(const int proxyRow) const { if (d->mShufflePlayList) { return d->mRandomMapping.at(proxyRow); } else { return proxyRow; } } int MediaPlayListProxyModel::mapRowFromSource(const int sourceRow) const { if (d->mShufflePlayList) { return d->mRandomMapping.indexOf(sourceRow); } else { return sourceRow; } } int MediaPlayListProxyModel::rowCount(const QModelIndex &parent) const { if (d->mShufflePlayList) { if (parent.isValid()) { return 0; } return d->mRandomMapping.count(); } else { return d->mPlayListModel->rowCount(parent); } } int MediaPlayListProxyModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 1; } QModelIndex MediaPlayListProxyModel::parent(const QModelIndex &child) const { Q_UNUSED(child); return QModelIndex(); } bool MediaPlayListProxyModel::hasChildren(const QModelIndex &parent) const { return (!parent.isValid()) ? false : (rowCount() > 0); } void MediaPlayListProxyModel::setPlayListModel(MediaPlayList *playListModel) { if (d->mPlayListModel) { disconnect(playListModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &MediaPlayListProxyModel::sourceRowsAboutToBeInserted); disconnect(playListModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &MediaPlayListProxyModel::sourceRowsAboutToBeRemoved); disconnect(playListModel, &QAbstractItemModel::rowsAboutToBeMoved, this, &MediaPlayListProxyModel::sourceRowsAboutToBeMoved); disconnect(playListModel, &QAbstractItemModel::rowsInserted, this, &MediaPlayListProxyModel::sourceRowsInserted); disconnect(playListModel, &QAbstractItemModel::rowsRemoved, this, &MediaPlayListProxyModel::sourceRowsRemoved); disconnect(playListModel, &QAbstractItemModel::rowsMoved, this, &MediaPlayListProxyModel::sourceRowsMoved); disconnect(playListModel, &QAbstractItemModel::dataChanged, this, &MediaPlayListProxyModel::sourceDataChanged); disconnect(playListModel, &QAbstractItemModel::headerDataChanged, this, &MediaPlayListProxyModel::sourceHeaderDataChanged); disconnect(playListModel, &QAbstractItemModel::layoutAboutToBeChanged, this, &MediaPlayListProxyModel::sourceLayoutAboutToBeChanged); disconnect(playListModel, &QAbstractItemModel::layoutChanged, this, &MediaPlayListProxyModel::sourceLayoutChanged); disconnect(playListModel, &QAbstractItemModel::modelAboutToBeReset, this, &MediaPlayListProxyModel::sourceModelAboutToBeReset); disconnect(playListModel, &QAbstractItemModel::modelReset, this, &MediaPlayListProxyModel::sourceModelReset); } d->mPlayListModel = playListModel; setSourceModel(playListModel); if (d->mPlayListModel) { connect(playListModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &MediaPlayListProxyModel::sourceRowsAboutToBeInserted); connect(playListModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &MediaPlayListProxyModel::sourceRowsAboutToBeRemoved); connect(playListModel, &QAbstractItemModel::rowsAboutToBeMoved, this, &MediaPlayListProxyModel::sourceRowsAboutToBeMoved); connect(playListModel, &QAbstractItemModel::rowsInserted, this, &MediaPlayListProxyModel::sourceRowsInserted); connect(playListModel, &QAbstractItemModel::rowsRemoved, this, &MediaPlayListProxyModel::sourceRowsRemoved); connect(playListModel, &QAbstractItemModel::rowsMoved, this, &MediaPlayListProxyModel::sourceRowsMoved); connect(playListModel, &QAbstractItemModel::dataChanged, this, &MediaPlayListProxyModel::sourceDataChanged); connect(playListModel, &QAbstractItemModel::headerDataChanged, this, &MediaPlayListProxyModel::sourceHeaderDataChanged); connect(playListModel, &QAbstractItemModel::layoutAboutToBeChanged, this, &MediaPlayListProxyModel::sourceLayoutAboutToBeChanged); connect(playListModel, &QAbstractItemModel::layoutChanged, this, &MediaPlayListProxyModel::sourceLayoutChanged); connect(playListModel, &QAbstractItemModel::modelAboutToBeReset, this, &MediaPlayListProxyModel::sourceModelAboutToBeReset); connect(playListModel, &QAbstractItemModel::modelReset, this, &MediaPlayListProxyModel::sourceModelReset); } } void MediaPlayListProxyModel::setSourceModel(QAbstractItemModel *sourceModel) { QAbstractProxyModel::setSourceModel(sourceModel); } QPersistentModelIndex MediaPlayListProxyModel::previousTrack() const { return d->mPreviousTrack; } QPersistentModelIndex MediaPlayListProxyModel::currentTrack() const { return d->mCurrentTrack; } QPersistentModelIndex MediaPlayListProxyModel::nextTrack() const { return d->mNextTrack; } void MediaPlayListProxyModel::setRepeatPlay(const bool value) { if (d->mRepeatPlay != value) { d->mRepeatPlay = value; Q_EMIT repeatPlayChanged(); Q_EMIT remainingTracksChanged(); Q_EMIT persistentStateChanged(); determineAndNotifyPreviousAndNextTracks(); } } bool MediaPlayListProxyModel::repeatPlay() const { return d->mRepeatPlay; } void MediaPlayListProxyModel::setShufflePlayList(const bool value) { if (d->mShufflePlayList != value) { Q_EMIT layoutAboutToBeChanged(QList(), QAbstractItemModel::VerticalSortHint); auto playListSize = d->mPlayListModel->rowCount(); if (playListSize != 0) { if (value) { d->mRandomMapping.clear(); d->mRandomMapping.reserve(playListSize); QModelIndexList to; to.reserve(playListSize); for (int i = 0; i < playListSize; ++i) { to.append(index(i,0)); d->mRandomMapping.append(i); } QModelIndexList from; from.reserve(playListSize); // Fisher-Yates algorithm for (int i = 0; i < playListSize - 1; ++i) { const int swapIndex = d->mRandomGenerator.bounded(i, playListSize); std::swap(d->mRandomMapping[i], d->mRandomMapping[swapIndex]); from.append(index(d->mRandomMapping.at(i), 0)); } from.append(index(d->mRandomMapping.at(playListSize - 1), 0)); changePersistentIndexList(from, to); } else { QModelIndexList from; from.reserve(playListSize); QModelIndexList to; to.reserve(playListSize); for (int i = 0; i < playListSize; ++i) { to.append(index(d->mRandomMapping.at(i), 0)); from.append(index(i, 0)); } changePersistentIndexList(from, to); d->mRandomMapping.clear(); } d->mCurrentPlayListPosition = d->mCurrentTrack.row(); d->mShufflePlayList = value; Q_EMIT layoutChanged(QList(), QAbstractItemModel::VerticalSortHint); determineAndNotifyPreviousAndNextTracks(); } else { d->mShufflePlayList = value; } Q_EMIT shufflePlayListChanged(); Q_EMIT remainingTracksChanged(); Q_EMIT persistentStateChanged(); } } bool MediaPlayListProxyModel::shufflePlayList() const { return d->mShufflePlayList; } void MediaPlayListProxyModel::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end) { /* * When in random mode, rows are only inserted after * the source model is done inserting new items since * new items can be added at arbitrarily positions, * which requires a split of beginInsertRows */ if (!d->mShufflePlayList) { beginInsertRows(parent, start, end); } } void MediaPlayListProxyModel::sourceRowsInserted(const QModelIndex &parent, int start, int end) { if (d->mShufflePlayList) { const auto newItemsCount = end - start + 1; d->mRandomMapping.reserve(rowCount() + newItemsCount); if (rowCount() == 0 || newItemsCount == 1) { beginInsertRows(parent, start, end); for (int i = 0; i < newItemsCount; ++i) { //QRandomGenerator.bounded(int) is exclusive, thus + 1 const auto random = d->mRandomGenerator.bounded(d->mRandomMapping.count()+1); d->mRandomMapping.insert(random, start + i); } endInsertRows(); } else { for (int i = 0; i < newItemsCount; ++i) { //QRandomGenerator.bounded(int) is exclusive, thus + 1 const auto random = d->mRandomGenerator.bounded(d->mRandomMapping.count()+1); beginInsertRows(parent, random, random); d->mRandomMapping.insert(random, start + i); endInsertRows(); } } } else { endInsertRows(); } determineTracks(); Q_EMIT tracksCountChanged(); Q_EMIT remainingTracksChanged(); Q_EMIT persistentStateChanged(); } void MediaPlayListProxyModel::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { if (d->mShufflePlayList) { if (end - start + 1 == rowCount()) { beginRemoveRows(parent, start, end); d->mRandomMapping.clear(); endRemoveRows(); } int row = 0; auto it = d->mRandomMapping.begin(); while (it != d->mRandomMapping.end()) { if (*it >= start && *it <= end){ beginRemoveRows(parent, row, row); it = d->mRandomMapping.erase(it); endRemoveRows(); } else { if (*it > end) { *it = *it - end + start - 1; } it++; row++; } } } else { beginRemoveRows(parent, start, end); } } void MediaPlayListProxyModel::sourceRowsRemoved(const QModelIndex &parent, int start, int end) { Q_UNUSED(parent); Q_UNUSED(start); Q_UNUSED(end); if (!d->mShufflePlayList) { endRemoveRows(); } if (!d->mCurrentTrack.isValid()) { d->mCurrentTrack = index(d->mCurrentPlayListPosition, 0); if (d->mCurrentTrack.isValid()) { notifyCurrentTrackChanged(); } if (!d->mCurrentTrack.isValid()) { Q_EMIT playListFinished(); determineTracks(); if (!d->mCurrentTrack.isValid()) { notifyCurrentTrackChanged(); } } } if (!d->mNextTrack.isValid() || !d->mPreviousTrack.isValid()) { determineAndNotifyPreviousAndNextTracks(); } Q_EMIT tracksCountChanged(); Q_EMIT remainingTracksChanged(); Q_EMIT persistentStateChanged(); } void MediaPlayListProxyModel::sourceRowsAboutToBeMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destParent, int destRow) { Q_ASSERT(!d->mShufflePlayList); beginMoveRows(parent, start, end, destParent, destRow); } void MediaPlayListProxyModel::sourceRowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destParent, int destRow) { Q_ASSERT(!d->mShufflePlayList); Q_UNUSED(parent); Q_UNUSED(start); Q_UNUSED(end); Q_UNUSED(destParent); Q_UNUSED(destRow); endMoveRows(); Q_EMIT remainingTracksChanged(); Q_EMIT persistentStateChanged(); } void MediaPlayListProxyModel::sourceModelAboutToBeReset() { beginResetModel(); } void MediaPlayListProxyModel::sourceModelReset() { endResetModel(); } void MediaPlayListProxyModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { auto startSourceRow = topLeft.row(); auto endSourceRow = bottomRight.row(); for (int i = startSourceRow; i <= endSourceRow; i++) { Q_EMIT dataChanged(index(mapRowFromSource(i), 0), index(mapRowFromSource(i), 0), roles); if (i == d->mCurrentTrack.row()) { Q_EMIT currentTrackDataChanged(); } else if (i == d->mNextTrack.row()) { Q_EMIT nextTrackDataChanged(); } else if (i == d->mPreviousTrack.row()) { Q_EMIT previousTrackDataChanged(); } determineTracks(); } } void MediaPlayListProxyModel::sourceLayoutAboutToBeChanged() { Q_EMIT layoutAboutToBeChanged(); } void MediaPlayListProxyModel::sourceLayoutChanged() { Q_EMIT layoutChanged(); } void MediaPlayListProxyModel::sourceHeaderDataChanged(Qt::Orientation orientation, int first, int last) { Q_EMIT headerDataChanged(orientation, first, last); } int MediaPlayListProxyModel::remainingTracks() const { if (!d->mCurrentTrack.isValid() || d->mRepeatPlay) { return -1; } else { return rowCount() - d->mCurrentTrack.row() - 1; } } int MediaPlayListProxyModel::tracksCount() const { return rowCount(); } int MediaPlayListProxyModel::currentTrackRow() const { return d->mCurrentTrack.row(); } void MediaPlayListProxyModel::enqueue(const DataTypes::MusicDataType &newEntry, const QString &newEntryTitle, ElisaUtils::PlayListEnqueueMode enqueueMode, ElisaUtils::PlayListEnqueueTriggerPlay triggerPlay) { enqueue({{newEntry, newEntryTitle, {}}}, enqueueMode, triggerPlay); } void MediaPlayListProxyModel::enqueue(const QUrl &entryUrl, ElisaUtils::PlayListEnqueueMode enqueueMode, ElisaUtils::PlayListEnqueueTriggerPlay triggerPlay) { enqueue({{{{DataTypes::ElementTypeRole, ElisaUtils::Track}}, {}, entryUrl}}, enqueueMode, triggerPlay); } void MediaPlayListProxyModel::enqueue(const DataTypes::EntryDataList &newEntries, ElisaUtils::PlayListEnqueueMode enqueueMode, ElisaUtils::PlayListEnqueueTriggerPlay triggerPlay) { if (newEntries.isEmpty()) { return; } d->mTriggerPlay = triggerPlay; if (enqueueMode == ElisaUtils::ReplacePlayList) { if (rowCount() == 0) { Q_EMIT hideUndoNotification(); } else { clearPlayList(); } } d->mPlayListModel->enqueueMultipleEntries(newEntries); } void MediaPlayListProxyModel::trackInError(const QUrl &sourceInError, QMediaPlayer::Error playerError) { d->mPlayListModel->trackInError(sourceInError, playerError); } void MediaPlayListProxyModel::skipNextTrack() { if (!d->mCurrentTrack.isValid()) { return; } if (d->mCurrentTrack.row() >= rowCount() - 1) { d->mCurrentTrack = index(0, 0); if (!d->mRepeatPlay) { Q_EMIT playListFinished(); } } else { d->mCurrentTrack = index(d->mCurrentTrack.row() + 1, 0); } notifyCurrentTrackChanged(); } void MediaPlayListProxyModel::skipPreviousTrack() { if (!d->mCurrentTrack.isValid()) { return; } if (d->mCurrentTrack.row() == 0) { if (d->mRepeatPlay) { d->mCurrentTrack = index(rowCount() - 1, 0); } else { return; } } else { d->mCurrentTrack = index(d->mCurrentTrack.row() - 1, 0); } notifyCurrentTrackChanged(); } void MediaPlayListProxyModel::switchTo(int row) { if (!d->mCurrentTrack.isValid()) { return; } d->mCurrentTrack = index(row, 0); notifyCurrentTrackChanged(); } void MediaPlayListProxyModel::removeSelection(QList selection) { std::sort(selection.begin(), selection.end()); std::reverse(selection.begin(), selection.end()); for (auto oneItem : selection) { removeRow(oneItem); } } void MediaPlayListProxyModel::removeRow(int row) { d->mPlayListModel->removeRows(mapRowToSource(row), 1); } void MediaPlayListProxyModel::moveRow(int from, int to) { if (d->mShufflePlayList) { beginMoveRows({}, from, from, {}, from < to ? to + 1 : to); d->mRandomMapping.move(from, to); endMoveRows(); } else { d->mPlayListModel->moveRows({}, from, 1, {}, from < to ? to + 1 : to); } } void MediaPlayListProxyModel::notifyCurrentTrackChanged() { if (d->mCurrentTrack.isValid()) { d->mCurrentPlayListPosition = d->mCurrentTrack.row(); } else { d->mCurrentPlayListPosition = -1; } determineAndNotifyPreviousAndNextTracks(); Q_EMIT currentTrackChanged(d->mCurrentTrack); Q_EMIT currentTrackRowChanged(); Q_EMIT remainingTracksChanged(); } void MediaPlayListProxyModel::determineAndNotifyPreviousAndNextTracks() { if (!d->mCurrentTrack.isValid()) { d->mPreviousTrack = QPersistentModelIndex(); d->mNextTrack = QPersistentModelIndex(); } auto mOldPreviousTrack = d->mPreviousTrack; auto mOldNextTrack = d->mNextTrack; if (d->mRepeatPlay) { // forward to end or begin when repeating if (d->mCurrentTrack.row() == 0) { d->mPreviousTrack = index(rowCount() - 1, 0); } else { d->mPreviousTrack = index(d->mCurrentTrack.row() - 1, 0); } if (d->mCurrentTrack.row() == rowCount() - 1) { d->mNextTrack = index(0, 0); } else { d->mNextTrack = index(d->mCurrentTrack.row() + 1, 0); } } else { // return nothing if no tracks available if (d->mCurrentTrack.row() == 0) { d->mPreviousTrack = QPersistentModelIndex(); } else { d->mPreviousTrack = index(d->mCurrentTrack.row() - 1, 0); } if (d->mCurrentTrack.row() == rowCount() - 1) { d->mNextTrack = QPersistentModelIndex(); } else { d->mNextTrack = index(d->mCurrentTrack.row() + 1, 0); } } if (d->mPreviousTrack != mOldPreviousTrack) { Q_EMIT previousTrackChanged(d->mPreviousTrack); } if (d->mNextTrack != mOldNextTrack) { Q_EMIT nextTrackChanged(d->mNextTrack); } } void MediaPlayListProxyModel::clearPlayList() { if (rowCount() == 0) { return; } d->mPersistentSettingsForUndo = persistentState(); d->mCurrentPlayListPosition = -1; d->mCurrentTrack = QPersistentModelIndex{}; d->mPlayListModel->clearPlayList(); Q_EMIT clearPlayListPlayer(); Q_EMIT displayUndoNotification(); } void MediaPlayListProxyModel::undoClearPlayList() { d->mPlayListModel->clearPlayList(); setPersistentState(d->mPersistentSettingsForUndo); Q_EMIT hideUndoNotification(); Q_EMIT undoClearPlayListPlayer(); } void MediaPlayListProxyModel::determineTracks() { if (!d->mCurrentTrack.isValid()) { for (int row = 0; row < rowCount(); ++row) { auto candidateTrack = index(row, 0); const auto type = candidateTrack.data(MediaPlayList::ElementTypeRole).value(); if (candidateTrack.isValid() && candidateTrack.data(MediaPlayList::IsValidRole).toBool() && (type == ElisaUtils::Track || type == ElisaUtils::Radio || type == ElisaUtils::FileName)) { d->mCurrentTrack = candidateTrack; notifyCurrentTrackChanged(); if (d->mTriggerPlay == ElisaUtils::TriggerPlay) { d->mTriggerPlay = ElisaUtils::DoNotTriggerPlay; Q_EMIT ensurePlay(); } break; } } } if (!d->mNextTrack.isValid() || !d->mPreviousTrack.isValid()) { determineAndNotifyPreviousAndNextTracks(); } } bool MediaPlayListProxyModel::savePlayList(const QUrl &fileName) { QMediaPlaylist savePlaylist; for (int i = 0; i < rowCount(); ++i) { if (data(index(i,0), MediaPlayList::IsValidRole).toBool()) { data(index(i,0), MediaPlayList::ResourceRole).toUrl(); savePlaylist.addMedia(data(index(i,0), MediaPlayList::ResourceRole).toUrl()); } } return savePlaylist.save(fileName, "m3u"); } void MediaPlayListProxyModel::loadPlayList(const QUrl &fileName) { d->mLoadPlaylist.clear(); d->mLoadPlaylist.load(fileName, "m3u"); } void MediaPlayListProxyModel::loadPlayListLoaded() { clearPlayList(); auto newTracks = DataTypes::EntryDataList{}; for (int i = 0; i < d->mLoadPlaylist.mediaCount(); ++i) { #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) newTracks.push_back({{{{DataTypes::ElementTypeRole, ElisaUtils::FileName}}}, {}, d->mLoadPlaylist.media(i).canonicalUrl()}); #else newTracks.push_back({{{{DataTypes::ElementTypeRole, ElisaUtils::FileName}}}, {}, d->mLoadPlaylist.media(i).request().url()}); #endif } enqueue(newTracks, ElisaUtils::ReplacePlayList, ElisaUtils::DoNotTriggerPlay); Q_EMIT persistentStateChanged(); d->mLoadPlaylist.clear(); Q_EMIT playListLoaded(); } void MediaPlayListProxyModel::loadPlayListLoadFailed() { d->mLoadPlaylist.clear(); Q_EMIT playListLoadFailed(); } QVariantMap MediaPlayListProxyModel::persistentState() const { QVariantMap currentState; currentState[QStringLiteral("playList")] = d->mPlayListModel->getEntriesForRestore(); currentState[QStringLiteral("currentTrack")] = d->mCurrentPlayListPosition; currentState[QStringLiteral("shufflePlayList")] = d->mShufflePlayList; currentState[QStringLiteral("repeatPlay")] = d->mRepeatPlay; return currentState; } void MediaPlayListProxyModel::setPersistentState(const QVariantMap &persistentStateValue) { qCDebug(orgKdeElisaPlayList()) << "MediaPlayListProxyModel::setPersistentState" << persistentStateValue; auto playListIt = persistentStateValue.find(QStringLiteral("playList")); if (playListIt != persistentStateValue.end()) { d->mPlayListModel->enqueueRestoredEntries(playListIt.value().toList()); } auto playerCurrentTrack = persistentStateValue.find(QStringLiteral("currentTrack")); if (playerCurrentTrack != persistentStateValue.end()) { auto newIndex = index(playerCurrentTrack->toInt(), 0); if (newIndex.isValid() && (newIndex != d->mCurrentTrack)) { d->mCurrentTrack = newIndex; notifyCurrentTrackChanged(); } } auto shufflePlayListStoredValue = persistentStateValue.find(QStringLiteral("shufflePlayList")); if (shufflePlayListStoredValue != persistentStateValue.end()) { setShufflePlayList(shufflePlayListStoredValue->toBool()); } auto repeatPlayStoredValue = persistentStateValue.find(QStringLiteral("repeatPlay")); if (repeatPlayStoredValue != persistentStateValue.end()) { setRepeatPlay(repeatPlayStoredValue->toBool()); } Q_EMIT persistentStateChanged(); } void MediaPlayListProxyModel::enqueueDirectory(const QUrl &fileName, ElisaUtils::PlayListEntryType databaseIdType, ElisaUtils::PlayListEnqueueMode enqueueMode, ElisaUtils::PlayListEnqueueTriggerPlay triggerPlay, int depth) { if (!fileName.isLocalFile()) return; // clear playlist if required if (enqueueMode == ElisaUtils::ReplacePlayList) { if (rowCount() == 0) { Q_EMIT hideUndoNotification(); } else { clearPlayList(); } } // get contents of directory QDir dirInfo = QDir(fileName.toLocalFile()); auto files = dirInfo.entryInfoList(QDir::NoDotAndDotDot | QDir::Readable | QDir::Files | QDir::Dirs, QDir::Name); auto newFiles = DataTypes::EntryDataList(); - for (auto file : files) + for (const auto &file : files) { auto fileUrl = QUrl::fromLocalFile(file.filePath()); if (file.isFile() && d->mMimeDb.mimeTypeForUrl(fileUrl).name().startsWith(QLatin1String("audio/"))) { newFiles.push_back({{{DataTypes::ElementTypeRole, ElisaUtils::Track}, {DataTypes::ResourceRole, fileUrl}},{}, {}}); } else if (file.isDir() && depth > 1) { // recurse through directory enqueueDirectory(fileUrl, databaseIdType, ElisaUtils::AppendPlayList, triggerPlay, depth-1); } } if (newFiles.size() != 0) enqueue(newFiles, ElisaUtils::AppendPlayList, triggerPlay); } #include "moc_mediaplaylistproxymodel.cpp" diff --git a/src/viewslistdata.h b/src/viewslistdata.h index f68a272d..228c7c44 100644 --- a/src/viewslistdata.h +++ b/src/viewslistdata.h @@ -1,173 +1,173 @@ /* SPDX-FileCopyrightText: 2020 (c) Matthieu Gallien SPDX-License-Identifier: LGPL-3.0-or-later */ #ifndef VIEWSLISTDATA_H #define VIEWSLISTDATA_H #include "elisaLib_export.h" #include #include "viewmanager.h" #include "datatypes.h" #include "elisautils.h" #include class QUrl; class QString; class ViewsListDataPrivate; class ViewParameters { public: ViewParameters() = default; ViewParameters(QString mainTitle, QUrl mainImage, ViewManager::ViewPresentationType viewPresentationType) : mMainTitle(std::move(mainTitle)) , mMainImage(std::move(mainImage)) , mViewPresentationType(viewPresentationType) , mIsValid(true) { } ViewParameters(QString mainTitle, QUrl mainImage, ViewManager::ViewPresentationType viewPresentationType, ElisaUtils::FilterType filterType, ElisaUtils::PlayListEntryType dataType, QUrl fallbackItemIcon, ViewManager::DelegateUseSecondaryText showSecondaryTextOnDelegates, ViewManager::ViewCanBeRated viewCanBeRated) : mMainTitle(std::move(mainTitle)) , mMainImage(std::move(mainImage)) , mViewPresentationType(viewPresentationType) , mFilterType(filterType) , mDataType(dataType) - , mFallbackItemIcon(fallbackItemIcon) + , mFallbackItemIcon(std::move(fallbackItemIcon)) , mShowSecondaryTextOnDelegates(showSecondaryTextOnDelegates) , mViewCanBeRated(viewCanBeRated) , mIsValid(true) { } ViewParameters(QString mainTitle, QUrl mainImage, ViewManager::ViewPresentationType viewPresentationType, ElisaUtils::FilterType filterType, ElisaUtils::PlayListEntryType dataType, int sortRole, ViewManager::SortOrder sortOrder, ViewManager::AlbumCardinality albumCardinality, ViewManager::AlbumViewStyle albumViewStyle, ViewManager::RadioSpecificStyle radioSpecificStyle) : mMainTitle(std::move(mainTitle)) , mMainImage(std::move(mainImage)) , mViewPresentationType(viewPresentationType) , mFilterType(filterType) , mDataType(dataType) , mSortRole(sortRole) , mSortOrder(sortOrder) , mAlbumCardinality(albumCardinality) , mAlbumViewStyle(albumViewStyle) , mRadioSpecificStyle(radioSpecificStyle) , mIsValid(true) { } bool operator==(const ViewParameters &other) const { return mMainTitle == other.mMainTitle && mMainImage == other.mMainImage && mSecondaryTitle == other.mSecondaryTitle && mViewPresentationType == other.mViewPresentationType && mFilterType == other.mFilterType && mDataType == other.mDataType && mFallbackItemIcon == other.mFallbackItemIcon && mShowSecondaryTextOnDelegates == other.mShowSecondaryTextOnDelegates && mViewCanBeRated == other.mViewCanBeRated && mSortRole == other.mSortRole && mSortOrder == other.mSortOrder && mAlbumCardinality == other.mAlbumCardinality && mAlbumViewStyle == other.mAlbumViewStyle && mRadioSpecificStyle == other.mRadioSpecificStyle && mDepth == other.mDepth && mDatabaseIdFilter == other.mDatabaseIdFilter && mGenreNameFilter == other.mGenreNameFilter && mArtistNameFilter == other.mArtistNameFilter; } bool operator!=(const ViewParameters &other) const { return mMainTitle != other.mMainTitle || mMainImage != other.mMainImage || mSecondaryTitle != other.mSecondaryTitle || mViewPresentationType != other.mViewPresentationType || mFilterType != other.mFilterType || mDataType != other.mDataType || mFallbackItemIcon != other.mFallbackItemIcon || mShowSecondaryTextOnDelegates != other.mShowSecondaryTextOnDelegates || mViewCanBeRated != other.mViewCanBeRated || mSortRole != other.mSortRole || mSortOrder != other.mSortOrder || mAlbumCardinality != other.mAlbumCardinality || mAlbumViewStyle != other.mAlbumViewStyle || mRadioSpecificStyle != other.mRadioSpecificStyle || mDepth != other.mDepth || mDatabaseIdFilter != other.mDatabaseIdFilter || mGenreNameFilter != other.mGenreNameFilter || mArtistNameFilter != other.mArtistNameFilter; } QString mMainTitle; QString mSecondaryTitle; QUrl mMainImage; ViewManager::ViewPresentationType mViewPresentationType = ViewManager::UnknownViewPresentation; ElisaUtils::FilterType mFilterType = ElisaUtils::UnknownFilter; ElisaUtils::PlayListEntryType mDataType = ElisaUtils::Unknown; QUrl mFallbackItemIcon; ViewManager::DelegateUseSecondaryText mShowSecondaryTextOnDelegates = ViewManager::DelegateWithSecondaryText; ViewManager::ViewCanBeRated mViewCanBeRated = ViewManager::ViewHideRating; int mSortRole = Qt::DisplayRole; ViewManager::SortOrder mSortOrder = ViewManager::SortAscending; ViewManager::AlbumCardinality mAlbumCardinality = ViewManager::MultipleAlbum; ViewManager::AlbumViewStyle mAlbumViewStyle = ViewManager::NoDiscHeaders; ViewManager::RadioSpecificStyle mRadioSpecificStyle = ViewManager::IsTrack; int mDepth = 1; qulonglong mDatabaseIdFilter = 0; QString mGenreNameFilter; QString mArtistNameFilter; bool mIsValid = false; }; class ELISALIB_EXPORT ViewsListData : public QObject { Q_OBJECT public: explicit ViewsListData(QObject *parent = nullptr); ~ViewsListData(); int count() const; const ViewParameters& viewParameters(int index) const; const QString& title(int index) const; const QUrl& iconUrl(int index) const; Q_SIGNALS: public Q_SLOTS: private: std::unique_ptr d; }; #endif // VIEWSLISTDATA_H