/* * Cantata * * Copyright (c) 2017-2022 Craig Drummond * * ---- * * 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 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(i)->allEntries(allowPlaylists); } else if (allowPlaylists || Song::Playlist!=static_cast(i)->getSong().type) { entries+=static_cast(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)), this, SLOT(folderContents(QString,QStringList,QList))); 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)), this, SLOT(folderContents(QString,QStringList,QList))); 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(parent.internalPointer()) : root; const Item * c = rowgetChildCount() ? 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(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(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(item)->canFetchMore(); } else { return false; } } void BrowseModel::fetchMore(const QModelIndex &index) { if (!index.isValid()) { return; } FolderItem *item = static_cast(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(index.internalPointer()); switch (role) { case Qt::DecorationRole: if (item->isFolder()) { return Icons::self()->folderListIcon; } else { TrackItem *track = static_cast(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(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(item)->getSong().type) { return static_cast(item)->getPath(); } return static_cast(item)->getSong().toolTip(); case Cantata::Role_SubText: if (!item->isFolder()) { TrackItem *track = static_cast(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(item)->allEntries(false); } else { files.append(static_cast(item)->getSong().file); } } } PlayQueueModel::encode(*mimeData, PlayQueueModel::constFileNameMimeType, files); return mimeData; } QList BrowseModel::songs(const QModelIndexList &indexes, bool allowPlaylists) const { QList songList; for (const QModelIndex &idx: indexes) { Item *item=toItem(idx); if (item && !item->isFolder() && (allowPlaylists || Song::Standard==static_cast(item)->getSong().type)) { songList.append(static_cast(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 &songs) { QMap::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"