/*************************************************************************** * Copyright (C) 2020-2021 by Ilya Kotov * * forkotov02@ya.ru * * * * 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; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include #include #include #include #include #include #include "librarymodel.h" #define CONNECTION_NAME "qmmp_library_view" class LibraryTreeItem { public: LibraryTreeItem() {} ~LibraryTreeItem() { clear(); } void clear() { name.clear(); type = Qmmp::UNKNOWN; parent = nullptr; qDeleteAll(children); children.clear(); } QString name; Qmmp::MetaData type = Qmmp::UNKNOWN; QList children; LibraryTreeItem *parent = nullptr; }; LibraryModel::LibraryModel(QObject *parent) : QAbstractItemModel(parent) { m_rootItem = new LibraryTreeItem; refresh(); } LibraryModel::~LibraryModel() { delete m_rootItem; if(QSqlDatabase::contains(CONNECTION_NAME)) { QSqlDatabase::database(CONNECTION_NAME).close(); QSqlDatabase::removeDatabase(CONNECTION_NAME); } } Qt::ItemFlags LibraryModel::flags(const QModelIndex &index) const { if(index.isValid()) return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled; else return QAbstractItemModel::flags(index); } QStringList LibraryModel::mimeTypes() const { return QStringList("text/uri-list"); } QMimeData *LibraryModel::mimeData(const QModelIndexList &indexes) const { QList urls; for(const QModelIndex &index : indexes) { if(index.isValid()) urls << getUrls(index); } if(!urls.isEmpty()) { QMimeData *mimeData = new QMimeData; mimeData->setUrls(urls); return mimeData; } return nullptr; } bool LibraryModel::canFetchMore(const QModelIndex &parent) const { if(!parent.isValid()) return false; LibraryTreeItem *parentItem = static_cast(parent.internalPointer()); if(parentItem == m_rootItem || parentItem->type == Qmmp::TITLE) return false; else return parentItem->children.isEmpty(); } void LibraryModel::fetchMore(const QModelIndex &parent) { if(!parent.isValid()) return; LibraryTreeItem *parentItem = static_cast(parent.internalPointer()); QSqlDatabase db = QSqlDatabase::database(CONNECTION_NAME); if(!db.isOpen()) return; if(parentItem->type == Qmmp::ARTIST) { QSqlQuery query(db); if(m_filter.isEmpty()) { query.prepare("SELECT DISTINCT Album from track_library WHERE Artist = :artist"); } else { query.prepare("SELECT DISTINCT Album from track_library WHERE Artist = :artist AND SearchString LIKE :filter"); query.bindValue(":filter", QString("%%1%").arg(m_filter.toLower())); } query.bindValue(":artist", parentItem->name); if(!query.exec()) { qWarning("Library: exec error: %s", qPrintable(query.lastError().text())); return; } while(query.next()) { LibraryTreeItem *item = new LibraryTreeItem; item->name = query.value("Album").toString(); item->type = Qmmp::ALBUM; item->parent = parentItem; parentItem->children << item; qDebug() << parentItem->name << item->name; } } else if(parentItem->type == Qmmp::ALBUM) { QSqlQuery query(db); if(m_filter.isEmpty()) { query.prepare("SELECT Title from track_library WHERE Artist = :artist AND Album = :album"); } else { query.prepare("SELECT Title from track_library WHERE Artist = :artist AND Album = :album " "AND SearchString LIKE :filter"); query.bindValue(":filter", QString("%%1%").arg(m_filter.toLower())); } query.bindValue(":artist", parentItem->parent->name); query.bindValue(":album", parentItem->name); if(!query.exec()) { qWarning("Library: exec error: %s", qPrintable(query.lastError().text())); return; } while(query.next()) { LibraryTreeItem *item = new LibraryTreeItem; item->name = query.value("Title").toString(); item->type = Qmmp::TITLE; item->parent = parentItem; parentItem->children << item; } } } QVariant LibraryModel::data(const QModelIndex &index, int role) const { if(!index.isValid() || role != Qt::DisplayRole) return QVariant(); QString name = static_cast(index.internalPointer())->name; return name.isEmpty() ? tr("Unknown") : name; } QModelIndex LibraryModel::parent(const QModelIndex &child) const { if(!child.isValid()) return QModelIndex(); LibraryTreeItem *childItem = static_cast(child.internalPointer()); LibraryTreeItem *parentItem = childItem->parent; if(parentItem == m_rootItem || !parentItem || !parentItem->parent) return QModelIndex(); return createIndex(parentItem->parent->children.indexOf(parentItem), 0, parentItem); } QModelIndex LibraryModel::index(int row, int column, const QModelIndex &parent) const { if(parent.isValid() && parent.column() != 0) return QModelIndex(); LibraryTreeItem *parentItem = parent.isValid() ? static_cast(parent.internalPointer()) : m_rootItem; if(row >= 0 && row < parentItem->children.count()) return createIndex(row, column, parentItem->children.at(row)); else return QModelIndex(); } int LibraryModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 1; } int LibraryModel::rowCount(const QModelIndex &parent) const { if(!parent.isValid()) return m_rootItem->children.count(); LibraryTreeItem *parentItem = static_cast(parent.internalPointer()); if(parentItem->type == Qmmp::TITLE) return 0; else return qMax(1, parentItem->children.count()); } void LibraryModel::setFilter(const QString &filter) { m_filter = filter; } void LibraryModel::refresh() { beginResetModel(); m_rootItem->clear(); QSqlDatabase db; if(QSqlDatabase::contains(CONNECTION_NAME)) { db = QSqlDatabase::database(CONNECTION_NAME); } else { db = QSqlDatabase::addDatabase("QSQLITE", CONNECTION_NAME); db.setDatabaseName(Qmmp::configDir() + "/" + "library.sqlite"); db.open(); } if(!db.isOpen()) { endResetModel(); return; } QSqlQuery query(db); if(m_filter.isEmpty()) { query.prepare("SELECT DISTINCT Artist from track_library ORDER BY Artist"); } else { query.prepare("SELECT DISTINCT Artist from track_library WHERE SearchString LIKE :filter ORDER BY Artist"); query.bindValue(":filter", QString("%%1%").arg(m_filter.toLower())); } if(!query.exec()) qWarning("Library: exec error: %s", qPrintable(query.lastError().text())); while(query.next()) { LibraryTreeItem *item = new LibraryTreeItem; item->name = query.value("Artist").toString(); item->type = Qmmp::ARTIST; item->parent = m_rootItem; m_rootItem->children << item; } endResetModel(); } QList LibraryModel::getUrls(const QModelIndex &index) const { QSqlDatabase db = QSqlDatabase::database(CONNECTION_NAME); QList urls; if(!db.isOpen()) return urls; const LibraryTreeItem *item = static_cast(index.internalPointer()); if(item->type == Qmmp::TITLE) { QSqlQuery query(db); query.prepare("SELECT URL from track_library WHERE Artist = :artist AND Album = :album AND Title = :title"); query.bindValue(":artist", item->parent->parent->name); query.bindValue(":album", item->parent->name); query.bindValue(":title", item->name); if(!query.exec()) { qWarning("Library: exec error: %s", qPrintable(query.lastError().text())); return urls; } if(query.next()) { QString path = query.value("URL").toString(); urls << (path.contains("://") ? QUrl(path) : QUrl::fromLocalFile(path)); } } else if(item->type == Qmmp::ALBUM) { QSqlQuery query(db); query.prepare("SELECT URL from track_library WHERE Artist = :artist AND Album = :album"); query.bindValue(":artist", item->parent->name); query.bindValue(":album", item->name); if(!query.exec()) { qWarning("Library: exec error: %s", qPrintable(query.lastError().text())); return urls; } while(query.next()) { QString path = query.value("URL").toString(); urls << (path.contains("://") ? QUrl(path) : QUrl::fromLocalFile(path)); } } else if(item->type == Qmmp::ARTIST) { QSqlQuery query(db); query.prepare("SELECT URL from track_library WHERE Artist = :artist"); query.bindValue(":artist", item->name); if(!query.exec()) { qWarning("Library: exec error: %s", qPrintable(query.lastError().text())); return urls; } while(query.next()) { QString path = query.value("URL").toString(); urls << (path.contains("://") ? QUrl(path) : QUrl::fromLocalFile(path)); } } return urls; }