Files
cantata/models/browsemodel.cpp
2021-07-29 12:13:42 +01:00

336 lines
10 KiB
C++

/*
* Cantata
*
* Copyright (c) 2017-2021 Craig Drummond <craig.p.drummond@gmail.com>
*
* ----
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "browsemodel.h"
#include "roles.h"
#include "playqueuemodel.h"
#include "widgets/icons.h"
#include "gui/settings.h"
#include "mpd-interface/mpdconnection.h"
#include "mpd-interface/mpdstats.h"
#include "support/monoicon.h"
#include "support/utils.h"
#include <QMimeData>
void BrowseModel::FolderItem::add(Item *i)
{
i->setRow(children.count());
children.append(i);
}
QStringList BrowseModel::FolderItem::allEntries(bool allowPlaylists) const
{
QStringList entries;
if (children.isEmpty()) {
entries << MPDConnection::constDirPrefix+path;
} else {
for (Item *i: children) {
if (i->isFolder()) {
entries+=static_cast<FolderItem *>(i)->allEntries(allowPlaylists);
} else if (allowPlaylists || Song::Playlist!=static_cast<TrackItem *>(i)->getSong().type) {
entries+=static_cast<TrackItem *>(i)->getSong().file;
}
}
}
return entries;
}
BrowseModel::BrowseModel(QObject *p)
: ActionModel(p)
, root(new FolderItem("/", nullptr))
, enabled(false)
, dbVersion(0)
{
icn=MonoIcon::icon(FontAwesome::server, Utils::monoIconColor());
connect(this, SIGNAL(listFolder(QString)), MPDConnection::self(), SLOT(listFolder(QString)));
folderIndex.insert(root->getPath(), root);
}
QString BrowseModel::name() const
{
return QLatin1String("mpdbrowse");
}
QString BrowseModel::title() const
{
return tr("Server Folders");
}
QString BrowseModel::descr() const
{
return tr("MPD virtual file-system");
}
void BrowseModel::clear()
{
beginResetModel();
root->clear();
folderIndex.clear();
folderIndex.insert(root->getPath(), root);
endResetModel();
}
void BrowseModel::load()
{
if (!enabled || (root && (root->getChildCount() || root->isFetching()))) {
return;
}
root->setState(FolderItem::State_Fetching);
emit listFolder(root->getPath());
}
void BrowseModel::setEnabled(bool e)
{
if (e==enabled) {
return;
}
enabled=e;
if (enabled) {
connect(MPDConnection::self(), SIGNAL(folderContents(QString,QStringList,QList<Song>)), this, SLOT(folderContents(QString,QStringList,QList<Song>)));
connect(MPDConnection::self(), SIGNAL(connectionChanged(MPDConnectionDetails)), this, SLOT(connectionChanged()));
connect(MPDConnection::self(), SIGNAL(statsUpdated(MPDStatsValues)), this, SLOT(statsUpdated(MPDStatsValues)));
} else {
disconnect(MPDConnection::self(), SIGNAL(folderContents(QString,QStringList,QList<Song>)), this, SLOT(folderContents(QString,QStringList,QList<Song>)));
disconnect(MPDConnection::self(), SIGNAL(connectionChanged(MPDConnectionDetails)), this, SLOT(connectionChanged()));
disconnect(MPDConnection::self(), SIGNAL(statsUpdated(MPDStatsValues)), this, SLOT(statsUpdated(MPDStatsValues)));
clear();
}
}
Qt::ItemFlags BrowseModel::flags(const QModelIndex &index) const
{
if (index.isValid()) {
return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled;
}
return Qt::ItemIsDropEnabled;
}
QModelIndex BrowseModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent)) {
return QModelIndex();
}
const FolderItem * p = parent.isValid() ? static_cast<FolderItem *>(parent.internalPointer()) : root;
const Item * c = row<p->getChildCount() ? p->getChildren().at(row) : nullptr;
return c ? createIndex(row, column, (void *)c) : QModelIndex();
}
QModelIndex BrowseModel::parent(const QModelIndex &child) const
{
if (!child.isValid()) {
return QModelIndex();
}
const Item * const item = static_cast<Item *>(child.internalPointer());
Item * const parentItem = item->getParent();
if (parentItem == root || nullptr==parentItem) {
return QModelIndex();
}
return createIndex(parentItem->getRow(), 0, parentItem);
}
int BrowseModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() > 0) {
return 0;
}
const FolderItem *parentItem=parent.isValid() ? static_cast<FolderItem *>(parent.internalPointer()) : root;
return parentItem ? parentItem->getChildCount() : 0;
}
int BrowseModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 1;
}
bool BrowseModel::hasChildren(const QModelIndex &index) const
{
Item *item=toItem(index);
return item && item->isFolder();
}
bool BrowseModel::canFetchMore(const QModelIndex &index) const
{
if (index.isValid()) {
Item *item = toItem(index);
return item && item->isFolder() && static_cast<FolderItem *>(item)->canFetchMore();
} else {
return false;
}
}
void BrowseModel::fetchMore(const QModelIndex &index)
{
if (!index.isValid()) {
return;
}
FolderItem *item = static_cast<FolderItem *>(toItem(index));
if (!item->isFetching()) {
item->setState(FolderItem::State_Fetching);
emit listFolder(item->getPath());
}
}
QVariant BrowseModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
switch (role) {
case Cantata::Role_TitleText:
return title();
case Cantata::Role_SubText:
return descr();
case Qt::DecorationRole:
return icon();
}
return QVariant();
}
Item *item = static_cast<Item *>(index.internalPointer());
switch (role) {
case Qt::DecorationRole:
if (item->isFolder()) {
return Icons::self()->folderListIcon;
} else {
TrackItem *track = static_cast<TrackItem *>(item);
return Song::Playlist==track->getSong().type ? Icons::self()->playlistListIcon : Icons::self()->audioListIcon;
}
break;
case Cantata::Role_BriefMainText:
case Cantata::Role_MainText:
case Qt::DisplayRole:
if (!item->isFolder()) {
TrackItem *track = static_cast<TrackItem *>(item);
if (Song::Playlist==track->getSong().type) {
return Utils::getFile(track->getSong().file);
}
}
return item->getText();
case Qt::ToolTipRole:
if (!Settings::self()->infoTooltips()) {
return QVariant();
}
if (item->isFolder() || Song::Playlist==static_cast<TrackItem *>(item)->getSong().type) {
return static_cast<FolderItem *>(item)->getPath();
}
return static_cast<TrackItem *>(item)->getSong().toolTip();
case Cantata::Role_SubText:
if (!item->isFolder()) {
TrackItem *track = static_cast<TrackItem *>(item);
if (Song::Playlist==track->getSong().type) {
return track->getSong().isCueFile() ? tr("Cue Sheet") : tr("Playlist");
}
}
return item->getSubText();
case Cantata::Role_TitleText:
return item->getText();
case Cantata::Role_TitleActions:
return item->isFolder();
default:
break;
}
return ActionModel::data(index, role);
}
QMimeData * BrowseModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData *mimeData = new QMimeData();
QStringList files;
for (const QModelIndex &idx: indexes) {
Item *item=toItem(idx);
if (item) {
if (item->isFolder()) {
files+=static_cast<FolderItem *>(item)->allEntries(false);
} else {
files.append(static_cast<TrackItem *>(item)->getSong().file);
}
}
}
PlayQueueModel::encode(*mimeData, PlayQueueModel::constFileNameMimeType, files);
return mimeData;
}
QList<Song> BrowseModel::songs(const QModelIndexList &indexes, bool allowPlaylists) const
{
QList<Song> songList;
for (const QModelIndex &idx: indexes) {
Item *item=toItem(idx);
if (item && !item->isFolder() && (allowPlaylists || Song::Standard==static_cast<TrackItem *>(item)->getSong().type)) {
songList.append(static_cast<TrackItem *>(item)->getSong());
}
}
return songList;
}
void BrowseModel::connectionChanged()
{
clear();
if (!root->isFetching() && MPDConnection::self()->isConnected()) {
dbVersion=0;
root->setState(FolderItem::State_Fetching);
emit listFolder(root->getPath());
}
}
void BrowseModel::statsUpdated(const MPDStatsValues &stats)
{
if (stats.dbUpdate!=dbVersion) {
if (0!=dbVersion) {
connectionChanged();
}
dbVersion=stats.dbUpdate;
}
}
void BrowseModel::folderContents(const QString &path, const QStringList &folders, const QList<Song> &songs)
{
QMap<QString, FolderItem *>::Iterator it=folderIndex.find(path);
if (it==folderIndex.end() || 0!=it.value()->getChildCount()) {
return;
}
if (folders.count() + songs.count()) {
QModelIndex idx=it.value()==root ? QModelIndex() : createIndex(it.value()->getRow(), 0, it.value());
beginInsertRows(idx, 0, folders.count() + songs.count() - 1);
for (const QString &folder: folders) {
FolderItem *item = new FolderItem(folder.split("/", CANTATA_SKIP_EMPTY).last(), folder, it.value());
it.value()->add(item);
folderIndex.insert(folder, item);
}
for (const Song &song: songs) {
it.value()->add(new TrackItem(song, it.value()));
}
it.value()->setState(FolderItem::State_Fetched);
endInsertRows();
}
}
#include "moc_browsemodel.cpp"