diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -27,10 +27,10 @@ ../src/databaseinterface.cpp ../src/musiclistenersmanager.cpp ../src/elisaapplication.cpp - ../src/allalbumsmodel.cpp - ../src/allartistsmodel.cpp - ../src/alltracksmodel.cpp - ../src/albummodel.cpp + ../src/models/allalbumsmodel.cpp + ../src/models/allartistsmodel.cpp + ../src/models/alltracksmodel.cpp + ../src/models/albummodel.cpp ../src/notificationitem.cpp ../src/trackslistener.cpp ../src/musicartist.cpp @@ -108,10 +108,10 @@ ../src/databaseinterface.cpp ../src/musiclistenersmanager.cpp ../src/elisaapplication.cpp - ../src/allalbumsmodel.cpp - ../src/allartistsmodel.cpp - ../src/alltracksmodel.cpp - ../src/albummodel.cpp + ../src/models/allalbumsmodel.cpp + ../src/models/allartistsmodel.cpp + ../src/models/alltracksmodel.cpp + ../src/models/albummodel.cpp ../src/notificationitem.cpp ../src/trackslistener.cpp ../src/trackslistener.cpp @@ -200,10 +200,10 @@ ../src/databaseinterface.cpp ../src/trackslistener.cpp ../src/musiclistenersmanager.cpp - ../src/allalbumsmodel.cpp - ../src/allartistsmodel.cpp - ../src/alltracksmodel.cpp - ../src/albummodel.cpp + ../src/models/allalbumsmodel.cpp + ../src/models/allartistsmodel.cpp + ../src/models/alltracksmodel.cpp + ../src/models/albummodel.cpp ../src/elisaapplication.cpp ../src/notificationitem.cpp ../src/musicartist.cpp @@ -282,10 +282,10 @@ ../src/trackslistener.cpp ../src/musiclistenersmanager.cpp ../src/elisaapplication.cpp - ../src/allalbumsmodel.cpp - ../src/allartistsmodel.cpp - ../src/alltracksmodel.cpp - ../src/albummodel.cpp + ../src/models/allalbumsmodel.cpp + ../src/models/allartistsmodel.cpp + ../src/models/alltracksmodel.cpp + ../src/models/albummodel.cpp ../src/notificationitem.cpp ../src/musicartist.cpp ../src/musicalbum.cpp @@ -361,8 +361,7 @@ ../src/musicartist.cpp ../src/musicalbum.cpp ../src/musicaudiotrack.cpp - ../src/allalbumsmodel.cpp - ../src/albummodel.cpp + ../src/models/allalbumsmodel.cpp modeltest.cpp allalbumsmodeltest.cpp ) @@ -378,7 +377,7 @@ ../src/musicartist.cpp ../src/musicalbum.cpp ../src/musicaudiotrack.cpp - ../src/albummodel.cpp + ../src/models/albummodel.cpp modeltest.cpp albummodeltest.cpp ) @@ -394,7 +393,7 @@ ../src/musicartist.cpp ../src/musicalbum.cpp ../src/musicaudiotrack.cpp - ../src/allartistsmodel.cpp + ../src/models/allartistsmodel.cpp modeltest.cpp allartistsmodeltest.cpp ) @@ -410,7 +409,7 @@ ../src/musicartist.cpp ../src/musicalbum.cpp ../src/musicaudiotrack.cpp - ../src/alltracksmodel.cpp + ../src/models/alltracksmodel.cpp modeltest.cpp alltracksmodeltest.cpp ) diff --git a/autotests/albummodeltest.cpp b/autotests/albummodeltest.cpp --- a/autotests/albummodeltest.cpp +++ b/autotests/albummodeltest.cpp @@ -20,7 +20,7 @@ #include "musicalbum.h" #include "musicaudiotrack.h" #include "databaseinterface.h" -#include "albummodel.h" +#include "models/albummodel.h" #include "modeltest.h" #include diff --git a/autotests/allalbumsmodeltest.cpp b/autotests/allalbumsmodeltest.cpp --- a/autotests/allalbumsmodeltest.cpp +++ b/autotests/allalbumsmodeltest.cpp @@ -20,7 +20,7 @@ #include "musicalbum.h" #include "musicaudiotrack.h" #include "databaseinterface.h" -#include "allalbumsmodel.h" +#include "models/allalbumsmodel.h" #include "modeltest.h" #include diff --git a/autotests/allartistsmodeltest.cpp b/autotests/allartistsmodeltest.cpp --- a/autotests/allartistsmodeltest.cpp +++ b/autotests/allartistsmodeltest.cpp @@ -20,7 +20,7 @@ #include "musicalbum.h" #include "musicaudiotrack.h" #include "databaseinterface.h" -#include "allartistsmodel.h" +#include "models/allartistsmodel.h" #include "modeltest.h" #include diff --git a/autotests/alltracksmodeltest.cpp b/autotests/alltracksmodeltest.cpp --- a/autotests/alltracksmodeltest.cpp +++ b/autotests/alltracksmodeltest.cpp @@ -20,7 +20,7 @@ #include "musicalbum.h" #include "musicaudiotrack.h" #include "databaseinterface.h" -#include "alltracksmodel.h" +#include "models/alltracksmodel.h" #include "modeltest.h" #include diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,26 +9,32 @@ musicaudiotrack.cpp musicartist.cpp progressindicator.cpp - albummodel.cpp - allalbumsmodel.cpp - allartistsmodel.cpp databaseinterface.cpp musiclistenersmanager.cpp managemediaplayercontrol.cpp manageheaderbar.cpp manageaudioplayer.cpp - albumfilterproxymodel.cpp trackslistener.cpp elisaapplication.cpp audiowrapper.cpp - alltracksmodel.cpp notificationitem.cpp topnotificationmanager.cpp elisautils.cpp abstractfile/abstractfilelistener.cpp abstractfile/abstractfilelisting.cpp file/filelistener.cpp file/localfilelisting.cpp + models/albummodel.cpp + models/allalbumsmodel.cpp + models/allartistsmodel.cpp + models/alltracksmodel.cpp + models/abstractmediaproxymodel.cpp + models/allalbumsproxymodel.cpp + models/allartistsproxymodel.cpp + models/alltracksproxymodel.cpp + models/singleartistproxymodel.cpp + models/singlealbumproxymodel.cpp + qml/ElisaMainWindow.qml qml/Theme.qml @@ -53,9 +59,9 @@ qml/HeaderBar.qml qml/NavigationActionBar.qml - qml/FilterBar.qml qml/MediaPlayerControl.qml qml/ContextView.qml + qml/ViewSelector.qml qml/MediaPlayListView.qml qml/MediaAlbumView.qml @@ -65,8 +71,6 @@ qml/MediaTrackMetadataView.qml qml/GridBrowserView.qml qml/GridBrowserDelegate.qml - - qml/ViewSelector.qml ) if (KF5Baloo_FOUND) @@ -231,15 +235,15 @@ musicalbum.cpp musicaudiotrack.cpp musicartist.cpp + manageaudioplayer.cpp progressindicator.cpp - albummodel.cpp - allalbumsmodel.cpp - allartistsmodel.cpp + models/albummodel.cpp + models/allalbumsmodel.cpp + models/allartistsmodel.cpp + models/alltracksmodel.cpp databaseinterface.cpp musiclistenersmanager.cpp - albumfilterproxymodel.cpp trackslistener.cpp - alltracksmodel.cpp notificationitem.cpp topnotificationmanager.cpp elisautils.cpp diff --git a/src/databaseinterface.h b/src/databaseinterface.h --- a/src/databaseinterface.h +++ b/src/databaseinterface.h @@ -102,6 +102,8 @@ void trackModified(const MusicAudioTrack &modifiedTrack); + void sentAlbumData(const MusicAlbum albumData); + void requestsInitDone(); void databaseError(); @@ -116,6 +118,8 @@ void removeAllTracksFromSource(const QString &sourceName); + void getAlbumFromAlbumId(qulonglong id); + void cleanInvalidTracks(); private: diff --git a/src/databaseinterface.cpp b/src/databaseinterface.cpp --- a/src/databaseinterface.cpp +++ b/src/databaseinterface.cpp @@ -240,6 +240,7 @@ return result; } + QList DatabaseInterface::allTracks() { auto result = QList(); @@ -741,6 +742,25 @@ } } +void DatabaseInterface::getAlbumFromAlbumId(qulonglong id) +{ + auto result = MusicAlbum(); + + auto transactionResult = startTransaction(); + if (!transactionResult) { + Q_EMIT sentAlbumData(result); + } + + result = internalAlbumFromId(id); + + transactionResult = finishTransaction(); + if (!transactionResult) { + Q_EMIT sentAlbumData(result); + } + + Q_EMIT sentAlbumData(result); +} + void DatabaseInterface::cleanInvalidTracks() { if (d->mStopRequest == 1) { diff --git a/src/main.cpp b/src/main.cpp --- a/src/main.cpp +++ b/src/main.cpp @@ -43,15 +43,19 @@ #include "manageheaderbar.h" #include "manageaudioplayer.h" #include "musicstatistics.h" -#include "allalbumsmodel.h" -#include "albummodel.h" -#include "allartistsmodel.h" #include "musicaudiotrack.h" #include "musiclistenersmanager.h" -#include "albumfilterproxymodel.h" +#include "models/allalbumsmodel.h" +#include "models/albummodel.h" +#include "models/allartistsmodel.h" +#include "models/alltracksmodel.h" +#include "models/allalbumsproxymodel.h" +#include "models/alltracksproxymodel.h" +#include "models/allartistsproxymodel.h" +#include "models/singleartistproxymodel.h" +#include "models/singlealbumproxymodel.h" #include "elisaapplication.h" #include "audiowrapper.h" -#include "alltracksmodel.h" #include "notificationitem.h" #include "topnotificationmanager.h" #include "elisa_settings.h" @@ -145,22 +149,22 @@ qmlRegisterType("org.kde.elisa", 1, 0, "ManageAudioPlayer"); qmlRegisterType("org.kde.elisa", 1, 0, "MusicStatistics"); qmlRegisterType("org.kde.elisa", 1, 0, "ProgressIndicator"); - qmlRegisterType("org.kde.elisa", 1, 0, "AllAlbumsModel"); - qmlRegisterType("org.kde.elisa", 1, 0, "AllArtistsModel"); - qmlRegisterType("org.kde.elisa", 1, 0, "AlbumModel"); - qmlRegisterType("org.kde.elisa", 1, 0, "AllTracksModel"); qmlRegisterType("org.kde.elisa", 1, 0, "MusicListenersManager"); - qmlRegisterType("org.kde.elisa", 1, 0, "SortFilterProxyModel"); - qmlRegisterType("org.kde.elisa", 1, 0, "AlbumFilterProxyModel"); + qmlRegisterType("org.kde.elisa", 1, 0, "AlbumModel"); + qmlRegisterType("org.kde.elisa", 1, 0, "AllAlbumsProxyModel"); + qmlRegisterType("org.kde.elisa", 1, 0, "AllArtistsProxyModel"); + qmlRegisterType("org.kde.elisa", 1, 0, "AllTracksProxyModel"); + qmlRegisterType("org.kde.elisa", 1, 0, "SingleAlbumProxyModel"); + qmlRegisterType("org.kde.elisa", 1, 0, "SingleArtistProxyModel"); qmlRegisterType("org.kde.elisa", 1, 0, "AudioWrapper"); qmlRegisterType("org.kde.elisa", 1, 0, "TopNotificationManager"); #if defined Qt5DBus_FOUND && Qt5DBus_FOUND qmlRegisterType("org.kde.elisa", 1, 0, "Mpris2"); qRegisterMetaType(); #endif - qRegisterMetaType(); + qRegisterMetaType(); qRegisterMetaType>("QHash"); qRegisterMetaType>("QList"); qRegisterMetaType>("QVector"); @@ -232,6 +236,16 @@ MusicListenersManager myMusicManager; myMusicManager.setElisaApplication(&myApp); + AllAlbumsProxyModel allAlbumsProxyModel; + allAlbumsProxyModel.setSourceModel(myMusicManager.allAlbumsModel()); + AllArtistsProxyModel allArtistsProxyModel; + allArtistsProxyModel.setSourceModel(myMusicManager.allArtistsModel()); + AllTracksProxyModel allTracksProxyModel; + allTracksProxyModel.setSourceModel(myMusicManager.allTracksModel()); + SingleArtistProxyModel singleArtistProxyModel; + singleArtistProxyModel.setSourceModel(myMusicManager.allAlbumsModel()); + SingleAlbumProxyModel singleAlbumProxyModel; + singleAlbumProxyModel.setSourceModel(myMusicManager.albumModel()); QQmlApplicationEngine engine; engine.addImportPath(QStringLiteral("qrc:/imports")); @@ -246,6 +260,11 @@ engine.rootContext()->setContextObject(new KLocalizedContext(&engine)); engine.rootContext()->setContextProperty(QStringLiteral("elisa"), &myApp); engine.rootContext()->setContextProperty(QStringLiteral("allListeners"), &myMusicManager); + engine.rootContext()->setContextProperty(QStringLiteral("allAlbumsProxyModel"), &allAlbumsProxyModel); + engine.rootContext()->setContextProperty(QStringLiteral("allArtistsProxyModel"), &allArtistsProxyModel); + engine.rootContext()->setContextProperty(QStringLiteral("allTracksProxyModel"), &allTracksProxyModel); + engine.rootContext()->setContextProperty(QStringLiteral("singleArtistProxyModel"), &singleArtistProxyModel); + engine.rootContext()->setContextProperty(QStringLiteral("singleAlbumProxyModel"), &singleAlbumProxyModel); engine.load(QUrl(QStringLiteral("qrc:/qml/ElisaMainWindow.qml"))); diff --git a/src/albumfilterproxymodel.h b/src/models/abstractmediaproxymodel.h rename from src/albumfilterproxymodel.h rename to src/models/abstractmediaproxymodel.h --- a/src/albumfilterproxymodel.h +++ b/src/models/abstractmediaproxymodel.h @@ -1,5 +1,6 @@ /* - * Copyright 2016 Matthieu Gallien + * Copyright 2016-2017 Matthieu Gallien + * Copyright 2017 Alexander Stippich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -17,13 +18,18 @@ * Boston, MA 02110-1301, USA. */ -#ifndef ALBUMFILTERPROXYMODEL_H -#define ALBUMFILTERPROXYMODEL_H +#ifndef ABSTRACTMEDIAPROXYMODEL_H +#define ABSTRACTMEDIAPROXYMODEL_H #include #include -class AlbumFilterProxyModel : public QSortFilterProxyModel +#include "mediaplaylist.h" +#include "manageaudioplayer.h" + +class MediaPlayList; + +class AbstractMediaProxyModel : public QSortFilterProxyModel { Q_OBJECT @@ -38,40 +44,72 @@ WRITE setFilterRating NOTIFY filterRatingChanged) + Q_PROPERTY(MediaPlayList* mediaPlayList + READ mediaPlayList + WRITE setMediaPlayList + NOTIFY mediaPlayListChanged) + + Q_PROPERTY(MediaPlayList* mediaPlayList + READ mediaPlayList + WRITE setMediaPlayList + NOTIFY mediaPlayListChanged) + + Q_PROPERTY(ManageAudioPlayer* audioControl + READ audioControl + WRITE setAudioControl + NOTIFY audioControlChanged) + + public: - explicit AlbumFilterProxyModel(QObject *parent = nullptr); + explicit AbstractMediaProxyModel(QObject *parent = nullptr); - ~AlbumFilterProxyModel() override; + ~AbstractMediaProxyModel() override; QString filterText() const; int filterRating() const; + MediaPlayList *mediaPlayList() const; + + ManageAudioPlayer *audioControl() const; + public Q_SLOTS: + void setMediaPlayList(MediaPlayList *playList); + + void setAudioControl(ManageAudioPlayer *audioControl); + void setFilterText(const QString &filterText); void setFilterRating(int filterRating); + virtual void enqueueToPlayList() = 0; + Q_SIGNALS: void filterTextChanged(const QString &filterText); void filterRatingChanged(int filterRating); -protected: + void audioControlChanged(ManageAudioPlayer *audioControl); - bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; + void mediaPlayListChanged(MediaPlayList *mediaPlayList); -private: +protected: + + virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override = 0; QString mFilterText; int mFilterRating = 0; QRegularExpression mFilterExpression; + MediaPlayList* mMediaPlayList; + + ManageAudioPlayer* mAudioControl; + }; -#endif // ALBUMFILTERPROXYMODEL_H +#endif // ABSTRACTMEDIAPROXYMODEL_H diff --git a/src/models/abstractmediaproxymodel.cpp b/src/models/abstractmediaproxymodel.cpp new file mode 100644 --- /dev/null +++ b/src/models/abstractmediaproxymodel.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2016-2017 Matthieu Gallien + * Copyright 2017 Alexander Stippich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "abstractmediaproxymodel.h" + +AbstractMediaProxyModel::AbstractMediaProxyModel(QObject *parent) : QSortFilterProxyModel(parent) +{ + setFilterCaseSensitivity(Qt::CaseInsensitive); +} + +AbstractMediaProxyModel::~AbstractMediaProxyModel() += default; + +QString AbstractMediaProxyModel::filterText() const +{ + return mFilterText; +} + +int AbstractMediaProxyModel::filterRating() const +{ + return mFilterRating; +} + +void AbstractMediaProxyModel::setFilterText(const QString &filterText) +{ + if (mFilterText == filterText) + return; + + mFilterText = filterText; + + mFilterExpression.setPattern(mFilterText); + mFilterExpression.setPatternOptions(QRegularExpression::CaseInsensitiveOption); + mFilterExpression.optimize(); + + invalidate(); + + Q_EMIT filterTextChanged(mFilterText); +} + +void AbstractMediaProxyModel::setFilterRating(int filterRating) +{ + if (mFilterRating == filterRating) { + return; + } + + mFilterRating = filterRating; + + invalidate(); + + Q_EMIT filterRatingChanged(filterRating); +} + + +MediaPlayList *AbstractMediaProxyModel::mediaPlayList() const +{ + return mMediaPlayList; +} + +ManageAudioPlayer *AbstractMediaProxyModel::audioControl() const +{ + return mAudioControl; +} + +void AbstractMediaProxyModel::setMediaPlayList(MediaPlayList *playList) +{ + mMediaPlayList = playList; + Q_EMIT mediaPlayListChanged(mMediaPlayList); +} + +void AbstractMediaProxyModel::setAudioControl(ManageAudioPlayer *audioControl) +{ + mAudioControl = audioControl; + Q_EMIT audioControlChanged(mAudioControl); +} + +#include "moc_abstractmediaproxymodel.cpp" diff --git a/src/albummodel.h b/src/models/albummodel.h rename from src/albummodel.h rename to src/models/albummodel.h --- a/src/albummodel.h +++ b/src/models/albummodel.h @@ -32,7 +32,6 @@ class DatabaseInterface; class AlbumModelPrivate; -class MusicStatistics; class QMutex; class AlbumModel : public QAbstractItemModel @@ -123,6 +122,8 @@ int tracksCount() const; + Q_INVOKABLE void loadAlbumData(qulonglong id); + Q_SIGNALS: void albumDataChanged(); @@ -133,6 +134,8 @@ void tracksCountChanged(); + void requestAlbumData(qulonglong id); + public Q_SLOTS: void setAlbumData(const MusicAlbum &album); diff --git a/src/albummodel.cpp b/src/models/albummodel.cpp rename from src/albummodel.cpp rename to src/models/albummodel.cpp --- a/src/albummodel.cpp +++ b/src/models/albummodel.cpp @@ -18,7 +18,6 @@ */ #include "albummodel.h" -#include "musicstatistics.h" #include "databaseinterface.h" #include @@ -35,7 +34,6 @@ } MusicAlbum mCurrentAlbum; - }; AlbumModel::AlbumModel(QObject *parent) : QAbstractItemModel(parent), d(std::make_unique()) @@ -332,6 +330,7 @@ Q_EMIT tracksCountChanged(); Q_EMIT authorChanged(); Q_EMIT titleChanged(); + } void AlbumModel::albumModified(const MusicAlbum &modifiedAlbum) @@ -457,5 +456,9 @@ endRemoveRows(); } +void AlbumModel::loadAlbumData(qulonglong id) +{ + Q_EMIT requestAlbumData(id); +} #include "moc_albummodel.cpp" diff --git a/src/allalbumsmodel.h b/src/models/allalbumsmodel.h rename from src/allalbumsmodel.h rename to src/models/allalbumsmodel.h diff --git a/src/allalbumsmodel.cpp b/src/models/allalbumsmodel.cpp rename from src/allalbumsmodel.cpp rename to src/models/allalbumsmodel.cpp --- a/src/allalbumsmodel.cpp +++ b/src/models/allalbumsmodel.cpp @@ -202,9 +202,8 @@ break; case ColumnsRoles::ChildModelRole: { - auto newModel = new AlbumModel(); - newModel->setAlbumData(d->mAlbumsData[d->mAllAlbums[albumIndex]]); - result = QVariant::fromValue(newModel); + auto albumData = d->mAlbumsData[d->mAllAlbums[albumIndex]]; + result = QVariant::fromValue(albumData); break; } case ColumnsRoles::IsTracksContainerRole: diff --git a/src/models/allalbumsproxymodel.h b/src/models/allalbumsproxymodel.h new file mode 100644 --- /dev/null +++ b/src/models/allalbumsproxymodel.h @@ -0,0 +1,40 @@ +/* + * Copyright 2016-2017 Matthieu Gallien + * Copyright 2017 Alexander Stippich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef ALLALBUMSPROXYMODEL_H +#define ALLALBUMSPROXYMODEL_H + +#include "abstractmediaproxymodel.h" + +class AllAlbumsProxyModel : public AbstractMediaProxyModel +{ + Q_OBJECT + +public Q_SLOTS: + + void enqueueToPlayList() override; + +protected: + + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; + +}; + +#endif // ALLALBUMSPROXYMODEL_H diff --git a/src/albumfilterproxymodel.cpp b/src/models/allalbumsproxymodel.cpp rename from src/albumfilterproxymodel.cpp rename to src/models/allalbumsproxymodel.cpp --- a/src/albumfilterproxymodel.cpp +++ b/src/models/allalbumsproxymodel.cpp @@ -1,5 +1,6 @@ /* - * Copyright 2016 Matthieu Gallien + * Copyright 2016-2017 Matthieu Gallien + * Copyright 2017 Alexander Stippich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -17,59 +18,11 @@ * Boston, MA 02110-1301, USA. */ -#include "albumfilterproxymodel.h" +#include "allalbumsproxymodel.h" #include "allalbumsmodel.h" -#include "allartistsmodel.h" -AlbumFilterProxyModel::AlbumFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent), mFilterText() -{ - setFilterCaseSensitivity(Qt::CaseInsensitive); -} - -AlbumFilterProxyModel::~AlbumFilterProxyModel() -= default; - -QString AlbumFilterProxyModel::filterText() const -{ - return mFilterText; -} - -int AlbumFilterProxyModel::filterRating() const -{ - return mFilterRating; -} - -void AlbumFilterProxyModel::setFilterText(const QString &filterText) -{ - if (mFilterText == filterText) - return; - - mFilterText = filterText; - - mFilterExpression.setPattern(mFilterText); - mFilterExpression.setPatternOptions(QRegularExpression::CaseInsensitiveOption); - mFilterExpression.optimize(); - - invalidate(); - - Q_EMIT filterTextChanged(mFilterText); -} - -void AlbumFilterProxyModel::setFilterRating(int filterRating) -{ - if (mFilterRating == filterRating) { - return; - } - - mFilterRating = filterRating; - - invalidate(); - - Q_EMIT filterRatingChanged(filterRating); -} - -bool AlbumFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +bool AllAlbumsProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { bool result = false; @@ -115,5 +68,16 @@ return result; } +void AllAlbumsProxyModel::enqueueToPlayList() +{ + MediaPlayList *playList = AbstractMediaProxyModel::mediaPlayList(); + for (int columnIndex = 0, columnCount = this->columnCount(); columnIndex < columnCount; ++columnIndex) { + for (int rowIndex = 0, rowCount = this->rowCount(); rowIndex < rowCount; ++rowIndex) { + auto currentIndex = this->index(rowIndex, columnIndex); + playList->enqueue(this->data(currentIndex,AllAlbumsModel::ContainerDataRole).value()); + } + } + +} -#include "moc_albumfilterproxymodel.cpp" +#include "moc_allalbumsproxymodel.cpp" diff --git a/src/allartistsmodel.h b/src/models/allartistsmodel.h rename from src/allartistsmodel.h rename to src/models/allartistsmodel.h --- a/src/allartistsmodel.h +++ b/src/models/allartistsmodel.h @@ -79,8 +79,6 @@ AllAlbumsModel* allAlbums() const; - Q_INVOKABLE QAbstractItemModel* itemModelForName(const QString &name) const; - Q_SIGNALS: void allAlbumsChanged(); diff --git a/src/allartistsmodel.cpp b/src/models/allartistsmodel.cpp rename from src/allartistsmodel.cpp rename to src/models/allartistsmodel.cpp --- a/src/allartistsmodel.cpp +++ b/src/models/allartistsmodel.cpp @@ -26,7 +26,6 @@ #include #include #include -#include class AllArtistsModelPrivate { @@ -147,15 +146,8 @@ result = QVariant::fromValue(d->mAllArtists[index.row()]); break; case ColumnsRoles::ChildModelRole: - { - auto newModel = new QSortFilterProxyModel; - newModel->setSourceModel(d->mAllAlbumsModel); - newModel->setFilterRole(AllAlbumsModel::AllArtistsRole); - newModel->setFilterRegExp(QRegExp(QStringLiteral(".*") + d->mAllArtists[index.row()].name() + QStringLiteral(".*"), - Qt::CaseInsensitive)); - result = QVariant::fromValue(newModel); + result = d->mAllArtists[index.row()].name(); break; - } case ColumnsRoles::IsTracksContainerRole: result = false; break; @@ -202,17 +194,6 @@ return d->mAllAlbumsModel; } -QAbstractItemModel *AllArtistsModel::itemModelForName(const QString &name) const -{ - auto newModel = new QSortFilterProxyModel; - newModel->setSourceModel(d->mAllAlbumsModel); - newModel->setFilterRole(AllAlbumsModel::AllArtistsRole); - newModel->setFilterRegExp(QRegExp(QStringLiteral(".*") + name + QStringLiteral(".*"), - Qt::CaseInsensitive)); - - return newModel; -} - void AllArtistsModel::artistAdded(const MusicArtist &newArtist) { if (newArtist.isValid()) { diff --git a/src/models/allartistsproxymodel.h b/src/models/allartistsproxymodel.h new file mode 100644 --- /dev/null +++ b/src/models/allartistsproxymodel.h @@ -0,0 +1,38 @@ +/* + * Copyright 2017 Alexander Stippich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef ALLARTISTSPROXYMODEL_H +#define ALLARTISTSPROXYMODEL_H + +#include "abstractmediaproxymodel.h" + +class AllArtistsProxyModel : public AbstractMediaProxyModel +{ + Q_OBJECT + +public Q_SLOTS: + + void enqueueToPlayList() override; + +protected: + + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; + +}; +#endif // ALLARTISTSPROXYMODEL_H diff --git a/src/models/allartistsproxymodel.cpp b/src/models/allartistsproxymodel.cpp new file mode 100644 --- /dev/null +++ b/src/models/allartistsproxymodel.cpp @@ -0,0 +1,62 @@ +/* + * Copyright 2017 Alexander Stippich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "allartistsproxymodel.h" + +#include "allartistsmodel.h" + +bool AllArtistsProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + bool result = false; + + for (int column = 0, columnCount = sourceModel()->columnCount(source_parent); column < columnCount; ++column) { + auto currentIndex = sourceModel()->index(source_row, column, source_parent); + + const auto &artistValue = sourceModel()->data(currentIndex, AllArtistsModel::NameRole).toString(); + + if (mFilterExpression.match(artistValue).hasMatch()) { + result = true; + continue; + } + + if (result) { + continue; + } + + if (!result) { + break; + } + } + + return result; +} + +void AllArtistsProxyModel::enqueueToPlayList() +{ + MediaPlayList *playList = AbstractMediaProxyModel::mediaPlayList(); + for (int columnIndex = 0, columnCount = this->columnCount(); columnIndex < columnCount; ++columnIndex) { + for (int rowIndex = 0, rowCount = this->rowCount(); rowIndex < rowCount; ++rowIndex) { + auto currentIndex = this->index(rowIndex, columnIndex); + playList->enqueue(this->data(currentIndex,AllArtistsModel::NameRole).toString()); + } + } +} + + +#include "moc_allartistsproxymodel.cpp" diff --git a/src/alltracksmodel.h b/src/models/alltracksmodel.h rename from src/alltracksmodel.h rename to src/models/alltracksmodel.h diff --git a/src/alltracksmodel.cpp b/src/models/alltracksmodel.cpp rename from src/alltracksmodel.cpp rename to src/models/alltracksmodel.cpp diff --git a/src/models/alltracksproxymodel.h b/src/models/alltracksproxymodel.h new file mode 100644 --- /dev/null +++ b/src/models/alltracksproxymodel.h @@ -0,0 +1,39 @@ +/* + * Copyright 2017 Alexander Stippich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef ALLTRACKSPROXYMODEL_H +#define ALLTRACKSPROXYMODEL_H + +#include "abstractmediaproxymodel.h" + +class AllTracksProxyModel : public AbstractMediaProxyModel +{ + Q_OBJECT + +public Q_SLOTS: + + void enqueueToPlayList() override; + +protected: + + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; + +}; + +#endif // ALLTRACKSPROXYMODEL_H diff --git a/src/models/alltracksproxymodel.cpp b/src/models/alltracksproxymodel.cpp new file mode 100644 --- /dev/null +++ b/src/models/alltracksproxymodel.cpp @@ -0,0 +1,72 @@ +/* + * Copyright 2017 Alexander Stippich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "alltracksproxymodel.h" + +#include "alltracksmodel.h" + +bool AllTracksProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + bool result = false; + + for (int column = 0, columnCount = sourceModel()->columnCount(source_parent); column < columnCount; ++column) { + auto currentIndex = sourceModel()->index(source_row, column, source_parent); + + const auto &titleValue = sourceModel()->data(currentIndex, AllTracksModel::TitleRole).toString(); + const auto &artistValue = sourceModel()->data(currentIndex, AllTracksModel::ArtistRole).toString(); + const auto maximumRatingValue = sourceModel()->data(currentIndex, AllTracksModel::RatingRole).toInt(); + + if (maximumRatingValue < mFilterRating) { + result = false; + continue; + } + + if (mFilterExpression.match(titleValue).hasMatch()) { + result = true; + continue; + } + + if (mFilterExpression.match(artistValue).hasMatch()) { + result = true; + continue; + } + + if (result) { + continue; + } + + if (!result) { + break; + } + } + + return result; +} + +void AllTracksProxyModel::enqueueToPlayList() +{ + MediaPlayList *playList = AbstractMediaProxyModel::mediaPlayList(); + for (int columnIndex = 0, columnCount = this->columnCount(); columnIndex < columnCount; ++columnIndex) { + for (int rowIndex = 0, rowCount = this->rowCount(); rowIndex < rowCount; ++rowIndex) { + auto currentIndex = this->index(rowIndex, columnIndex); + playList->enqueue(this->data(currentIndex,AllTracksModel::DatabaseIdRole).toULongLong()); + } + } +} +#include "moc_alltracksproxymodel.cpp" diff --git a/src/models/singlealbumproxymodel.h b/src/models/singlealbumproxymodel.h new file mode 100644 --- /dev/null +++ b/src/models/singlealbumproxymodel.h @@ -0,0 +1,39 @@ +/* + * Copyright 2017 Alexander Stippich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SINGELALBUMPROXYMODEL_H +#define SINGELALBUMPROXYMODEL_H + +#include "abstractmediaproxymodel.h" + +class SingleAlbumProxyModel : public AbstractMediaProxyModel +{ + Q_OBJECT + +public Q_SLOTS: + + void enqueueToPlayList() override; + +protected: + + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; + +}; + +#endif // SINGELALBUMPROXYMODEL_H diff --git a/src/models/singlealbumproxymodel.cpp b/src/models/singlealbumproxymodel.cpp new file mode 100644 --- /dev/null +++ b/src/models/singlealbumproxymodel.cpp @@ -0,0 +1,67 @@ +/* + * Copyright 2017 Alexander Stippich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "singlealbumproxymodel.h" + +#include "albummodel.h" + +bool SingleAlbumProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + bool result = false; + + for (int column = 0, columnCount = sourceModel()->columnCount(source_parent); column < columnCount; ++column) { + auto currentIndex = sourceModel()->index(source_row, column, source_parent); + + const auto &titleValue = sourceModel()->data(currentIndex, AlbumModel::TitleRole).toString(); + const auto maximumRatingValue = sourceModel()->data(currentIndex, AlbumModel::RatingRole).toInt(); + + if (maximumRatingValue < mFilterRating) { + result = false; + continue; + } + + if (mFilterExpression.match(titleValue).hasMatch()) { + result = true; + continue; + } + + if (result) { + continue; + } + + if (!result) { + break; + } + } + + return result; +} + +void SingleAlbumProxyModel::enqueueToPlayList() +{ + MediaPlayList *playList = AbstractMediaProxyModel::mediaPlayList(); + for (int columnIndex = 0, columnCount = this->columnCount(); columnIndex < columnCount; ++columnIndex) { + for (int rowIndex = 0, rowCount = this->rowCount(); rowIndex < rowCount; ++rowIndex) { + auto currentIndex = this->index(rowIndex, columnIndex); + playList->enqueue(this->data(currentIndex,AlbumModel::DatabaseIdRole).toULongLong()); + } + } +} + +#include "moc_singlealbumproxymodel.cpp" diff --git a/src/models/singleartistproxymodel.h b/src/models/singleartistproxymodel.h new file mode 100644 --- /dev/null +++ b/src/models/singleartistproxymodel.h @@ -0,0 +1,58 @@ +/* + * Copyright 2016-2017 Matthieu Gallien + * Copyright 2017 Alexander Stippich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SINGLEARTISTPROXYMODEL_H +#define SINGLEARTISTPROXYMODEL_H + +#include "abstractmediaproxymodel.h" + +class SingleArtistProxyModel : public AbstractMediaProxyModel +{ + Q_OBJECT + + Q_PROPERTY(QString artistFilter + READ artistFilter + WRITE setArtistFilterText + NOTIFY artistFilterTextChanged) + +public: + QString artistFilter() const; + +public Q_SLOTS: + + void enqueueToPlayList() override; + + void setArtistFilterText(const QString &filterText); + +Q_SIGNALS: + + void artistFilterTextChanged(const QString &filterText); + +protected: + + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; + + QString mArtistFilter; + + QRegularExpression mArtistExpression; + +}; + +#endif // SINGLEARTISTPROXYMODEL_H diff --git a/src/albumfilterproxymodel.cpp b/src/models/singleartistproxymodel.cpp rename from src/albumfilterproxymodel.cpp rename to src/models/singleartistproxymodel.cpp --- a/src/albumfilterproxymodel.cpp +++ b/src/models/singleartistproxymodel.cpp @@ -1,5 +1,6 @@ /* - * Copyright 2016 Matthieu Gallien + * Copyright 2016-2017 Matthieu Gallien + * Copyright 2017 Alexander Stippich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -17,90 +18,54 @@ * Boston, MA 02110-1301, USA. */ -#include "albumfilterproxymodel.h" +#include "singleartistproxymodel.h" #include "allalbumsmodel.h" -#include "allartistsmodel.h" -AlbumFilterProxyModel::AlbumFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent), mFilterText() +QString SingleArtistProxyModel::artistFilter() const { - setFilterCaseSensitivity(Qt::CaseInsensitive); + return mArtistFilter; } -AlbumFilterProxyModel::~AlbumFilterProxyModel() -= default; -QString AlbumFilterProxyModel::filterText() const +void SingleArtistProxyModel::setArtistFilterText(const QString &filterText) { - return mFilterText; -} - -int AlbumFilterProxyModel::filterRating() const -{ - return mFilterRating; -} - -void AlbumFilterProxyModel::setFilterText(const QString &filterText) -{ - if (mFilterText == filterText) + if (mArtistFilter == filterText) return; - mFilterText = filterText; + mArtistFilter = filterText; - mFilterExpression.setPattern(mFilterText); - mFilterExpression.setPatternOptions(QRegularExpression::CaseInsensitiveOption); - mFilterExpression.optimize(); + mArtistExpression.setPattern(QStringLiteral(".*") + mArtistFilter + QStringLiteral(".*")); + mArtistExpression.setPatternOptions(QRegularExpression::CaseInsensitiveOption); + mArtistExpression.optimize(); invalidate(); - Q_EMIT filterTextChanged(mFilterText); + Q_EMIT artistFilterTextChanged(mArtistFilter); } -void AlbumFilterProxyModel::setFilterRating(int filterRating) -{ - if (mFilterRating == filterRating) { - return; - } - - mFilterRating = filterRating; - - invalidate(); - - Q_EMIT filterRatingChanged(filterRating); -} - -bool AlbumFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +bool SingleArtistProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { bool result = false; for (int column = 0, columnCount = sourceModel()->columnCount(source_parent); column < columnCount; ++column) { auto currentIndex = sourceModel()->index(source_row, column, source_parent); const auto &titleValue = sourceModel()->data(currentIndex, AllAlbumsModel::TitleRole).toString(); - const auto &artistValue = sourceModel()->data(currentIndex, AllAlbumsModel::ArtistRole).toString(); - const auto &allArtistsValue = sourceModel()->data(currentIndex, AllAlbumsModel::AllArtistsRole).toStringList(); + const auto &artistValue = sourceModel()->data(currentIndex, AllAlbumsModel::AllArtistsRole).toString(); const auto maximumRatingValue = sourceModel()->data(currentIndex, AllAlbumsModel::HighestTrackRating).toInt(); if (maximumRatingValue < mFilterRating) { result = false; continue; } - if (mFilterExpression.match(titleValue).hasMatch()) { - result = true; - continue; - } - - if (mFilterExpression.match(artistValue).hasMatch()) { - result = true; - continue; - } - - for (const auto &oneArtist : allArtistsValue) { - if (mFilterExpression.match(oneArtist).hasMatch()) { + if (mArtistExpression.match(artistValue).hasMatch()) { + if (mFilterExpression.match(titleValue).hasMatch()) { result = true; - break; + continue; } + } if (result) { @@ -115,5 +80,16 @@ return result; } +void SingleArtistProxyModel::enqueueToPlayList() +{ + MediaPlayList *playList = AbstractMediaProxyModel::mediaPlayList(); + for (int columnIndex = 0, columnCount = this->columnCount(); columnIndex < columnCount; ++columnIndex) { + for (int rowIndex = 0, rowCount = this->rowCount(); rowIndex < rowCount; ++rowIndex) { + auto currentIndex = this->index(rowIndex, columnIndex); + playList->enqueue(this->data(currentIndex,AllAlbumsModel::ContainerDataRole).value()); + } + } + +} -#include "moc_albumfilterproxymodel.cpp" +#include "moc_singleartistproxymodel.cpp" diff --git a/src/musiclistenersmanager.h b/src/musiclistenersmanager.h --- a/src/musiclistenersmanager.h +++ b/src/musiclistenersmanager.h @@ -38,6 +38,7 @@ class NotificationItem; class ElisaApplication; class QAbstractItemModel; +class AbstractMediaProxyModel; class MusicListenersManager : public QObject { @@ -63,15 +64,19 @@ Q_PROPERTY(QAbstractItemModel* allAlbumsModel READ allAlbumsModel - NOTIFY allAlbumsModelChanged) + CONSTANT) Q_PROPERTY(QAbstractItemModel* allArtistsModel READ allArtistsModel - NOTIFY allArtistsModelChanged) + CONSTANT) Q_PROPERTY(QAbstractItemModel* allTracksModel READ allTracksModel - NOTIFY allTracksModelChanged) + CONSTANT) + + Q_PROPERTY(QAbstractItemModel* albumModel + READ albumModel + CONSTANT) Q_PROPERTY(bool indexerBusy READ indexerBusy @@ -99,6 +104,8 @@ QAbstractItemModel *allTracksModel() const; + QAbstractItemModel *albumModel() const; + bool indexerBusy() const; Q_SIGNALS: @@ -141,12 +148,6 @@ void displayTrackError(const QString &fileName); - void allAlbumsModelChanged(); - - void allArtistsModelChanged(); - - void allTracksModelChanged(); - void indexerBusyChanged(); public Q_SLOTS: diff --git a/src/musiclistenersmanager.cpp b/src/musiclistenersmanager.cpp --- a/src/musiclistenersmanager.cpp +++ b/src/musiclistenersmanager.cpp @@ -37,9 +37,16 @@ #include "notificationitem.h" #include "elisaapplication.h" #include "elisa_settings.h" -#include "allalbumsmodel.h" -#include "allartistsmodel.h" -#include "alltracksmodel.h" +#include "models/allalbumsmodel.h" +#include "models/allartistsmodel.h" +#include "models/alltracksmodel.h" +#include "models/albummodel.h" +#include "models/abstractmediaproxymodel.h" +#include "models/allalbumsproxymodel.h" +#include "models/allartistsproxymodel.h" +#include "models/alltracksproxymodel.h" +#include "models/singleartistproxymodel.h" +#include "models/singlealbumproxymodel.h" #include @@ -95,6 +102,8 @@ AllTracksModel mAllTracksModel; + AlbumModel mAlbumModel; + bool mIndexerBusy = false; }; @@ -187,6 +196,15 @@ &d->mAllTracksModel, &AllTracksModel::trackModified); connect(&d->mDatabaseInterface, &DatabaseInterface::trackRemoved, &d->mAllTracksModel, &AllTracksModel::trackRemoved); + + connect(&d->mDatabaseInterface, &DatabaseInterface::albumModified, + &d->mAlbumModel, &AlbumModel::albumModified); + connect(&d->mDatabaseInterface, &DatabaseInterface::albumRemoved, + &d->mAlbumModel, &AlbumModel::albumRemoved); + connect(&d->mAlbumModel, &AlbumModel::requestAlbumData, + &d->mDatabaseInterface, &DatabaseInterface::getAlbumFromAlbumId); + connect(&d->mDatabaseInterface, &DatabaseInterface::sentAlbumData, + &d->mAlbumModel, &AlbumModel::setAlbumData); } MusicListenersManager::~MusicListenersManager() @@ -246,6 +264,11 @@ return &d->mAllTracksModel; } +QAbstractItemModel *MusicListenersManager::albumModel() const +{ + return &d->mAlbumModel; +} + bool MusicListenersManager::indexerBusy() const { return d->mIndexerBusy; diff --git a/src/qml/ElisaMainWindow.qml b/src/qml/ElisaMainWindow.qml --- a/src/qml/ElisaMainWindow.qml +++ b/src/qml/ElisaMainWindow.qml @@ -79,7 +79,6 @@ property bool playControlItemMuted : false } - Action { text: goBackAction.text shortcut: goBackAction.shortcut @@ -178,6 +177,11 @@ seedRandomGenerator(n); playFiles(elisa.arguments) + allAlbumsProxyModel.setMediaPlayList(playListModelItem) + allArtistsProxyModel.setMediaPlayList(playListModelItem) + allTracksProxyModel.setMediaPlayList(playListModelItem) + singleAlbumProxyModel.setMediaPlayList(playListModelItem) + singleArtistProxyModel.setMediaPlayList(playListModelItem) } onPlayListLoadFailed: @@ -250,6 +254,14 @@ } onDisplayTrackError: messageNotification.showNotification(i18n("Error when playing %1", "" + fileName), 3000) + + Component.onCompleted: { + allAlbumsProxyModel.setAudioControl(manageAudioPlayer) + allArtistsProxyModel.setAudioControl(manageAudioPlayer) + allTracksProxyModel.setAudioControl(manageAudioPlayer) + singleAlbumProxyModel.setAudioControl(manageAudioPlayer) + singleArtistProxyModel.setAudioControl(manageAudioPlayer) + } } ManageMediaPlayerControl { @@ -522,26 +534,26 @@ firstPage: GridBrowserView { id: allAlbumsView - focus: true + tempMediaPlayList: playListModelItem + tempMediaControl: manageAudioPlayer - isFirstPage: true + focus: true - model: AlbumFilterProxyModel { - sourceModel: allListeners.allAlbumsModel - } + model: allAlbumsProxyModel + image: elisaTheme.albumIcon mainTitle: i18nc("Title of the view of all albums", "Albums") onEnqueue: playListModelItem.enqueue(data) onReplaceAndPlay: { playListModelItem.clearAndEnqueue(data) manageAudioPlayer.ensurePlay() } onOpen: { + singleAlbumProxyModel.sourceModel.loadAlbumData(databaseId) localAlbums.stackView.push(albumView, { stackView: localAlbums.stackView, albumName: innerMainTitle, - albumModel: innerModel, albumArtUrl: innerImage, albumId: databaseId }) @@ -567,30 +579,31 @@ firstPage: GridBrowserView { id: allArtistsView focus: true + tempMediaPlayList: playListModelItem + tempMediaControl: manageAudioPlayer - isFirstPage: true showRating: false delegateDisplaySecondaryText: false - model: AlbumFilterProxyModel { - sourceModel: allListeners.allArtistsModel - } + model: allArtistsProxyModel + image: elisaTheme.artistIcon mainTitle: i18nc("Title of the view of all artists", "Artists") onEnqueue: playListModelItem.enqueue(data) onReplaceAndPlay: { playListModelItem.clearAndEnqueue(data) manageAudioPlayer.ensurePlay() } onOpen: { + singleArtistProxyModel.setArtistFilterText(innerMainTitle) localArtists.stackView.push(innerAlbumView, { - model: innerModel, mainTitle: innerMainTitle, secondaryTitle: innerSecondaryTitle, image: innerImage, stackView: localArtists.stackView }) + } onGoBack: localArtists.stackView.pop() } @@ -612,11 +625,11 @@ firstPage: MediaAllTracksView { focus: true - + tempMediaPlayList: playListModelItem + tempMediaControl: manageAudioPlayer stackView: localTracks.stackView - model: AlbumFilterProxyModel { - sourceModel: allListeners.allTracksModel - } + + model: allTracksProxyModel onEnqueue: playListModelItem.enqueue(data) onReplaceAndPlay: { @@ -942,17 +955,23 @@ GridBrowserView { property var stackView + tempMediaPlayList: playListModelItem + tempMediaControl: manageAudioPlayer + model: singleArtistProxyModel + + isSubPage: true onEnqueue: playListModelItem.enqueue(data) onReplaceAndPlay: { playListModelItem.clearAndEnqueue(data) manageAudioPlayer.ensurePlay() } + onOpen: { + singleAlbumProxyModel.sourceModel.loadAlbumData(databaseId) localArtists.stackView.push(albumView, { stackView: localArtists.stackView, albumName: innerMainTitle, - albumModel: innerModel, albumArtUrl: innerImage, albumId: databaseId }) @@ -966,6 +985,10 @@ MediaAlbumView { property var stackView + tempMediaPlayList: playListModelItem + tempMediaControl: manageAudioPlayer + + model: singleAlbumProxyModel onEnqueue: playListModelItem.enqueue(data) @@ -987,22 +1010,9 @@ localArtists.stackView.pop() } } - - allArtistsView.open(allListeners.allArtistsModel.itemModelForName(name), name, '', elisaTheme.defaultArtistImage, '') + allArtistsView.open(name, name, elisaTheme.defaultArtistImage, '') } onGoBack: stackView.pop() - - Connections { - target: allListeners - - onAlbumRemoved: if (albumId === removedAlbumId) { removeAlbum(removedAlbum) } - } - - Connections { - target: allListeners - - onAlbumModified: if (albumId === modifiedAlbumId) { modifyAlbum(modifiedAlbum) } - } } } } diff --git a/src/qml/FilterBar.qml b/src/qml/FilterBar.qml deleted file mode 100644 --- a/src/qml/FilterBar.qml +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2016-2017 Matthieu Gallien - * Copyright 2017 Alexander Stippich - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - - -import QtQuick 2.7 -import QtQuick.Controls 1.2 -import QtQuick.Layouts 1.2 - -FocusScope { - id: filterBar - - property string labelText - property bool showRating: true - property alias filterText: filterTextInput.text - property alias filterRating: ratingFilter.starRating - - ColumnLayout { - anchors.fill: parent - spacing: 0 - - TextMetrics { - id: titleHeight - text: viewTitleHeight.text - font - { - pixelSize: viewTitleHeight.font.pixelSize - bold: viewTitleHeight.font.bold - } - } - - LabelWithToolTip { - id: viewTitleHeight - text: labelText - - color: myPalette.text - font.pixelSize: elisaTheme.defaultFontPixelSize * 2 - - Layout.preferredHeight: implicitHeight - Layout.leftMargin: elisaTheme.layoutHorizontalMargin - Layout.rightMargin: elisaTheme.layoutHorizontalMargin - Layout.topMargin: elisaTheme.layoutVerticalMargin - Layout.bottomMargin: elisaTheme.layoutVerticalMargin - } - - Item { - Layout.fillHeight: true - } - - RowLayout { - id: filterRow - - spacing: 0 - - Layout.fillWidth: true - Layout.bottomMargin: elisaTheme.layoutVerticalMargin - Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 - Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 - - LabelWithToolTip { - text: i18nc("before the TextField input of the filter", "Filter: ") - - font.bold: true - - Layout.bottomMargin: 0 - - color: myPalette.text - } - - TextField { - id: filterTextInput - - horizontalAlignment: TextInput.AlignLeft - - placeholderText: i18nc("Placeholder text in the filter text box", "Filter") - - Layout.bottomMargin: 0 - Layout.preferredWidth: filterBar.width / 2 - - Image { - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: parent.right - anchors.margins: elisaTheme.filterClearButtonMargin - id: clearText - fillMode: Image.PreserveAspectFit - smooth: true - visible: parent.text - source: Qt.resolvedUrl(elisaTheme.clearIcon) - height: parent.height - width: parent.height - sourceSize.width: parent.height - sourceSize.height: parent.height - mirror: LayoutMirroring.enabled - - MouseArea { - id: clear - anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter } - height: parent.parent.height - width: parent.parent.height - onClicked: { - parent.parent.text = "" - parent.parent.forceActiveFocus() - } - } - } - } - - LabelWithToolTip { - text: i18nc("before the Rating widget input of the filter", "Rating: ") - - visible: showRating - - font.bold: true - - color: myPalette.text - - Layout.bottomMargin: 0 - Layout.leftMargin: !LayoutMirroring.enabled ? (elisaTheme.layoutHorizontalMargin * 2) : 0 - Layout.rightMargin: LayoutMirroring.enabled ? (elisaTheme.layoutHorizontalMargin * 2) : 0 - } - - RatingStar { - id: ratingFilter - - visible: showRating - - readOnly: false - - starSize: elisaTheme.ratingStarSize - - Layout.bottomMargin: 0 - } - } - } -} diff --git a/src/qml/GridBrowserView.qml b/src/qml/GridBrowserView.qml --- a/src/qml/GridBrowserView.qml +++ b/src/qml/GridBrowserView.qml @@ -30,17 +30,20 @@ FocusScope { id: gridView - property bool isFirstPage: false + property bool isSubPage: false property string mainTitle property string secondaryTitle property url image - property alias model: proxyModel.model + property alias model: contentDirectoryView.model property bool showRating: true property bool delegateDisplaySecondaryText: true + property var tempMediaPlayList + property var tempMediaControl + signal enqueue(var data) signal replaceAndPlay(var data) - signal open(var innerModel, var innerMainTitle, var innerSecondaryTitle, var innerImage, var databaseId) + signal open(var innerMainTitle, var innerSecondaryTitle, var innerImage, var databaseId) signal goBack() SystemPalette { @@ -56,63 +59,41 @@ anchors.fill: parent spacing: 0 - Loader { - sourceComponent: FilterBar { - id: filterBar - - labelText: mainTitle - showRating: gridView.showRating - - anchors.fill: parent - - Binding { - target: model - property: 'filterText' - value: filterBar.filterText - when: isFirstPage - } + NavigationActionBar { + id: navigationBar - Binding { - target: model - property: 'filterRating' - value: filterBar.filterRating - when: isFirstPage - } - } + mainTitle: gridView.mainTitle + secondaryTitle: gridView.secondaryTitle + image: gridView.image + enableGoBack: isSubPage height: elisaTheme.navigationBarHeight Layout.preferredHeight: height Layout.minimumHeight: height Layout.maximumHeight: height Layout.fillWidth: true - active: isFirstPage - visible: isFirstPage - } - - Loader { - sourceComponent: NavigationActionBar { - id: navigationBar + Binding { + target: model + property: 'filterText' + value: navigationBar.filterText + } - mainTitle: gridView.mainTitle - secondaryTitle: gridView.secondaryTitle - image: gridView.image + Binding { + target: model + property: 'filterRating' + value: navigationBar.filterRating + } - anchors.fill: parent + onEnqueue: model.enqueueToPlayList(tempMediaPlayList) - onEnqueue: gridView.enqueue(mainTitle) - onReplaceAndPlay: gridView.replaceAndPlay(mainTitle) - onGoBack: gridView.goBack() + onReplaceAndPlay: { + tempMediaPlayList.clearPlayList() + model.enqueueToPlayList(tempMediaPlayList) + tempMediaControl.ensurePlay() } - height: elisaTheme.navigationBarHeight - Layout.preferredHeight: height - Layout.minimumHeight: height - Layout.maximumHeight: height - Layout.fillWidth: true - - active: !isFirstPage - visible: !isFirstPage + onGoBack: gridView.goBack() } Rectangle { @@ -139,29 +120,25 @@ cellWidth: elisaTheme.gridDelegateWidth cellHeight: (delegateDisplaySecondaryText ? elisaTheme.gridDelegateHeight : elisaTheme.gridDelegateHeight - secondaryLabelSize.height) - model: DelegateModel { - id: proxyModel - - delegate: GridBrowserDelegate { - width: contentDirectoryView.cellWidth - height: contentDirectoryView.cellHeight - - focus: true - - mainText: model.display - secondaryText: model.secondaryText - imageUrl: model.imageUrl - shadowForImage: model.shadowForImage - containerData: model.containerData - delegateDisplaySecondaryText: gridView.delegateDisplaySecondaryText - - onEnqueue: gridView.enqueue(data) - onReplaceAndPlay: gridView.replaceAndPlay(data) - onOpen: gridView.open(model.childModel, model.display, model.secondaryText, model.imageUrl, model.databaseId) - onSelected: { - forceActiveFocus() - contentDirectoryView.currentIndex = model.index - } + delegate: GridBrowserDelegate { + width: contentDirectoryView.cellWidth + height: contentDirectoryView.cellHeight + + focus: true + + mainText: model.display + secondaryText: model.secondaryText + imageUrl: model.imageUrl + shadowForImage: model.shadowForImage + containerData: model.containerData + delegateDisplaySecondaryText: gridView.delegateDisplaySecondaryText + + onEnqueue: gridView.enqueue(data) + onReplaceAndPlay: gridView.replaceAndPlay(data) + onOpen: gridView.open(model.display, model.secondaryText, model.imageUrl, model.databaseId) + onSelected: { + forceActiveFocus() + contentDirectoryView.currentIndex = model.index } } } diff --git a/src/qml/MediaAlbumView.qml b/src/qml/MediaAlbumView.qml --- a/src/qml/MediaAlbumView.qml +++ b/src/qml/MediaAlbumView.qml @@ -32,13 +32,21 @@ property var albumArtUrl property bool isSingleDiscAlbum property var albumId - property alias albumModel: contentDirectoryView.model + property alias model: contentDirectoryView.model + + property var tempMediaPlayList + property var tempMediaControl signal showArtist(var name) signal enqueue(var data) signal replaceAndPlay(var data) signal goBack(); + function loadAlbumData(id) + { + contentDirectoryView.model.sourceModel.loadAlbumData(id) + } + SystemPalette { id: myPalette colorGroup: SystemPalette.Active @@ -48,21 +56,12 @@ id: elisaTheme } - function removeAlbum(removedAlbum) { - albumModel.albumRemoved(removedAlbum) - } - - function modifyAlbum(modifiedAlbum) { - albumModel.albumData = modifiedAlbum - albumModel.albumModified(modifiedAlbum) - } - ColumnLayout { anchors.fill: parent spacing: 0 NavigationActionBar { - id: navBar + id: navigationBar height: elisaTheme.navigationBarHeight @@ -76,15 +75,29 @@ image: (topListing.albumArtUrl ? topListing.albumArtUrl : elisaTheme.defaultAlbumImage) allowArtistNavigation: true - onEnqueue: topListing.enqueue(albumModel.albumData) + onEnqueue: model.enqueueToPlayList(tempMediaPlayList) onReplaceAndPlay: { - topListing.replaceAndPlay(albumModel.albumData) + tempMediaPlayList.clearPlayList() + model.enqueueToPlayList(tempMediaPlayList) + tempMediaControl.ensurePlay() + } + + Binding { + target: contentDirectoryView.model + property: 'filterText' + value: navigationBar.filterText + } + + Binding { + target: contentDirectoryView.model + property: 'filterRating' + value: navigationBar.filterRating } onGoBack: topListing.goBack() - onShowArtist: topListing.showArtist((topListing.albumModel ? topListing.albumModel.author : '')) + onShowArtist: topListing.showArtist(topListing.model.sourceModel.author) } ScrollView { diff --git a/src/qml/MediaAllTracksView.qml b/src/qml/MediaAllTracksView.qml --- a/src/qml/MediaAllTracksView.qml +++ b/src/qml/MediaAllTracksView.qml @@ -31,7 +31,10 @@ id: rootElement property var stackView - property alias model: proxyModel.model + property alias model: contentDirectoryView.model + + property var tempMediaPlayList + property var tempMediaControl signal enqueue(var data) signal replaceAndPlay(var data) @@ -49,9 +52,13 @@ anchors.fill: parent spacing: 0 - FilterBar { - id: filterBar - labelText: i18nc("Title of the view of all tracks", "Tracks") + NavigationActionBar { + id: navigationBar + + mainTitle: i18nc("Title of the view of all tracks", "Tracks") + secondaryTitle: "" + image: elisaTheme.tracksIcon + enableGoBack: false height: elisaTheme.navigationBarHeight Layout.preferredHeight: height @@ -62,13 +69,21 @@ Binding { target: model property: 'filterText' - value: filterBar.filterText + value: navigationBar.filterText } Binding { target: model property: 'filterRating' - value: filterBar.filterRating + value: navigationBar.filterRating + } + + onEnqueue: model.enqueueToPlayList(tempMediaPlayList) + + onReplaceAndPlay: { + tempMediaPlayList.clearPlayList() + model.enqueueToPlayList(tempMediaPlayList) + tempMediaControl.ensurePlay() } } @@ -88,77 +103,73 @@ focus: true - model: DelegateModel { - id: proxyModel - - delegate: MediaTrackDelegate { - id: entry + delegate: MediaTrackDelegate { + id: entry - width: contentDirectoryView.width - height: elisaTheme.trackDelegateHeight + width: contentDirectoryView.width + height: elisaTheme.trackDelegateHeight - focus: true + focus: true - isAlternateColor: (index % 2) === 1 + isAlternateColor: (index % 2) === 1 - title: if (model != undefined && model.title !== undefined) - model.title + title: if (model != undefined && model.title !== undefined) + model.title + else + '' + artist: if (model != undefined && model.artist !== undefined) + model.artist + else + '' + albumName: if (model != undefined && model.album !== undefined) + model.album else '' - artist: if (model != undefined && model.artist !== undefined) - model.artist + albumArtist: if (model != undefined && model.albumArtist !== undefined) + model.albumArtist + else + '' + duration: if (model != undefined && model.duration !== undefined) + model.duration + else + '' + trackNumber: if (model != undefined && model.trackNumber !== undefined) + model.trackNumber + else + '' + discNumber: if (model != undefined && model.discNumber !== undefined) + model.discNumber else '' - albumName: if (model != undefined && model.album !== undefined) - model.album - else - '' - albumArtist: if (model != undefined && model.albumArtist !== undefined) - model.albumArtist - else - '' - duration: if (model != undefined && model.duration !== undefined) - model.duration - else - '' - trackNumber: if (model != undefined && model.trackNumber !== undefined) - model.trackNumber - else - '' - discNumber: if (model != undefined && model.discNumber !== undefined) - model.discNumber - else - '' - rating: if (model != undefined && model.rating !== undefined) - model.rating + rating: if (model != undefined && model.rating !== undefined) + model.rating + else + 0 + trackData: if (model != undefined && model.containerData !== undefined) + model.containerData + else + '' + coverImage: if (model != undefined && model.image !== undefined) + model.image else - 0 - trackData: if (model != undefined && model.containerData !== undefined) - model.containerData + '' + trackResource: if (model != undefined && model.trackResource !== undefined) + model.trackResource else '' - coverImage: if (model != undefined && model.image !== undefined) - model.image - else - '' - trackResource: if (model != undefined && model.trackResource !== undefined) - model.trackResource + isFirstTrackOfDisc: if (model != undefined && model.isFirstTrackOfDisc !== undefined) + model.isFirstTrackOfDisc + else + false + isSingleDiscAlbum: if (model != undefined && model.isSingleDiscAlbum !== undefined) + model.isSingleDiscAlbum else - '' - isFirstTrackOfDisc: if (model != undefined && model.isFirstTrackOfDisc !== undefined) - model.isFirstTrackOfDisc - else - false - isSingleDiscAlbum: if (model != undefined && model.isSingleDiscAlbum !== undefined) - model.isSingleDiscAlbum - else - true - - onEnqueue: rootElement.enqueue(data) - onReplaceAndPlay: rootElement.replaceAndPlay(data) - - onClicked: contentDirectoryView.currentIndex = index - } + true + + onEnqueue: rootElement.enqueue(data) + onReplaceAndPlay: rootElement.replaceAndPlay(data) + + onClicked: contentDirectoryView.currentIndex = index } } } diff --git a/src/qml/MediaPlayListView.qml b/src/qml/MediaPlayListView.qml --- a/src/qml/MediaPlayListView.qml +++ b/src/qml/MediaPlayListView.qml @@ -119,73 +119,88 @@ anchors.fill: parent spacing: 0 - ColumnLayout { + RowLayout { height: elisaTheme.navigationBarHeight Layout.preferredHeight: elisaTheme.navigationBarHeight Layout.minimumHeight: elisaTheme.navigationBarHeight Layout.maximumHeight: elisaTheme.navigationBarHeight - spacing: 0 + Image { + id: mainIcon + source: elisaTheme.playlistIcon - TextMetrics { - id: titleHeight - text: viewTitleHeight.text - font - { - pixelSize: viewTitleHeight.font.pixelSize - bold: viewTitleHeight.font.bold - } - } + asynchronous: true - LabelWithToolTip { - id: viewTitleHeight - text: i18nc("Title of the view of the playlist", "Now Playing") + sourceSize.height: elisaTheme.coverImageSize / 2 + sourceSize.width: elisaTheme.coverImageSize / 2 - color: myPalette.text - font.pixelSize: elisaTheme.defaultFontPixelSize * 2 - Layout.topMargin: elisaTheme.layoutVerticalMargin + fillMode: Image.PreserveAspectFit - Layout.leftMargin: elisaTheme.layoutHorizontalMargin - Layout.rightMargin: elisaTheme.layoutHorizontalMargin - } + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft - Item { - Layout.fillHeight: true + Layout.preferredHeight: elisaTheme.coverImageSize / 2 + Layout.minimumHeight: elisaTheme.coverImageSize / 2 + Layout.maximumHeight: elisaTheme.coverImageSize / 2 + Layout.preferredWidth: elisaTheme.coverImageSize / 2 + Layout.minimumWidth: elisaTheme.coverImageSize / 2 + Layout.maximumWidth: elisaTheme.coverImageSize / 2 + Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 } - RowLayout { - Layout.fillWidth: true - Layout.bottomMargin: elisaTheme.layoutVerticalMargin - Layout.leftMargin: elisaTheme.layoutHorizontalMargin - Layout.rightMargin: elisaTheme.layoutHorizontalMargin + ColumnLayout { + spacing: 0 - CheckBox { - id: shuffleOption + Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 - text: i18n("Shuffle") + TextMetrics { + id: titleHeight + text: viewTitleHeight.text + font + { + pixelSize: viewTitleHeight.font.pixelSize + bold: viewTitleHeight.font.bold + } } - CheckBox { - id: repeatOption + LabelWithToolTip { + id: viewTitleHeight + text: i18nc("Title of the view of the playlist", "Now Playing") + + color: myPalette.text + font.pixelSize: elisaTheme.defaultFontPixelSize * 2 + Layout.topMargin: elisaTheme.layoutVerticalMargin + } + + LabelWithToolTip { + id: playListInfo - text: i18n("Repeat") + text: i18np("1 track", "%1 tracks", playListModel.tracksCount) - Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 - Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + color: myPalette.text + + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter } Item { - Layout.fillWidth: true + Layout.fillHeight: true } - LabelWithToolTip { - id: playListInfo + RowLayout { + Layout.bottomMargin: elisaTheme.layoutVerticalMargin - text: i18np("1 track", "%1 tracks", playListModel.tracksCount) + CheckBox { + id: shuffleOption - color: myPalette.text + text: i18n("Shuffle") + } - Layout.alignment: Qt.AlignRight + CheckBox { + id: repeatOption + + text: i18n("Repeat") + } } } } diff --git a/src/qml/NavigationActionBar.qml b/src/qml/NavigationActionBar.qml --- a/src/qml/NavigationActionBar.qml +++ b/src/qml/NavigationActionBar.qml @@ -23,14 +23,21 @@ import QtQuick.Controls 1.4 import QtQuick.Window 2.0 -Item { +FocusScope { id: navigationBar property string mainTitle property string secondaryTitle property url image property bool allowArtistNavigation: false + property string labelText + property bool showRating: true + property alias filterText: filterTextInput.text + property alias filterRating: ratingFilter.starRating + property bool enableGoBack: true + property bool collapsed: true + signal enqueue(); signal replaceAndPlay(); signal goBack(); @@ -43,149 +50,288 @@ onTriggered: goBack() } - RowLayout { + Action { + id: showFilterAction + text: collapsed ? i18nc("Show filters in the navigation bar", "Show Search Options") : i18nc("Hide filters in the navigation bar", "Hide Search Options") + iconName: collapsed ? "go-down-search" : "go-up-search" + onTriggered: collapsed ? collapsed = false : collapsed = true + } + + ColumnLayout { anchors.fill: parent + spacing: 0 anchors.margins: { top: elisaTheme.layoutVerticalMargin bottom: elisaTheme.layoutVerticalMargin } - spacing: 0 + RowLayout { + spacing: 0 + Layout.alignment: Qt.AlignTop - ToolButton { - action: goPreviousAction - Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 - Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 - } + ToolButton { + action: goPreviousAction + Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + visible: enableGoBack + } - Image { - id: mainIcon - source: image + Image { + id: mainIcon + source: image - asynchronous: true + asynchronous: true - sourceSize.height: elisaTheme.coverImageSize / 2 - sourceSize.width: elisaTheme.coverImageSize / 2 + sourceSize.height: elisaTheme.coverImageSize / 2 + sourceSize.width: elisaTheme.coverImageSize / 2 - fillMode: Image.PreserveAspectFit + fillMode: Image.PreserveAspectFit - Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft - Layout.preferredHeight: elisaTheme.coverImageSize / 2 - Layout.minimumHeight: elisaTheme.coverImageSize / 2 - Layout.maximumHeight: elisaTheme.coverImageSize / 2 - Layout.preferredWidth: elisaTheme.coverImageSize / 2 - Layout.minimumWidth: elisaTheme.coverImageSize / 2 - Layout.maximumWidth: elisaTheme.coverImageSize / 2 - Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 - Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 - } + Layout.preferredHeight: elisaTheme.coverImageSize / 2 + Layout.minimumHeight: elisaTheme.coverImageSize / 2 + Layout.maximumHeight: elisaTheme.coverImageSize / 2 + Layout.preferredWidth: elisaTheme.coverImageSize / 2 + Layout.minimumWidth: elisaTheme.coverImageSize / 2 + Layout.maximumWidth: elisaTheme.coverImageSize / 2 + Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + } - ColumnLayout { - Layout.fillHeight: true + ColumnLayout { + Layout.preferredHeight: elisaTheme.coverImageSize / 2 + Layout.minimumHeight: elisaTheme.coverImageSize / 2 + Layout.maximumHeight: elisaTheme.coverImageSize / 2 - spacing: 0 + spacing: 0 - Layout.fillWidth: true - Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 - Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + Layout.fillWidth: true + Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + + TextMetrics { + id: albumTextSize + text: albumLabel.text + font.pixelSize: albumLabel.font.pixelSize + font.bold: albumLabel.font.bold + } - TextMetrics { - id: albumTextSize - text: albumLabel.text - font.pixelSize: albumLabel.font.pixelSize - font.bold: albumLabel.font.bold - } + LabelWithToolTip { + id: albumLabel - LabelWithToolTip { - id: albumLabel + text: secondaryTitle - text: secondaryTitle + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.fillWidth: true - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + elide: Text.ElideRight - elide: Text.ElideRight + color: myPalette.text - color: myPalette.text + font { + pixelSize: elisaTheme.defaultFontPixelSize * 2 + } - font { - pixelSize: elisaTheme.defaultFontPixelSize * 1.5 + visible: secondaryTitle !== "" } - visible: secondaryTitle !== "" - } + TextMetrics { + id: authorTextSize + text: authorLabel.text + font.pixelSize: authorLabel.font.pixelSize + font.bold: authorLabel.font.bold + } - TextMetrics { - id: authorTextSize - text: authorLabel.text - font.pixelSize: authorLabel.font.pixelSize - font.bold: authorLabel.font.bold - } + LabelWithToolTip { + id: authorLabel - LabelWithToolTip { - id: authorLabel + text: mainTitle - text: mainTitle + color: myPalette.text - color: myPalette.text + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.fillWidth: true - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + font { + pixelSize: (secondaryTitle !== "" ? elisaTheme.defaultFontPixelSize : elisaTheme.defaultFontPixelSize * 2) + } - font { - pixelSize: (secondaryTitle !== "" ? elisaTheme.defaultFontPixelSize : elisaTheme.defaultFontPixelSize * 1.5) + elide: Text.ElideRight } - elide: Text.ElideRight - } + Item { + id: emptyBottomFiller - Item { - id: emptyBottomFiller + Layout.fillWidth: true + Layout.fillHeight: true + } - Layout.fillWidth: true - Layout.fillHeight: true - } + RowLayout { + Layout.fillWidth: true + spacing: 0 - RowLayout { - Layout.fillWidth: true - spacing: 0 + Button { + text: i18nc("Add current list to playlist", "Enqueue") + iconName: "media-track-add-amarok" - Button { - text: i18nc("Add current list to playlist", "Enqueue") - iconName: "media-track-add-amarok" + onClicked: enqueue() - onClicked: enqueue() + Layout.leftMargin: 0 + Layout.rightMargin: 0 + } - Layout.leftMargin: 0 - Layout.rightMargin: 0 - } + Button { + text: i18nc("Clear playlist and play", "Replace and Play") + tooltip: i18nc("Clear playlist and add current list to it", "Replace PlayList and Play Now") + iconName: "media-playback-start" - Button { - text: i18nc("Clear playlist and play", "Replace and Play") - tooltip: i18nc("Clear playlist and add current list to it", "Replace PlayList and Play Now") - iconName: "media-playback-start" + onClicked: replaceAndPlay() - onClicked: replaceAndPlay() + Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + } - Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 - Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + Button { + id: showArtistButton + + visible: allowArtistNavigation + text: i18nc("Button to navigate to the artist of the album", "Display Artist") + iconName: "view-media-artist" + + onClicked: showArtist() + + Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + } + + Item { + Layout.fillWidth: true + } + + ToolButton { + action: showFilterAction + + Layout.alignment: Qt.AlignRight + Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + } } + } + } + + RowLayout { + id: filterRow + + spacing: 0 + + Layout.fillWidth: true + Layout.topMargin: elisaTheme.layoutVerticalMargin * 2 + Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + Layout.alignment: Qt.AlignTop - Button { - id: showArtistButton + LabelWithToolTip { + text: i18nc("before the TextField input of the filter", "Search: ") - visible: allowArtistNavigation - text: i18nc("Button to navigate to the artist of the album", "Display Artist") - iconName: "view-media-artist" + font.bold: true - onClicked: showArtist() + Layout.bottomMargin: 0 - Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 - Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin : 0 + color: myPalette.text + } + + TextField { + id: filterTextInput + + horizontalAlignment: TextInput.AlignLeft + + placeholderText: i18nc("Placeholder text in the filter text box", "Album name, artist, etc.") + + Layout.bottomMargin: 0 + Layout.preferredWidth: navigationBar.width / 2 + + Image { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.margins: elisaTheme.filterClearButtonMargin + id: clearText + fillMode: Image.PreserveAspectFit + smooth: true + visible: parent.text + source: Qt.resolvedUrl(elisaTheme.clearIcon) + height: parent.height + width: parent.height + sourceSize.width: parent.height + sourceSize.height: parent.height + mirror: LayoutMirroring.enabled + + MouseArea { + id: clear + anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter } + height: parent.parent.height + width: parent.parent.height + onClicked: { + parent.parent.text = "" + parent.parent.forceActiveFocus() + } + } } } + + LabelWithToolTip { + text: i18nc("before the Rating widget input of the filter", "Rating: ") + + visible: showRating + + font.bold: true + + color: myPalette.text + + Layout.bottomMargin: 0 + Layout.leftMargin: !LayoutMirroring.enabled ? (elisaTheme.layoutHorizontalMargin * 2) : 0 + Layout.rightMargin: LayoutMirroring.enabled ? (elisaTheme.layoutHorizontalMargin * 2) : 0 + } + + RatingStar { + id: ratingFilter + + visible: showRating + + readOnly: false + + starSize: elisaTheme.ratingStarSize + + Layout.bottomMargin: 0 + } + } + } + + states: [ + State { + name: 'collapsed' + when: collapsed + PropertyChanges { + target: navigationBar + height: elisaTheme.navigationBarHeight + } + }, + State { + name: 'expanded' + when: !collapsed + PropertyChanges { + target: navigationBar + height: elisaTheme.navigationBarHeight + elisaTheme.navigationBarFilterHeight + } + } + ] + transitions: Transition { + PropertyAnimation { + properties: "height" + easing.type: Easing.Linear + duration: 250 } } } diff --git a/src/qml/Theme.qml b/src/qml/Theme.qml --- a/src/qml/Theme.qml +++ b/src/qml/Theme.qml @@ -25,7 +25,7 @@ property string defaultArtistImage: 'image://icon/view-media-artist' property string defaultBackgroundImage: 'qrc:///background.png' property string artistIcon: 'image://icon/view-media-artist' - property string albumIcon: 'image://icon/media-album-cover' + property string albumIcon: 'image://icon/media-album-cover-manager-amarok' property string playlistIcon: 'image://icon/amarok_playlist' property string tracksIcon: 'image://icon/media-album-track' property string clearIcon: 'image://icon/edit-clear' @@ -68,6 +68,7 @@ property int dragDropPlaceholderHeight: 28 property int navigationBarHeight: 100 + property int navigationBarFilterHeight: 44 property int gridDelegateHeight: 100 + layoutVerticalMargin + fontSize.height * 2 property int gridDelegateWidth: 100 diff --git a/src/resources.qrc b/src/resources.qrc --- a/src/resources.qrc +++ b/src/resources.qrc @@ -20,7 +20,6 @@ qml/TrackImportNotification.qml qml/TopNotificationItem.qml qml/ViewSelector.qml - qml/FilterBar.qml qml/MediaTrackDelegate.qml qml/MediaAlbumTrackDelegate.qml qml/MediaTrackMetadataView.qml diff --git a/src/windows/WindowsTheme.qml b/src/windows/WindowsTheme.qml --- a/src/windows/WindowsTheme.qml +++ b/src/windows/WindowsTheme.qml @@ -25,7 +25,7 @@ property string defaultArtistImage: 'image://icon/view-media-artist' property string defaultBackgroundImage: 'qrc:///background.png' property string artistIcon: 'image://icon/view-media-artist' - property string albumIcon: 'image://icon/media-album-cover' + property string albumIcon: 'image://icon/media-album-cover-manager-amarok' property string playlistIcon: 'image://icon/amarok_playlist' property string tracksIcon: 'image://icon/media-album-track' property string clearIcon: 'image://icon/edit-clear' @@ -68,6 +68,7 @@ property int dragDropPlaceholderHeight: 28 property int navigationBarHeight: 100 + property int navigationBarFilterHeight: 44 property int gridDelegateHeight: 100 + layoutVerticalMargin + fontSize.height * 2 property int gridDelegateWidth: 100