diff --git a/src/models/datamodel.cpp b/src/models/datamodel.cpp index f8c22f96..393d50e4 100644 --- a/src/models/datamodel.cpp +++ b/src/models/datamodel.cpp @@ -1,642 +1,690 @@ /* * Copyright 2015-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "datamodel.h" #include "modeldataloader.h" #include "musiclistenersmanager.h" #include #include #include #include #include #include class DataModelPrivate { public: DataModel::ListTrackDataType mAllTrackData; DataModel::ListAlbumDataType mAllAlbumData; DataModel::ListArtistDataType mAllArtistData; DataModel::ListGenreDataType mAllGenreData; ModelDataLoader mDataLoader; ElisaUtils::PlayListEntryType mModelType = ElisaUtils::Unknown; QString mAlbumTitle; QString mAlbumArtist; QString mGenre; + bool mIsBusy = false; + }; DataModel::DataModel(QObject *parent) : QAbstractListModel(parent), d(std::make_unique()) { } DataModel::~DataModel() = default; int DataModel::rowCount(const QModelIndex &parent) const { auto dataCount = 0; if (parent.isValid()) { return dataCount; } dataCount = d->mAllTrackData.size() + d->mAllAlbumData.size() + d->mAllArtistData.size() + d->mAllGenreData.size(); return dataCount; } QHash DataModel::roleNames() const { auto roles = QAbstractListModel::roleNames(); roles[static_cast(DatabaseInterface::ColumnsRoles::TitleRole)] = "title"; roles[static_cast(DatabaseInterface::ColumnsRoles::SecondaryTextRole)] = "secondaryText"; roles[static_cast(DatabaseInterface::ColumnsRoles::ImageUrlRole)] = "imageUrl"; roles[static_cast(DatabaseInterface::ColumnsRoles::DatabaseIdRole)] = "databaseId"; roles[static_cast(DatabaseInterface::ColumnsRoles::ElementTypeRole)] = "dataType"; roles[static_cast(DatabaseInterface::ColumnsRoles::ArtistRole)] = "artist"; roles[static_cast(DatabaseInterface::ColumnsRoles::AllArtistsRole)] = "allArtists"; roles[static_cast(DatabaseInterface::ColumnsRoles::HighestTrackRating)] = "highestTrackRating"; roles[static_cast(DatabaseInterface::ColumnsRoles::GenreRole)] = "genre"; roles[static_cast(DatabaseInterface::ColumnsRoles::AlbumRole)] = "album"; roles[static_cast(DatabaseInterface::ColumnsRoles::AlbumArtistRole)] = "albumArtist"; roles[static_cast(DatabaseInterface::ColumnsRoles::DurationRole)] = "duration"; roles[static_cast(DatabaseInterface::ColumnsRoles::TrackNumberRole)] = "trackNumber"; roles[static_cast(DatabaseInterface::ColumnsRoles::DiscNumberRole)] = "discNumber"; roles[static_cast(DatabaseInterface::ColumnsRoles::RatingRole)] = "rating"; roles[static_cast(DatabaseInterface::ColumnsRoles::IsSingleDiscAlbumRole)] = "isSingleDiscAlbum"; return roles; } Qt::ItemFlags DataModel::flags(const QModelIndex &index) const { if (!index.isValid()) { return Qt::NoItemFlags; } return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } QVariant DataModel::data(const QModelIndex &index, int role) const { auto result = QVariant(); const auto dataCount = d->mAllTrackData.size() + d->mAllAlbumData.size() + d->mAllArtistData.size() + d->mAllGenreData.size(); Q_ASSERT(index.isValid()); Q_ASSERT(index.column() == 0); Q_ASSERT(index.row() >= 0 && index.row() < dataCount); Q_ASSERT(!index.parent().isValid()); Q_ASSERT(index.model() == this); Q_ASSERT(index.internalId() == 0); switch(role) { case Qt::DisplayRole: switch(d->mModelType) { case ElisaUtils::Track: result = d->mAllTrackData[index.row()][TrackDataType::key_type::TitleRole]; break; case ElisaUtils::Album: result = d->mAllAlbumData[index.row()][AlbumDataType::key_type::TitleRole]; break; case ElisaUtils::Artist: result = d->mAllArtistData[index.row()][ArtistDataType::key_type::TitleRole]; break; case ElisaUtils::Genre: result = d->mAllGenreData[index.row()][GenreDataType::key_type::TitleRole]; break; case ElisaUtils::Lyricist: case ElisaUtils::Composer: case ElisaUtils::FileName: case ElisaUtils::Unknown: break; } break; case DatabaseInterface::ColumnsRoles::DurationRole: { if (d->mModelType == ElisaUtils::Track) { auto trackDuration = d->mAllTrackData[index.row()][TrackDataType::key_type::DurationRole].toTime(); if (trackDuration.hour() == 0) { result = trackDuration.toString(QStringLiteral("mm:ss")); } else { result = trackDuration.toString(); } } break; } default: switch(d->mModelType) { case ElisaUtils::Track: result = d->mAllTrackData[index.row()][static_cast(role)]; break; case ElisaUtils::Album: result = d->mAllAlbumData[index.row()][static_cast(role)]; break; case ElisaUtils::Artist: result = d->mAllArtistData[index.row()][static_cast(role)]; break; case ElisaUtils::Genre: result = d->mAllGenreData[index.row()][static_cast(role)]; break; case ElisaUtils::Lyricist: case ElisaUtils::Composer: case ElisaUtils::FileName: case ElisaUtils::Unknown: break; } } return result; } QModelIndex DataModel::index(int row, int column, const QModelIndex &parent) const { auto result = QModelIndex(); if (column != 0) { return result; } if (parent.isValid()) { return result; } result = createIndex(row, column); return result; } QModelIndex DataModel::parent(const QModelIndex &child) const { Q_UNUSED(child) auto result = QModelIndex(); return result; } QString DataModel::title() const { return d->mAlbumTitle; } QString DataModel::author() const { return d->mAlbumArtist; } +bool DataModel::isBusy() const +{ + return d->mIsBusy; +} + void DataModel::initialize(MusicListenersManager *manager, ElisaUtils::PlayListEntryType modelType) { d->mModelType = modelType; if (!manager) { return; } manager->connectModel(&d->mDataLoader); connectModel(manager); connect(this, &DataModel::needData, &d->mDataLoader, &ModelDataLoader::loadData); + setBusy(true); + Q_EMIT needData(d->mModelType); } void DataModel::initializeByAlbumTitleAndArtist(MusicListenersManager *manager, ElisaUtils::PlayListEntryType modelType, const QString &albumTitle, const QString &albumArtist) { d->mModelType = modelType; d->mAlbumTitle = albumTitle; d->mAlbumArtist = albumArtist; if (!manager) { return; } manager->connectModel(&d->mDataLoader); connectModel(manager); connect(this, &DataModel::needData, &d->mDataLoader, &ModelDataLoader::loadData); + setBusy(true); + Q_EMIT needData(d->mModelType); } void DataModel::initializeByGenre(MusicListenersManager *manager, ElisaUtils::PlayListEntryType modelType, const QString &genre) { d->mModelType = modelType; d->mGenre = genre; if (!manager) { return; } manager->connectModel(&d->mDataLoader); connectModel(manager); connect(this, &DataModel::needDataByGenre, &d->mDataLoader, &ModelDataLoader::loadDataByGenre); + setBusy(true); + Q_EMIT needDataByGenre(d->mModelType, genre); } void DataModel::initializeByArtist(MusicListenersManager *manager, ElisaUtils::PlayListEntryType modelType, const QString &artist) { d->mModelType = modelType; d->mAlbumArtist = artist; if (!manager) { return; } manager->connectModel(&d->mDataLoader); connectModel(manager); connect(this, &DataModel::needDataByArtist, &d->mDataLoader, &ModelDataLoader::loadDataByArtist); + setBusy(true); + Q_EMIT needDataByArtist(d->mModelType, artist); } void DataModel::initializeByGenreAndArtist(MusicListenersManager *manager, ElisaUtils::PlayListEntryType modelType, const QString &genre, const QString &artist) { d->mModelType = modelType; d->mGenre = genre; d->mAlbumArtist = artist; if (!manager) { return; } manager->connectModel(&d->mDataLoader); connectModel(manager); connect(this, &DataModel::needDataByGenreAndArtist, &d->mDataLoader, &ModelDataLoader::loadDataByGenreAndArtist); + setBusy(true); + Q_EMIT needDataByGenreAndArtist(d->mModelType, genre, artist); } void DataModel::initializeRecentlyPlayed(MusicListenersManager *manager, ElisaUtils::PlayListEntryType modelType) { d->mModelType = modelType; if (!manager) { return; } manager->connectModel(&d->mDataLoader); connectModel(manager); connect(this, &DataModel::needRecentlyPlayedData, &d->mDataLoader, &ModelDataLoader::loadRecentlyPlayedData); + setBusy(true); + Q_EMIT needRecentlyPlayedData(d->mModelType); } void DataModel::initializeFrequentlyPlayed(MusicListenersManager *manager, ElisaUtils::PlayListEntryType modelType) { d->mModelType = modelType; if (!manager) { return; } manager->connectModel(&d->mDataLoader); connectModel(manager); connect(this, &DataModel::needFrequentlyPlayedData, &d->mDataLoader, &ModelDataLoader::loadFrequentlyPlayedData); + setBusy(true); + Q_EMIT needFrequentlyPlayedData(d->mModelType); } +void DataModel::setBusy(bool value) +{ + if (d->mIsBusy == value) { + return; + } + + d->mIsBusy = value; + Q_EMIT isBusyChanged(); +} + int DataModel::trackIndexFromId(qulonglong id) const { int result; for (result = 0; result < d->mAllTrackData.size(); ++result) { if (d->mAllTrackData[result].databaseId() == id) { return result; } } result = -1; return result; } void DataModel::connectModel(MusicListenersManager *manager) { connect(manager->viewDatabase(), &DatabaseInterface::genresAdded, this, &DataModel::genresAdded); connect(manager->viewDatabase(), &DatabaseInterface::albumsAdded, this, &DataModel::albumsAdded); connect(manager->viewDatabase(), &DatabaseInterface::albumModified, this, &DataModel::albumModified); connect(manager->viewDatabase(), &DatabaseInterface::albumRemoved, this, &DataModel::albumRemoved); connect(manager->viewDatabase(), &DatabaseInterface::tracksAdded, this, &DataModel::tracksAdded); connect(manager->viewDatabase(), &DatabaseInterface::trackModified, this, &DataModel::trackModified); connect(manager->viewDatabase(), &DatabaseInterface::trackRemoved, this, &DataModel::trackRemoved); connect(manager->viewDatabase(), &DatabaseInterface::artistsAdded, this, &DataModel::artistsAdded); connect(manager->viewDatabase(), &DatabaseInterface::artistRemoved, this, &DataModel::artistRemoved); connect(&d->mDataLoader, &ModelDataLoader::allTracksData, this, &DataModel::tracksAdded); connect(&d->mDataLoader, &ModelDataLoader::allAlbumsData, this, &DataModel::albumsAdded); connect(&d->mDataLoader, &ModelDataLoader::allArtistsData, this, &DataModel::artistsAdded); connect(&d->mDataLoader, &ModelDataLoader::allGenresData, this, &DataModel::genresAdded); } void DataModel::tracksAdded(ListTrackDataType newData) { if (newData.isEmpty() || d->mModelType != ElisaUtils::Track) { return; } if (!d->mAlbumTitle.isEmpty() && !d->mAlbumArtist.isEmpty()) { for (const auto &newTrack : newData) { if (newTrack.album() != d->mAlbumTitle) { continue; } if (newTrack.albumArtist() != d->mAlbumArtist) { continue; } auto trackIndex = trackIndexFromId(newTrack.databaseId()); if (trackIndex != -1) { continue; } bool trackInserted = false; for (int trackIndex = 0; trackIndex < d->mAllTrackData.count(); ++trackIndex) { const auto &oneTrack = d->mAllTrackData[trackIndex]; if (oneTrack.discNumber() == newTrack.discNumber() && oneTrack.trackNumber() > newTrack.trackNumber()) { beginInsertRows({}, trackIndex, trackIndex); d->mAllTrackData.insert(trackIndex, newTrack); endInsertRows(); + + if (d->mAllTrackData.size() == 1) { + setBusy(false); + } + trackInserted = true; break; } } if (!trackInserted) { beginInsertRows({}, d->mAllTrackData.count(), d->mAllTrackData.count()); d->mAllTrackData.insert(d->mAllTrackData.count(), newTrack); endInsertRows(); + + if (d->mAllTrackData.size() == 1) { + setBusy(false); + } } } } else { if (d->mAllTrackData.isEmpty()) { beginInsertRows({}, 0, newData.size() - 1); d->mAllTrackData.swap(newData); endInsertRows(); + + setBusy(false); } else { beginInsertRows({}, d->mAllTrackData.size(), d->mAllTrackData.size() + newData.size() - 1); d->mAllTrackData.append(newData); endInsertRows(); } } } void DataModel::trackModified(const TrackDataType &modifiedTrack) { if (d->mModelType != ElisaUtils::Track) { return; } if (!d->mAlbumTitle.isEmpty() && !d->mAlbumArtist.isEmpty()) { if (modifiedTrack.album() != d->mAlbumTitle) { return; } auto trackIndex = trackIndexFromId(modifiedTrack.databaseId()); if (trackIndex == -1) { return; } d->mAllTrackData[trackIndex] = modifiedTrack; Q_EMIT dataChanged(index(trackIndex, 0), index(trackIndex, 0)); } else { auto itTrack = std::find_if(d->mAllTrackData.begin(), d->mAllTrackData.end(), [modifiedTrack](auto track) { return track.databaseId() == modifiedTrack.databaseId(); }); if (itTrack == d->mAllTrackData.end()) { return; } auto position = itTrack - d->mAllTrackData.begin(); d->mAllTrackData[position] = modifiedTrack; Q_EMIT dataChanged(index(position, 0), index(position, 0)); } } void DataModel::trackRemoved(qulonglong removedTrackId) { if (d->mModelType != ElisaUtils::Track) { return; } if (!d->mAlbumTitle.isEmpty() && !d->mAlbumArtist.isEmpty()) { auto trackIndex = trackIndexFromId(removedTrackId); if (trackIndex == -1) { return; } beginRemoveRows({}, trackIndex, trackIndex); d->mAllTrackData.removeAt(trackIndex); endRemoveRows(); } else { auto itTrack = std::find_if(d->mAllTrackData.begin(), d->mAllTrackData.end(), [removedTrackId](auto track) {return track.databaseId() == removedTrackId;}); if (itTrack == d->mAllTrackData.end()) { return; } auto position = itTrack - d->mAllTrackData.begin(); beginRemoveRows({}, position, position); d->mAllTrackData.erase(itTrack); endRemoveRows(); } } void DataModel::genresAdded(DataModel::ListGenreDataType newData) { if (newData.isEmpty() || d->mModelType != ElisaUtils::Genre) { return; } if (d->mAllGenreData.isEmpty()) { beginInsertRows({}, d->mAllGenreData.size(), newData.size() - 1); d->mAllGenreData.swap(newData); endInsertRows(); + + setBusy(false); } else { beginInsertRows({}, d->mAllGenreData.size(), d->mAllGenreData.size() + newData.size() - 1); d->mAllGenreData.append(newData); endInsertRows(); } } void DataModel::artistsAdded(DataModel::ListArtistDataType newData) { if (newData.isEmpty() || d->mModelType != ElisaUtils::Artist) { return; } if (d->mAllArtistData.isEmpty()) { beginInsertRows({}, d->mAllArtistData.size(), newData.size() - 1); d->mAllArtistData.swap(newData); endInsertRows(); + + setBusy(false); } else { beginInsertRows({}, d->mAllArtistData.size(), d->mAllArtistData.size() + newData.size() - 1); d->mAllArtistData.append(newData); endInsertRows(); } } void DataModel::artistRemoved(qulonglong removedDatabaseId) { if (d->mModelType != ElisaUtils::Artist) { return; } auto removedDataIterator = d->mAllArtistData.end(); removedDataIterator = std::find_if(d->mAllArtistData.begin(), d->mAllArtistData.end(), [removedDatabaseId](auto album) {return album.databaseId() == removedDatabaseId;}); if (removedDataIterator == d->mAllArtistData.end()) { return; } int dataIndex = removedDataIterator - d->mAllArtistData.begin(); beginRemoveRows({}, dataIndex, dataIndex); d->mAllArtistData.erase(removedDataIterator); endRemoveRows(); } void DataModel::albumsAdded(DataModel::ListAlbumDataType newData) { if (newData.isEmpty() || d->mModelType != ElisaUtils::Album) { return; } if (d->mAllAlbumData.isEmpty()) { beginInsertRows({}, d->mAllAlbumData.size(), newData.size() - 1); d->mAllAlbumData.swap(newData); endInsertRows(); + + setBusy(false); } else { beginInsertRows({}, d->mAllAlbumData.size(), d->mAllAlbumData.size() + newData.size() - 1); d->mAllAlbumData.append(newData); endInsertRows(); } } void DataModel::albumRemoved(qulonglong removedDatabaseId) { if (d->mModelType != ElisaUtils::Album) { return; } auto removedDataIterator = d->mAllAlbumData.end(); removedDataIterator = std::find_if(d->mAllAlbumData.begin(), d->mAllAlbumData.end(), [removedDatabaseId](auto album) {return album.databaseId() == removedDatabaseId;}); if (removedDataIterator == d->mAllAlbumData.end()) { return; } int dataIndex = removedDataIterator - d->mAllAlbumData.begin(); beginRemoveRows({}, dataIndex, dataIndex); d->mAllAlbumData.erase(removedDataIterator); endRemoveRows(); } void DataModel::albumModified(const DataModel::AlbumDataType &modifiedAlbum) { if (d->mModelType != ElisaUtils::Album) { return; } auto modifiedAlbumIterator = std::find_if(d->mAllAlbumData.begin(), d->mAllAlbumData.end(), [modifiedAlbum](auto album) { return album.databaseId() == modifiedAlbum.databaseId(); }); if (modifiedAlbumIterator == d->mAllAlbumData.end()) { return; } auto albumIndex = modifiedAlbumIterator - d->mAllAlbumData.begin(); Q_EMIT dataChanged(index(albumIndex, 0), index(albumIndex, 0)); } #include "moc_datamodel.cpp" diff --git a/src/models/datamodel.h b/src/models/datamodel.h index 260b36a5..5484cc57 100644 --- a/src/models/datamodel.h +++ b/src/models/datamodel.h @@ -1,153 +1,161 @@ /* * Copyright 2015-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef DATAMODEL_H #define DATAMODEL_H #include "elisaLib_export.h" #include "elisautils.h" #include "databaseinterface.h" #include #include #include #include #include class DataModelPrivate; class MusicListenersManager; class ELISALIB_EXPORT DataModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QString title READ title NOTIFY titleChanged) Q_PROPERTY(QString author READ author NOTIFY authorChanged) + Q_PROPERTY(bool isBusy READ isBusy NOTIFY isBusyChanged) + public: using ListTrackDataType = DatabaseInterface::ListTrackDataType; using TrackDataType = DatabaseInterface::TrackDataType; using ListAlbumDataType = DatabaseInterface::ListAlbumDataType; using AlbumDataType = DatabaseInterface::AlbumDataType; using ListArtistDataType = DatabaseInterface::ListArtistDataType; using ArtistDataType = DatabaseInterface::ArtistDataType; using ListGenreDataType = DatabaseInterface::ListGenreDataType; using GenreDataType = DatabaseInterface::GenreDataType; explicit DataModel(QObject *parent = nullptr); ~DataModel() override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QHash roleNames() const override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; QString title() const; QString author() const; + bool isBusy() const; + Q_SIGNALS: void titleChanged(); void authorChanged(); void needData(ElisaUtils::PlayListEntryType dataType); void needDataByGenre(ElisaUtils::PlayListEntryType dataType, const QString &genre); void needDataByArtist(ElisaUtils::PlayListEntryType dataType, const QString &artist); void needDataByGenreAndArtist(ElisaUtils::PlayListEntryType dataType, const QString &genre, const QString &artist); void needRecentlyPlayedData(ElisaUtils::PlayListEntryType dataType); void needFrequentlyPlayedData(ElisaUtils::PlayListEntryType dataType); + void isBusyChanged(); + public Q_SLOTS: void tracksAdded(DataModel::ListTrackDataType newData); void trackModified(const DataModel::TrackDataType &modifiedTrack); void trackRemoved(qulonglong removedTrackId); void genresAdded(DataModel::ListGenreDataType newData); void artistsAdded(DataModel::ListArtistDataType newData); void artistRemoved(qulonglong removedDatabaseId); void albumsAdded(DataModel::ListAlbumDataType newData); void albumRemoved(qulonglong removedDatabaseId); void albumModified(const DataModel::AlbumDataType &modifiedAlbum); void initialize(MusicListenersManager *manager, ElisaUtils::PlayListEntryType modelType); void initializeByAlbumTitleAndArtist(MusicListenersManager *manager, ElisaUtils::PlayListEntryType modelType, const QString &albumTitle, const QString &albumArtist); void initializeByGenre(MusicListenersManager *manager, ElisaUtils::PlayListEntryType modelType, const QString &genre); void initializeByArtist(MusicListenersManager *manager, ElisaUtils::PlayListEntryType modelType, const QString &artist); void initializeByGenreAndArtist(MusicListenersManager *manager, ElisaUtils::PlayListEntryType modelType, const QString &genre, const QString &artist); void initializeRecentlyPlayed(MusicListenersManager *manager, ElisaUtils::PlayListEntryType modelType); void initializeFrequentlyPlayed(MusicListenersManager *manager, ElisaUtils::PlayListEntryType modelType); private: int trackIndexFromId(qulonglong id) const; void connectModel(MusicListenersManager *manager); + void setBusy(bool value); + std::unique_ptr d; }; #endif // DATAMODEL_H diff --git a/src/qml/AlbumView.qml b/src/qml/AlbumView.qml index 49504be1..fbed753d 100644 --- a/src/qml/AlbumView.qml +++ b/src/qml/AlbumView.qml @@ -1,107 +1,118 @@ /* * Copyright 2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.10 import QtQuick.Controls 2.3 import org.kde.elisa 1.0 FocusScope { id: viewHeader property var viewType property alias mainTitle: albumGridView.mainTitle property alias secondaryTitle: albumGridView.secondaryTitle property alias image: albumGridView.image DataModel { id: realModel } SingleAlbumProxyModel { id: proxyModel sourceModel: realModel onEntriesToEnqueue: elisa.mediaPlayList.enqueue(newEntries, databaseIdType, enqueueMode, triggerPlay) } ListBrowserView { id: albumGridView anchors.fill: parent contentModel: proxyModel isSubPage: true enableSorting: false delegate: MediaAlbumTrackDelegate { id: entry width: albumGridView.delegateWidth height: ((true && !true) ? elisaTheme.delegateHeight*2 : elisaTheme.delegateHeight) focus: true databaseId: model.databaseId title: model.title artist: model.artist album: (model.album !== undefined && model.album !== '' ? model.album : '') albumArtist: model.albumArtist duration: model.duration imageUrl: (model.imageUrl !== undefined && model.imageUrl !== '' ? model.imageUrl : '') trackNumber: model.trackNumber discNumber: model.discNumber rating: model.rating isFirstTrackOfDisc: true isSingleDiscAlbum: true isAlternateColor: (index % 2) === 1 mediaTrack.onEnqueue: elisa.mediaPlayList.enqueue(databaseId, name, ElisaUtils.Track, ElisaUtils.AppendPlayList, ElisaUtils.DoNotTriggerPlay) mediaTrack.onReplaceAndPlay: elisa.mediaPlayList.enqueue(databaseId, name, ElisaUtils.Track, ElisaUtils.ReplacePlayList, ElisaUtils.TriggerPlay) mediaTrack.onClicked: albumGridView.currentIndex = index } allowArtistNavigation: true onShowArtist: { viewManager.openChildView(name, '', elisaTheme.artistIcon, 0, ElisaUtils.Artist) } onGoBack: viewManager.goBack() + + Loader { + anchors.fill: parent + + visible: realModel.isBusy + active: realModel.isBusy + + sourceComponent: BusyIndicator { + anchors.fill: parent + } + } } Connections { target: elisa onMusicManagerChanged: realModel.initializeByAlbumTitleAndArtist(elisa.musicManager, ElisaUtils.Track, mainTitle, secondaryTitle) } Component.onCompleted: { if (elisa.musicManager) { realModel.initializeByAlbumTitleAndArtist(elisa.musicManager, ElisaUtils.Track, mainTitle, secondaryTitle) } } } diff --git a/src/qml/DataGridView.qml b/src/qml/DataGridView.qml index 4e6cf34d..6698a3ff 100644 --- a/src/qml/DataGridView.qml +++ b/src/qml/DataGridView.qml @@ -1,103 +1,114 @@ /* * Copyright 2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.10 import QtQuick.Controls 2.3 import org.kde.elisa 1.0 FocusScope { id: viewHeader property var viewType property alias mainTitle: gridView.mainTitle property alias secondaryTitle: gridView.secondaryTitle property alias image: gridView.image property var modelType property alias defaultIcon: gridView.defaultIcon property alias showRating: gridView.showRating property alias delegateDisplaySecondaryText: gridView.delegateDisplaySecondaryText property alias isSubPage: gridView.isSubPage property string genreFilterText property string artistFilter focus: true DataModel { id: realModel } GridViewProxyModel { id: proxyModel sourceModel: realModel dataType: modelType onEntriesToEnqueue: elisa.mediaPlayList.enqueue(newEntries, databaseIdType, enqueueMode, triggerPlay) } GridBrowserView { id: gridView focus: true anchors.fill: parent contentModel: proxyModel onEnqueue: elisa.mediaPlayList.enqueue(databaseId, name, modelType, ElisaUtils.AppendPlayList, ElisaUtils.DoNotTriggerPlay) onReplaceAndPlay: elisa.mediaPlayList.enqueue(databaseId, name, modelType, ElisaUtils.ReplacePlayList, ElisaUtils.TriggerPlay) onOpen: viewManager.openChildView(innerMainTitle, innerSecondaryTitle, innerImage, databaseId, dataType) onGoBack: viewManager.goBack() + + Loader { + anchors.fill: parent + + visible: realModel.isBusy + active: realModel.isBusy + + sourceComponent: BusyIndicator { + anchors.fill: parent + } + } } Connections { target: elisa onMusicManagerChanged: { if (genreFilterText && artistFilter) { realModel.initializeByGenreAndArtist(elisa.musicManager, modelType, genreFilterText, artistFilter) } else if (genreFilterText) { realModel.initializeByGenre(elisa.musicManager, modelType, genreFilterText) } else if (artistFilter) { realModel.initializeByArtist(elisa.musicManager, modelType, artistFilter) } else { realModel.initialize(elisa.musicManager, modelType) } } } Component.onCompleted: { if (elisa.musicManager) { if (genreFilterText && artistFilter) { realModel.initializeByGenreAndArtist(elisa.musicManager, modelType, genreFilterText, artistFilter) } else if (genreFilterText) { realModel.initializeByGenre(elisa.musicManager, modelType, genreFilterText) } else if (artistFilter) { realModel.initializeByArtist(elisa.musicManager, modelType, artistFilter) } else { realModel.initialize(elisa.musicManager, modelType) } } } } diff --git a/src/qml/FrequentlyPlayedTracks.qml b/src/qml/FrequentlyPlayedTracks.qml index fcdf1fe4..6aebbbd6 100644 --- a/src/qml/FrequentlyPlayedTracks.qml +++ b/src/qml/FrequentlyPlayedTracks.qml @@ -1,100 +1,111 @@ /* * Copyright 2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.10 import QtQuick.Controls 2.3 import org.kde.elisa 1.0 FocusScope { id: viewHeader property var viewType property alias mainTitle: listView.mainTitle property alias image: listView.image property var modelType focus: true DataModel { id: realModel } AllTracksProxyModel { id: proxyModel sortRole: DatabaseInterface.PlayFrequency sourceModel: realModel onEntriesToEnqueue: elisa.mediaPlayList.enqueue(newEntries, databaseIdType, enqueueMode, triggerPlay) } ListBrowserView { id: listView focus: true anchors.fill: parent contentModel: proxyModel delegate: MediaTrackDelegate { id: entry width: listView.delegateWidth height: elisaTheme.trackDelegateHeight focus: true databaseId: model.databaseId title: model.title artist: model.artist album: (model.album !== undefined && model.album !== '' ? model.album : '') albumArtist: model.albumArtist duration: model.duration imageUrl: (model.imageUrl !== undefined && model.imageUrl !== '' ? model.imageUrl : '') trackNumber: model.trackNumber discNumber: model.discNumber rating: model.rating isFirstTrackOfDisc: false isSingleDiscAlbum: model.isSingleDiscAlbum onEnqueue: elisa.mediaPlayList.enqueue(databaseId, name, modelType, ElisaUtils.AppendPlayList, ElisaUtils.DoNotTriggerPlay) onReplaceAndPlay: elisa.mediaPlayList.enqueue(databaseId, name, modelType, ElisaUtils.ReplacePlayList, ElisaUtils.TriggerPlay) onClicked: contentDirectoryView.currentIndex = index } + + Loader { + anchors.fill: parent + + visible: realModel.isBusy + active: realModel.isBusy + + sourceComponent: BusyIndicator { + anchors.fill: parent + } + } } Connections { target: elisa onMusicManagerChanged: realModel.initializeFrequentlyPlayed(elisa.musicManager, modelType) } Component.onCompleted: { if (elisa.musicManager) { realModel.initializeFrequentlyPlayed(elisa.musicManager, modelType) } proxyModel.sortModel(Qt.DescendingOrder) } } diff --git a/src/qml/RecentlyPlayedTracks.qml b/src/qml/RecentlyPlayedTracks.qml index adcc08b6..25592a50 100644 --- a/src/qml/RecentlyPlayedTracks.qml +++ b/src/qml/RecentlyPlayedTracks.qml @@ -1,100 +1,111 @@ /* * Copyright 2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.10 import QtQuick.Controls 2.3 import org.kde.elisa 1.0 FocusScope { id: viewHeader property var viewType property alias mainTitle: listView.mainTitle property alias image: listView.image property var modelType focus: true DataModel { id: realModel } AllTracksProxyModel { id: proxyModel sortRole: DatabaseInterface.LastPlayDate sourceModel: realModel onEntriesToEnqueue: elisa.mediaPlayList.enqueue(newEntries, databaseIdType, enqueueMode, triggerPlay) } ListBrowserView { id: listView focus: true anchors.fill: parent contentModel: proxyModel delegate: MediaTrackDelegate { id: entry width: listView.delegateWidth height: elisaTheme.trackDelegateHeight focus: true databaseId: model.databaseId title: model.title artist: model.artist album: (model.album !== undefined && model.album !== '' ? model.album : '') albumArtist: model.albumArtist duration: model.duration imageUrl: (model.imageUrl !== undefined && model.imageUrl !== '' ? model.imageUrl : '') trackNumber: model.trackNumber discNumber: model.discNumber rating: model.rating isFirstTrackOfDisc: false isSingleDiscAlbum: model.isSingleDiscAlbum onEnqueue: elisa.mediaPlayList.enqueue(databaseId, name, modelType, ElisaUtils.AppendPlayList, ElisaUtils.DoNotTriggerPlay) onReplaceAndPlay: elisa.mediaPlayList.enqueue(databaseId, name, modelType, ElisaUtils.ReplacePlayList, ElisaUtils.TriggerPlay) onClicked: contentDirectoryView.currentIndex = index } + + Loader { + anchors.fill: parent + + visible: realModel.isBusy + active: realModel.isBusy + + sourceComponent: BusyIndicator { + anchors.fill: parent + } + } } Connections { target: elisa onMusicManagerChanged: realModel.initializeRecentlyPlayed(elisa.musicManager, modelType) } Component.onCompleted: { if (elisa.musicManager) { realModel.initializeRecentlyPlayed(elisa.musicManager, modelType) } proxyModel.sortModel(Qt.DescendingOrder) } } diff --git a/src/qml/TracksView.qml b/src/qml/TracksView.qml index 416af8f6..744ea618 100644 --- a/src/qml/TracksView.qml +++ b/src/qml/TracksView.qml @@ -1,98 +1,109 @@ /* * Copyright 2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.10 import QtQuick.Controls 2.3 import org.kde.elisa 1.0 FocusScope { id: viewHeader property var viewType property alias mainTitle: listView.mainTitle property alias image: listView.image property var modelType focus: true DataModel { id: realModel } AllTracksProxyModel { id: proxyModel sortRole: Qt.DisplayRole sourceModel: realModel onEntriesToEnqueue: elisa.mediaPlayList.enqueue(newEntries, databaseIdType, enqueueMode, triggerPlay) } ListBrowserView { id: listView focus: true anchors.fill: parent contentModel: proxyModel delegate: MediaTrackDelegate { id: entry width: listView.delegateWidth height: elisaTheme.trackDelegateHeight focus: true databaseId: model.databaseId title: model.title artist: model.artist album: (model.album !== undefined && model.album !== '' ? model.album : '') albumArtist: model.albumArtist duration: model.duration imageUrl: (model.imageUrl !== undefined && model.imageUrl !== '' ? model.imageUrl : '') trackNumber: model.trackNumber discNumber: model.discNumber rating: model.rating isFirstTrackOfDisc: false isSingleDiscAlbum: model.isSingleDiscAlbum onEnqueue: elisa.mediaPlayList.enqueue(databaseId, name, modelType, ElisaUtils.AppendPlayList, ElisaUtils.DoNotTriggerPlay) onReplaceAndPlay: elisa.mediaPlayList.enqueue(databaseId, name, modelType, ElisaUtils.ReplacePlayList, ElisaUtils.TriggerPlay) onClicked: contentDirectoryView.currentIndex = index } + + Loader { + anchors.fill: parent + + visible: realModel.isBusy + active: realModel.isBusy + + sourceComponent: BusyIndicator { + anchors.fill: parent + } + } } Connections { target: elisa onMusicManagerChanged: realModel.initialize(elisa.musicManager, modelType) } Component.onCompleted: { if (elisa.musicManager) { realModel.initialize(elisa.musicManager, modelType) } } }