-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
widget: same-folder playlist support
- Loading branch information
Showing
9 changed files
with
458 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,253 @@ | ||
// SPDX-FileCopyrightText: 2024 Gary Wang <[email protected]> | ||
// | ||
// SPDX-License-Identifier: MIT | ||
|
||
#include "playlistmanager.h" | ||
|
||
#include <QCollator> | ||
#include <QDir> | ||
#include <QFileInfo> | ||
#include <QUrl> | ||
|
||
PlaylistModel::PlaylistModel(QObject *parent) | ||
: QAbstractListModel(parent) | ||
{ | ||
|
||
} | ||
|
||
PlaylistModel::~PlaylistModel() | ||
{ | ||
|
||
} | ||
|
||
void PlaylistModel::setPlaylist(const QList<QUrl> &urls) | ||
{ | ||
beginResetModel(); | ||
m_playlist = urls; | ||
endResetModel(); | ||
} | ||
|
||
QModelIndex PlaylistModel::loadPlaylist(const QList<QUrl> & urls) | ||
{ | ||
if (urls.isEmpty()) return QModelIndex(); | ||
if (urls.count() == 1) { | ||
return loadPlaylist(urls.constFirst()); | ||
} else { | ||
setPlaylist(urls); | ||
return createIndex(0); | ||
} | ||
} | ||
|
||
QModelIndex PlaylistModel::loadPlaylist(const QUrl &url) | ||
{ | ||
QFileInfo info(url.toLocalFile()); | ||
QDir dir(info.path()); | ||
QString && currentFileName = info.fileName(); | ||
|
||
if (dir.path() == m_currentDir) { | ||
int index = indexOf(url); | ||
return index == -1 ? appendToPlaylist(url) : createIndex(index); | ||
} | ||
|
||
QStringList entryList = dir.entryList( | ||
m_autoLoadSuffixes, | ||
QDir::Files | QDir::NoSymLinks, QDir::NoSort); | ||
|
||
QCollator collator; | ||
collator.setNumericMode(true); | ||
|
||
std::sort(entryList.begin(), entryList.end(), collator); | ||
|
||
QList<QUrl> playlist; | ||
|
||
int index = -1; | ||
for (int i = 0; i < entryList.count(); i++) { | ||
const QString & fileName = entryList.at(i); | ||
const QString & oneEntry = dir.absoluteFilePath(fileName); | ||
const QUrl & url = QUrl::fromLocalFile(oneEntry); | ||
playlist.append(url); | ||
if (fileName == currentFileName) { | ||
index = i; | ||
} | ||
} | ||
if (index == -1) { | ||
index = playlist.count(); | ||
playlist.append(url); | ||
} | ||
m_currentDir = dir.path(); | ||
|
||
setPlaylist(playlist); | ||
|
||
return createIndex(index); | ||
} | ||
|
||
QModelIndex PlaylistModel::appendToPlaylist(const QUrl &url) | ||
{ | ||
const int lastIndex = rowCount(); | ||
beginInsertRows(QModelIndex(), lastIndex, lastIndex); | ||
m_playlist.append(url); | ||
endInsertRows(); | ||
return createIndex(lastIndex); | ||
} | ||
|
||
bool PlaylistModel::removeAt(int index) | ||
{ | ||
if (index < 0 || index >= rowCount()) return false; | ||
beginRemoveRows(QModelIndex(), index, index); | ||
m_playlist.removeAt(index); | ||
endRemoveRows(); | ||
return true; | ||
} | ||
|
||
int PlaylistModel::indexOf(const QUrl &url) const | ||
{ | ||
return m_playlist.indexOf(url); | ||
} | ||
|
||
QUrl PlaylistModel::urlByIndex(int index) const | ||
{ | ||
return m_playlist.value(index); | ||
} | ||
|
||
QStringList PlaylistModel::autoLoadFilterSuffixes() const | ||
{ | ||
return m_autoLoadSuffixes; | ||
} | ||
|
||
QModelIndex PlaylistModel::createIndex(int row) const | ||
{ | ||
return QAbstractItemModel::createIndex(row, 0, nullptr); | ||
} | ||
|
||
int PlaylistModel::rowCount(const QModelIndex &parent) const | ||
{ | ||
return m_playlist.count(); | ||
} | ||
|
||
QVariant PlaylistModel::data(const QModelIndex &index, int role) const | ||
{ | ||
if (!index.isValid()) return QVariant(); | ||
|
||
switch (role) { | ||
case Qt::DisplayRole: | ||
return m_playlist.at(index.row()).fileName(); | ||
case UrlRole: | ||
return m_playlist.at(index.row()); | ||
} | ||
|
||
return QVariant(); | ||
} | ||
|
||
PlaylistManager::PlaylistManager(QObject *parent) | ||
: QObject(parent) | ||
{ | ||
connect(&m_model, &PlaylistModel::rowsRemoved, this, | ||
[this](const QModelIndex &, int, int) { | ||
if (m_model.rowCount() <= m_currentIndex) { | ||
setProperty("currentIndex", m_currentIndex - 1); | ||
} | ||
}); | ||
|
||
auto onRowCountChanged = [this](){ | ||
emit totalCountChanged(m_model.rowCount()); | ||
}; | ||
|
||
connect(&m_model, &PlaylistModel::rowsInserted, this, onRowCountChanged); | ||
connect(&m_model, &PlaylistModel::rowsRemoved, this, onRowCountChanged); | ||
connect(&m_model, &PlaylistModel::modelReset, this, onRowCountChanged); | ||
} | ||
|
||
PlaylistManager::~PlaylistManager() | ||
{ | ||
|
||
} | ||
|
||
PlaylistModel *PlaylistManager::model() | ||
{ | ||
return &m_model; | ||
} | ||
|
||
void PlaylistManager::setPlaylist(const QList<QUrl> &urls) | ||
{ | ||
m_model.setPlaylist(urls); | ||
} | ||
|
||
QModelIndex PlaylistManager::loadPlaylist(const QList<QUrl> &urls) | ||
{ | ||
QModelIndex idx = m_model.loadPlaylist(urls); | ||
setProperty("currentIndex", idx.row()); | ||
return idx; | ||
} | ||
|
||
QModelIndex PlaylistManager::loadPlaylist(const QUrl &url) | ||
{ | ||
QModelIndex idx = m_model.loadPlaylist(url); | ||
setProperty("currentIndex", idx.row()); | ||
return idx; | ||
} | ||
|
||
int PlaylistManager::totalCount() const | ||
{ | ||
return m_model.rowCount(); | ||
} | ||
|
||
QModelIndex PlaylistManager::previousIndex() const | ||
{ | ||
int count = totalCount(); | ||
if (count == 0) return QModelIndex(); | ||
|
||
return m_model.createIndex(m_currentIndex - 1 < 0 ? count - 1 : m_currentIndex - 1); | ||
} | ||
|
||
QModelIndex PlaylistManager::nextIndex() const | ||
{ | ||
int count = totalCount(); | ||
if (count == 0) return QModelIndex(); | ||
|
||
return m_model.createIndex(m_currentIndex + 1 == count ? 0 : m_currentIndex + 1); | ||
} | ||
|
||
QModelIndex PlaylistManager::curIndex() const | ||
{ | ||
return m_model.createIndex(m_currentIndex); | ||
} | ||
|
||
void PlaylistManager::setCurrentIndex(const QModelIndex &index) | ||
{ | ||
if (index.isValid() && index.row() >= 0 && index.row() < totalCount()) { | ||
setProperty("currentIndex", index.row()); | ||
} | ||
} | ||
|
||
QUrl PlaylistManager::urlByIndex(const QModelIndex &index) | ||
{ | ||
return m_model.urlByIndex(index.row()); | ||
} | ||
|
||
QString PlaylistManager::localFileByIndex(const QModelIndex &index) | ||
{ | ||
return urlByIndex(index).toLocalFile(); | ||
} | ||
|
||
bool PlaylistManager::removeAt(const QModelIndex &index) | ||
{ | ||
return m_model.removeAt(index.row()); | ||
} | ||
|
||
void PlaylistManager::setAutoLoadFilterSuffixes(const QStringList &nameFilters) | ||
{ | ||
m_model.setProperty("autoLoadFilterSuffixes", nameFilters); | ||
} | ||
|
||
QList<QUrl> PlaylistManager::convertToUrlList(const QStringList &files) | ||
{ | ||
QList<QUrl> urlList; | ||
for (const QString & str : std::as_const(files)) { | ||
QUrl url = QUrl::fromLocalFile(str); | ||
if (url.isValid()) { | ||
urlList.append(url); | ||
} | ||
} | ||
|
||
return urlList; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// SPDX-FileCopyrightText: 2024 Gary Wang <[email protected]> | ||
// | ||
// SPDX-License-Identifier: MIT | ||
|
||
#pragma once | ||
|
||
#include <QAbstractListModel> | ||
|
||
class PlaylistModel : public QAbstractListModel | ||
{ | ||
Q_OBJECT | ||
public: | ||
enum PlaylistRole { | ||
UrlRole = Qt::UserRole | ||
}; | ||
Q_ENUM(PlaylistRole) | ||
Q_PROPERTY(QStringList autoLoadFilterSuffixes MEMBER m_autoLoadSuffixes NOTIFY autoLoadFilterSuffixesChanged) | ||
|
||
explicit PlaylistModel(QObject *parent = nullptr); | ||
~PlaylistModel(); | ||
|
||
void setPlaylist(const QList<QUrl> & urls); | ||
QModelIndex loadPlaylist(const QList<QUrl> & urls); | ||
QModelIndex loadPlaylist(const QUrl & url); | ||
QModelIndex appendToPlaylist(const QUrl & url); | ||
bool removeAt(int index); | ||
int indexOf(const QUrl & url) const; | ||
QUrl urlByIndex(int index) const; | ||
QStringList autoLoadFilterSuffixes() const; | ||
|
||
QModelIndex createIndex(int row) const; | ||
int rowCount(const QModelIndex &parent = QModelIndex()) const override; | ||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; | ||
|
||
signals: | ||
void autoLoadFilterSuffixesChanged(QStringList suffixes); | ||
|
||
private: | ||
// model data | ||
QList<QUrl> m_playlist; | ||
// properties | ||
QStringList m_autoLoadSuffixes = {}; | ||
// internal | ||
QString m_currentDir; | ||
}; | ||
|
||
class PlaylistManager : public QObject | ||
{ | ||
Q_OBJECT | ||
public: | ||
Q_PROPERTY(int currentIndex MEMBER m_currentIndex NOTIFY currentIndexChanged) | ||
|
||
explicit PlaylistManager(QObject *parent = nullptr); | ||
~PlaylistManager(); | ||
|
||
PlaylistModel * model(); | ||
|
||
void setPlaylist(const QList<QUrl> & url); | ||
QModelIndex loadPlaylist(const QList<QUrl> & urls); | ||
QModelIndex loadPlaylist(const QUrl & url); | ||
|
||
int totalCount() const; | ||
QModelIndex previousIndex() const; | ||
QModelIndex nextIndex() const; | ||
QModelIndex curIndex() const; | ||
void setCurrentIndex(const QModelIndex & index); | ||
QUrl urlByIndex(const QModelIndex & index); | ||
QString localFileByIndex(const QModelIndex & index); | ||
bool removeAt(const QModelIndex & index); | ||
|
||
void setAutoLoadFilterSuffixes(const QStringList &nameFilters); | ||
|
||
static QList<QUrl> convertToUrlList(const QStringList & files); | ||
|
||
signals: | ||
void currentIndexChanged(int index); | ||
void totalCountChanged(int count); | ||
|
||
private: | ||
int m_currentIndex; | ||
PlaylistModel m_model; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.