diff --git a/src/elisaapplication.cpp b/src/elisaapplication.cpp index 4646723c..f46464e7 100644 --- a/src/elisaapplication.cpp +++ b/src/elisaapplication.cpp @@ -1,522 +1,530 @@ /* * Copyright 2017 Matthieu Gallien * Copyright (C) 2012 Aleix Pol Gonzalez * * 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 "elisaapplication.h" #include "musiclistenersmanager.h" #include "models/allalbumsproxymodel.h" #include "models/alltracksproxymodel.h" #include "models/allartistsproxymodel.h" #include "models/singleartistproxymodel.h" #include "models/singlealbumproxymodel.h" #include "models/filebrowserproxymodel.h" #include "mediaplaylist.h" #include "audiowrapper.h" #include "manageaudioplayer.h" #include "managemediaplayercontrol.h" #include "manageheaderbar.h" #include "elisa_settings.h" #include #if defined KF5ConfigWidgets_FOUND && KF5ConfigWidgets_FOUND #include #endif #if defined KF5XmlGui_FOUND && KF5XmlGui_FOUND #include #include #include #include #include #endif #if defined KF5KCMUtils_FOUND && KF5KCMUtils_FOUND #include #endif #include #include #include #include #include #include #include #include #include #include #include #include class ElisaApplicationPrivate { public: explicit ElisaApplicationPrivate(QObject *parent) #if defined KF5XmlGui_FOUND && KF5XmlGui_FOUND : mCollection(parent) #endif { Q_UNUSED(parent) auto configurationFileName = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); configurationFileName += QStringLiteral("/elisarc"); Elisa::ElisaConfiguration::instance(configurationFileName); Elisa::ElisaConfiguration::self()->load(); Elisa::ElisaConfiguration::self()->save(); } #if defined KF5XmlGui_FOUND && KF5XmlGui_FOUND KActionCollection mCollection; #endif QStringList mArguments; std::unique_ptr mMusicManager; std::unique_ptr mAllAlbumsProxyModel; std::unique_ptr mAllArtistsProxyModel; std::unique_ptr mAllTracksProxyModel; std::unique_ptr mAllGenresProxyModel; std::unique_ptr mAllComposersProxyModel; std::unique_ptr mAllLyricistsProxyModel; std::unique_ptr mSingleArtistProxyModel; std::unique_ptr mSingleAlbumProxyModel; std::unique_ptr mFileBrowserProxyModel; std::unique_ptr mMediaPlayList; std::unique_ptr mAudioWrapper; std::unique_ptr mAudioControl; std::unique_ptr mPlayerControl; std::unique_ptr mManageHeaderBar; }; ElisaApplication::ElisaApplication(QObject *parent) : QObject(parent), d(std::make_unique(this)) { } ElisaApplication::~ElisaApplication() = default; void ElisaApplication::setupActions(const QString &actionName) { #if defined KF5XmlGui_FOUND && KF5XmlGui_FOUND if (actionName == QStringLiteral("file_quit")) { auto quitAction = KStandardAction::quit(QCoreApplication::instance(), &QCoreApplication::quit, &d->mCollection); d->mCollection.addAction(actionName, quitAction); } if (actionName == QStringLiteral("help_contents") && KAuthorized::authorizeAction(actionName)) { auto handBookAction = KStandardAction::helpContents(this, &ElisaApplication::appHelpActivated, &d->mCollection); d->mCollection.addAction(handBookAction->objectName(), handBookAction); } if (actionName == QStringLiteral("help_report_bug") && KAuthorized::authorizeAction(actionName) && !KAboutData::applicationData().bugAddress().isEmpty()) { auto reportBugAction = KStandardAction::reportBug(this, &ElisaApplication::reportBug, &d->mCollection); d->mCollection.addAction(reportBugAction->objectName(), reportBugAction); } if (actionName == QStringLiteral("help_about_app") && KAuthorized::authorizeAction(actionName)) { auto aboutAppAction = KStandardAction::aboutApp(this, &ElisaApplication::aboutApplication, this); d->mCollection.addAction(aboutAppAction->objectName(), aboutAppAction); } if (actionName == QStringLiteral("options_configure") && KAuthorized::authorizeAction(actionName)) { auto preferencesAction = KStandardAction::preferences(this, &ElisaApplication::configureElisa, this); d->mCollection.addAction(preferencesAction->objectName(), preferencesAction); } if (actionName == QStringLiteral("options_configure_keybinding") && KAuthorized::authorizeAction(actionName)) { auto keyBindingsAction = KStandardAction::keyBindings(this, &ElisaApplication::configureShortcuts, this); d->mCollection.addAction(keyBindingsAction->objectName(), keyBindingsAction); } if (actionName == QStringLiteral("go_back") && KAuthorized::authorizeAction(actionName)) { auto goBackAction = KStandardAction::back(this, &ElisaApplication::goBack, this); d->mCollection.addAction(goBackAction->objectName(), goBackAction); } + if (actionName == QStringLiteral("toggle_playlist") && KAuthorized::authorizeAction(actionName)) { + auto togglePlaylistAction = d->mCollection.addAction(actionName, this, &ElisaApplication::togglePlaylist); + togglePlaylistAction->setShortcut(QKeySequence(Qt::Key_F9)); + togglePlaylistAction->setText(QStringLiteral("Toggle Playlist")); + } + if (actionName == QStringLiteral("edit_find") && KAuthorized::authorizeAction(actionName)) { auto findAction = KStandardAction::find(this, &ElisaApplication::find, this); d->mCollection.addAction(findAction->objectName(), findAction); } d->mCollection.readSettings(); #endif } void ElisaApplication::setArguments(const QStringList &newArguments) { if (d->mArguments == newArguments) { return; } d->mArguments = checkFileListAndMakeAbsolute(newArguments, QDir::currentPath()); Q_EMIT argumentsChanged(); if (!d->mArguments.isEmpty()) { Q_EMIT enqueue(d->mArguments); } } void ElisaApplication::activateActionRequested(const QString &actionName, const QVariant ¶meter) { Q_UNUSED(actionName) Q_UNUSED(parameter) } void ElisaApplication::activateRequested(const QStringList &arguments, const QString &workingDirectory) { auto realArguments = arguments; if (realArguments.size() > 1) { realArguments.removeFirst(); Q_EMIT enqueue(checkFileListAndMakeAbsolute(realArguments, workingDirectory)); } } void ElisaApplication::openRequested(const QList &uris) { Q_UNUSED(uris) } void ElisaApplication::appHelpActivated() { QDesktopServices::openUrl(QUrl(QStringLiteral("help:/"))); } void ElisaApplication::aboutApplication() { #if defined KF5XmlGui_FOUND && KF5XmlGui_FOUND static QPointer dialog; if (!dialog) { dialog = new KAboutApplicationDialog(KAboutData::applicationData(), nullptr); dialog->setAttribute(Qt::WA_DeleteOnClose); } dialog->show(); #endif } void ElisaApplication::reportBug() { #if defined KF5XmlGui_FOUND && KF5XmlGui_FOUND static QPointer dialog; if (!dialog) { dialog = new KBugReport(KAboutData::applicationData(), nullptr); dialog->setAttribute(Qt::WA_DeleteOnClose); } dialog->show(); #endif } void ElisaApplication::configureShortcuts() { #if defined KF5XmlGui_FOUND && KF5XmlGui_FOUND KShortcutsDialog dlg(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, nullptr); dlg.setModal(true); dlg.addCollection(&d->mCollection); dlg.configure(); #endif } void ElisaApplication::configureElisa() { #if defined KF5KCMUtils_FOUND && KF5KCMUtils_FOUND KCMultiDialog configurationDialog; configurationDialog.addModule(QStringLiteral("kcm_elisa_local_file")); configurationDialog.setModal(true); configurationDialog.exec(); #endif } void ElisaApplication::goBack() {} void ElisaApplication::find() {} +void ElisaApplication::togglePlaylist() {} + QStringList ElisaApplication::checkFileListAndMakeAbsolute(const QStringList &filesList, const QString &workingDirectory) const { QStringList filesToOpen; for (const auto &oneFile : filesList) { auto newFile = QFileInfo(oneFile); if (newFile.isRelative()) { newFile = QFileInfo(workingDirectory + QStringLiteral("/") + oneFile); } if (newFile.exists()) { filesToOpen.push_back(newFile.canonicalFilePath()); } } return filesToOpen; } void ElisaApplication::initialize() { initializeModels(); initializePlayer(); } void ElisaApplication::initializeModels() { d->mMusicManager = std::make_unique(); Q_EMIT musicManagerChanged(); d->mAllAlbumsProxyModel = std::make_unique(); Q_EMIT allAlbumsProxyModelChanged(); d->mAllArtistsProxyModel = std::make_unique(); Q_EMIT allArtistsProxyModelChanged(); d->mAllGenresProxyModel = std::make_unique(); Q_EMIT allGenresProxyModelChanged(); d->mAllComposersProxyModel = std::make_unique(); Q_EMIT allComposersProxyModelChanged(); d->mAllLyricistsProxyModel = std::make_unique(); Q_EMIT allLyricistsProxyModelChanged(); d->mAllTracksProxyModel = std::make_unique(); Q_EMIT allTracksProxyModelChanged(); d->mSingleArtistProxyModel = std::make_unique(); Q_EMIT singleArtistProxyModelChanged(); d->mSingleAlbumProxyModel = std::make_unique(); Q_EMIT singleAlbumProxyModelChanged(); d->mFileBrowserProxyModel = std::make_unique(); Q_EMIT fileBrowserProxyModelChanged(); d->mMediaPlayList = std::make_unique(); Q_EMIT mediaPlayListChanged(); d->mMusicManager->setElisaApplication(this); d->mMediaPlayList->setMusicListenersManager(d->mMusicManager.get()); QObject::connect(this, &ElisaApplication::enqueue, d->mMediaPlayList.get(), &MediaPlayList::enqueueAndPlay); d->mAllAlbumsProxyModel->setSourceModel(d->mMusicManager->allAlbumsModel()); d->mAllArtistsProxyModel->setSourceModel(d->mMusicManager->allArtistsModel()); d->mAllGenresProxyModel->setSourceModel(d->mMusicManager->allGenresModel()); d->mAllComposersProxyModel->setSourceModel(d->mMusicManager->allComposersModel()); d->mAllLyricistsProxyModel->setSourceModel(d->mMusicManager->allLyricistsModel()); d->mAllTracksProxyModel->setSourceModel(d->mMusicManager->allTracksModel()); d->mSingleArtistProxyModel->setSourceModel(d->mMusicManager->allAlbumsModel()); d->mSingleAlbumProxyModel->setSourceModel(d->mMusicManager->albumModel()); QObject::connect(d->mAllAlbumsProxyModel.get(), &AllAlbumsProxyModel::albumToEnqueue, d->mMediaPlayList.get(), static_cast &, ElisaUtils::PlayListEnqueueMode, ElisaUtils::PlayListEnqueueTriggerPlay)>(&MediaPlayList::enqueue)); QObject::connect(d->mAllArtistsProxyModel.get(), &AllArtistsProxyModel::artistToEnqueue, d->mMediaPlayList.get(), &MediaPlayList::enqueueArtists); QObject::connect(d->mAllTracksProxyModel.get(), &AllTracksProxyModel::trackToEnqueue, d->mMediaPlayList.get(), static_cast &, ElisaUtils::PlayListEnqueueMode, ElisaUtils::PlayListEnqueueTriggerPlay)>(&MediaPlayList::enqueue)); QObject::connect(d->mSingleArtistProxyModel.get(), &SingleArtistProxyModel::albumToEnqueue, d->mMediaPlayList.get(), static_cast &, ElisaUtils::PlayListEnqueueMode, ElisaUtils::PlayListEnqueueTriggerPlay)>(&MediaPlayList::enqueue)); QObject::connect(d->mSingleAlbumProxyModel.get(), &SingleAlbumProxyModel::trackToEnqueue, d->mMediaPlayList.get(), static_cast &, ElisaUtils::PlayListEnqueueMode, ElisaUtils::PlayListEnqueueTriggerPlay)>(&MediaPlayList::enqueue)); QObject::connect(d->mFileBrowserProxyModel.get(), &FileBrowserProxyModel::filesToEnqueue, d->mMediaPlayList.get(), static_cast &, ElisaUtils::PlayListEnqueueMode, ElisaUtils::PlayListEnqueueTriggerPlay)>(&MediaPlayList::enqueue)); } void ElisaApplication::initializePlayer() { d->mAudioWrapper = std::make_unique(); Q_EMIT audioPlayerChanged(); d->mAudioControl = std::make_unique(); Q_EMIT audioControlChanged(); d->mPlayerControl = std::make_unique(); Q_EMIT playerControlChanged(); d->mManageHeaderBar = std::make_unique(); Q_EMIT manageHeaderBarChanged(); d->mAudioControl->setAlbumNameRole(MediaPlayList::AlbumRole); d->mAudioControl->setArtistNameRole(MediaPlayList::ArtistRole); d->mAudioControl->setTitleRole(MediaPlayList::TitleRole); d->mAudioControl->setUrlRole(MediaPlayList::ResourceRole); d->mAudioControl->setIsPlayingRole(MediaPlayList::IsPlayingRole); d->mAudioControl->setPlayListModel(d->mMediaPlayList.get()); QObject::connect(d->mAudioControl.get(), &ManageAudioPlayer::playerPlay, d->mAudioWrapper.get(), &AudioWrapper::play); QObject::connect(d->mAudioControl.get(), &ManageAudioPlayer::playerPause, d->mAudioWrapper.get(), &AudioWrapper::pause); QObject::connect(d->mAudioControl.get(), &ManageAudioPlayer::playerStop, d->mAudioWrapper.get(), &AudioWrapper::stop); QObject::connect(d->mAudioControl.get(), &ManageAudioPlayer::seek, d->mAudioWrapper.get(), &AudioWrapper::seek); QObject::connect(d->mAudioControl.get(), &ManageAudioPlayer::skipNextTrack, d->mMediaPlayList.get(), &MediaPlayList::skipNextTrack); QObject::connect(d->mAudioControl.get(), &ManageAudioPlayer::sourceInError, d->mMediaPlayList.get(), &MediaPlayList::trackInError); QObject::connect(d->mAudioControl.get(), &ManageAudioPlayer::sourceInError, d->mMusicManager.get(), &MusicListenersManager::playBackError); QObject::connect(d->mAudioControl.get(), &ManageAudioPlayer::playerSourceChanged, d->mAudioWrapper.get(), &AudioWrapper::setSource); QObject::connect(d->mMediaPlayList.get(), &MediaPlayList::ensurePlay, d->mAudioControl.get(), &ManageAudioPlayer::ensurePlay); QObject::connect(d->mMediaPlayList.get(), &MediaPlayList::playListFinished, d->mAudioControl.get(), &ManageAudioPlayer::playListFinished); QObject::connect(d->mMediaPlayList.get(), &MediaPlayList::currentTrackChanged, d->mAudioControl.get(), &ManageAudioPlayer::setCurrentTrack); QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::playbackStateChanged, d->mAudioControl.get(), &ManageAudioPlayer::setPlayerPlaybackState); QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::statusChanged, d->mAudioControl.get(), &ManageAudioPlayer::setPlayerStatus); QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::errorChanged, d->mAudioControl.get(), &ManageAudioPlayer::setPlayerError); QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::durationChanged, d->mAudioControl.get(), &ManageAudioPlayer::setAudioDuration); QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::seekableChanged, d->mAudioControl.get(), &ManageAudioPlayer::setPlayerIsSeekable); QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::positionChanged, d->mAudioControl.get(), &ManageAudioPlayer::setPlayerPosition); d->mPlayerControl->setPlayListModel(d->mMediaPlayList.get()); QObject::connect(d->mMediaPlayList.get(), &MediaPlayList::currentTrackChanged, d->mPlayerControl.get(), &ManageMediaPlayerControl::setCurrentTrack); QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::playing, d->mPlayerControl.get(), &ManageMediaPlayerControl::playerPlaying); QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::paused, d->mPlayerControl.get(), &ManageMediaPlayerControl::playerPaused); QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::stopped, d->mPlayerControl.get(), &ManageMediaPlayerControl::playerStopped); d->mManageHeaderBar->setTitleRole(MediaPlayList::TitleRole); d->mManageHeaderBar->setAlbumRole(MediaPlayList::AlbumRole); d->mManageHeaderBar->setArtistRole(MediaPlayList::ArtistRole); d->mManageHeaderBar->setImageRole(MediaPlayList::ImageRole); d->mManageHeaderBar->setIsValidRole(MediaPlayList::IsValidRole); d->mManageHeaderBar->setPlayListModel(d->mMediaPlayList.get()); QObject::connect(d->mMediaPlayList.get(), &MediaPlayList::currentTrackChanged, d->mManageHeaderBar.get(), &ManageHeaderBar::setCurrentTrack); if (!d->mArguments.isEmpty()) { Q_EMIT enqueue(d->mArguments); } } QAction * ElisaApplication::action(const QString& name) { #if defined KF5XmlGui_FOUND && KF5XmlGui_FOUND auto resultAction = d->mCollection.action(name); if (!resultAction) { setupActions(name); resultAction = d->mCollection.action(name); } return resultAction; #else Q_UNUSED(name); return new QAction(); #endif } QString ElisaApplication::iconName(const QIcon& icon) { return icon.name(); } const QStringList &ElisaApplication::arguments() const { return d->mArguments; } MusicListenersManager *ElisaApplication::musicManager() const { return d->mMusicManager.get(); } AllAlbumsProxyModel *ElisaApplication::allAlbumsProxyModel() const { return d->mAllAlbumsProxyModel.get(); } AllArtistsProxyModel *ElisaApplication::allArtistsProxyModel() const { return d->mAllArtistsProxyModel.get(); } AllArtistsProxyModel *ElisaApplication::allGenresProxyModel() const { return d->mAllGenresProxyModel.get(); } AllArtistsProxyModel *ElisaApplication::allComposersProxyModel() const { return d->mAllComposersProxyModel.get(); } AllArtistsProxyModel *ElisaApplication::allLyricistsProxyModel() const { return d->mAllLyricistsProxyModel.get(); } AllTracksProxyModel *ElisaApplication::allTracksProxyModel() const { return d->mAllTracksProxyModel.get(); } SingleArtistProxyModel *ElisaApplication::singleArtistProxyModel() const { return d->mSingleArtistProxyModel.get(); } SingleAlbumProxyModel *ElisaApplication::singleAlbumProxyModel() const { return d->mSingleAlbumProxyModel.get(); } FileBrowserProxyModel *ElisaApplication::fileBrowserProxyModel() const { return d->mFileBrowserProxyModel.get(); } MediaPlayList *ElisaApplication::mediaPlayList() const { return d->mMediaPlayList.get(); } AudioWrapper *ElisaApplication::audioPlayer() const { return d->mAudioWrapper.get(); } ManageAudioPlayer *ElisaApplication::audioControl() const { return d->mAudioControl.get(); } ManageMediaPlayerControl *ElisaApplication::playerControl() const { return d->mPlayerControl.get(); } ManageHeaderBar *ElisaApplication::manageHeaderBar() const { return d->mManageHeaderBar.get(); } #include "moc_elisaapplication.cpp" diff --git a/src/elisaapplication.h b/src/elisaapplication.h index da49b50f..39a06242 100644 --- a/src/elisaapplication.h +++ b/src/elisaapplication.h @@ -1,237 +1,239 @@ /* * Copyright 2017 Matthieu Gallien * * 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 ELISAAPPLICATION_H #define ELISAAPPLICATION_H #include "elisaLib_export.h" #include "config-upnp-qt.h" #include #include #include class QIcon; class QAction; class MusicListenersManager; class AllAlbumsProxyModel; class AllArtistsProxyModel; class AllTracksProxyModel; class SingleArtistProxyModel; class SingleAlbumProxyModel; class FileBrowserProxyModel; class MediaPlayList; class AudioWrapper; class ManageAudioPlayer; class ManageMediaPlayerControl; class ManageHeaderBar; class ElisaApplicationPrivate; class ELISALIB_EXPORT ElisaApplication : public QObject { Q_OBJECT Q_PROPERTY(QStringList arguments READ arguments WRITE setArguments NOTIFY argumentsChanged) Q_PROPERTY(MusicListenersManager *musicManager READ musicManager NOTIFY musicManagerChanged) Q_PROPERTY(AllAlbumsProxyModel *allAlbumsProxyModel READ allAlbumsProxyModel NOTIFY allAlbumsProxyModelChanged) Q_PROPERTY(AllArtistsProxyModel *allArtistsProxyModel READ allArtistsProxyModel NOTIFY allArtistsProxyModelChanged) Q_PROPERTY(AllTracksProxyModel *allTracksProxyModel READ allTracksProxyModel NOTIFY allTracksProxyModelChanged) Q_PROPERTY(AllArtistsProxyModel *allGenresProxyModel READ allGenresProxyModel NOTIFY allGenresProxyModelChanged) Q_PROPERTY(AllArtistsProxyModel *allComposersProxyModel READ allComposersProxyModel NOTIFY allComposersProxyModelChanged) Q_PROPERTY(AllArtistsProxyModel *allLyricistsProxyModel READ allLyricistsProxyModel NOTIFY allLyricistsProxyModelChanged) Q_PROPERTY(SingleArtistProxyModel *singleArtistProxyModel READ singleArtistProxyModel NOTIFY singleArtistProxyModelChanged) Q_PROPERTY(SingleAlbumProxyModel *singleAlbumProxyModel READ singleAlbumProxyModel NOTIFY singleAlbumProxyModelChanged) Q_PROPERTY(FileBrowserProxyModel *fileBrowserProxyModel READ fileBrowserProxyModel NOTIFY fileBrowserProxyModelChanged) Q_PROPERTY(MediaPlayList *mediaPlayList READ mediaPlayList NOTIFY mediaPlayListChanged) Q_PROPERTY(AudioWrapper *audioPlayer READ audioPlayer NOTIFY audioPlayerChanged) Q_PROPERTY(ManageAudioPlayer *audioControl READ audioControl NOTIFY audioControlChanged) Q_PROPERTY(ManageMediaPlayerControl *playerControl READ playerControl NOTIFY playerControlChanged) Q_PROPERTY(ManageHeaderBar *manageHeaderBar READ manageHeaderBar NOTIFY manageHeaderBarChanged) public: explicit ElisaApplication(QObject *parent = nullptr); ~ElisaApplication() override; Q_INVOKABLE QAction* action(const QString& name); Q_INVOKABLE QString iconName(const QIcon& icon); const QStringList &arguments() const; MusicListenersManager *musicManager() const; AllAlbumsProxyModel *allAlbumsProxyModel() const; AllArtistsProxyModel *allArtistsProxyModel() const; AllArtistsProxyModel *allGenresProxyModel() const; AllArtistsProxyModel *allComposersProxyModel() const; AllArtistsProxyModel *allLyricistsProxyModel() const; AllTracksProxyModel *allTracksProxyModel() const; SingleArtistProxyModel *singleArtistProxyModel() const; SingleAlbumProxyModel *singleAlbumProxyModel() const; FileBrowserProxyModel *fileBrowserProxyModel() const; MediaPlayList *mediaPlayList() const; AudioWrapper *audioPlayer() const; ManageAudioPlayer *audioControl() const; ManageMediaPlayerControl *playerControl() const; ManageHeaderBar *manageHeaderBar() const; Q_SIGNALS: void argumentsChanged(); void musicManagerChanged(); void allAlbumsProxyModelChanged(); void allArtistsProxyModelChanged(); void allGenresProxyModelChanged(); void allComposersProxyModelChanged(); void allLyricistsProxyModelChanged(); void allTracksProxyModelChanged(); void singleArtistProxyModelChanged(); void singleAlbumProxyModelChanged(); void fileBrowserProxyModelChanged(); void mediaPlayListChanged(); void audioPlayerChanged(); void audioControlChanged(); void playerControlChanged(); void manageHeaderBarChanged(); void enqueue(const QStringList &files); public Q_SLOTS: void appHelpActivated(); void aboutApplication(); void reportBug(); void configureShortcuts(); void configureElisa(); void setArguments(const QStringList &newArguments); void activateActionRequested(const QString &actionName, const QVariant ¶meter); void activateRequested(const QStringList &arguments, const QString &workingDirectory); void openRequested(const QList< QUrl > &uris); void initialize(); private Q_SLOTS: void goBack(); void find(); + void togglePlaylist(); + private: void initializeModels(); void initializePlayer(); void setupActions(const QString &actionName); QStringList checkFileListAndMakeAbsolute(const QStringList &filesList, const QString &workingDirectory) const; std::unique_ptr d; }; #endif // ELISAAPPLICATION_H diff --git a/src/qml/ApplicationMenu.qml b/src/qml/ApplicationMenu.qml index 5e316779..542dbbed 100644 --- a/src/qml/ApplicationMenu.qml +++ b/src/qml/ApplicationMenu.qml @@ -1,95 +1,105 @@ /* * Copyright 2016-2018 Matthieu Gallien * Copyright 2018 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 //explore menu from Qt 5.10 once we can require it, but it is item-based import QtQuick.Controls 1.4 +import org.kde.elisa 1.0 Menu { id: applicationMenu title: i18nc("open application menu", "Application Menu") property var helpAction: elisa.action("help_contents") property var quitApplication: elisa.action("file_quit") property var reportBugAction: elisa.action("help_report_bug") property var aboutAppAction: elisa.action("help_about_app") property var configureShortcutsAction: elisa.action("options_configure_keybinding") property var configureAction: elisa.action("options_configure") + property var togglePlaylistAction: elisa.action("toggle_playlist") MenuItem { text: configureAction.text shortcut: configureAction.shortcut iconName: elisa.iconName(configureAction.icon) onTriggered: configureAction.trigger() visible: configureAction.text !== "" } MenuItem { text: configureShortcutsAction.text shortcut: configureShortcutsAction.shortcut iconName: elisa.iconName(configureShortcutsAction.icon) onTriggered: configureShortcutsAction.trigger() visible: configureShortcutsAction.text !== "" } + MenuItem { + shortcut: togglePlaylistAction.shortcut + text: contentView.showPlaylist ? i18nc("Hide playlist", "Hide Playlist") : i18nc("Show playlist", "Show Playlist") + iconName: "view-media-playlist" + onTriggered: contentView.showPlaylist = !contentView.showPlaylist + enabled: contentView.currentViewIndex != 0 + } + MenuSeparator { visible: reportBugAction.text !== "" } MenuItem { text: reportBugAction.text shortcut: reportBugAction.shortcut iconName: elisa.iconName(reportBugAction.icon) onTriggered: reportBugAction.trigger() visible: reportBugAction.text !== "" } MenuSeparator { visible: helpAction.text !== "" } MenuItem { text: helpAction.text shortcut: helpAction.shortcut iconName: elisa.iconName(helpAction.icon) onTriggered: helpAction.trigger() visible: helpAction.text !== "" } MenuItem { text: aboutAppAction.text shortcut: aboutAppAction.shortcut iconName: elisa.iconName(aboutAppAction.icon) onTriggered: aboutAppAction.trigger() visible: aboutAppAction.text !== "" } MenuSeparator { visible: quitApplication.text !== "" } MenuItem { text: quitApplication.text shortcut: quitApplication.shortcut iconName: elisa.iconName(quitApplication.icon) onTriggered: quitApplication.trigger() visible: quitApplication.text !== "" } } diff --git a/src/qml/ContentView.qml b/src/qml/ContentView.qml index e8690087..89611bcf 100644 --- a/src/qml/ContentView.qml +++ b/src/qml/ContentView.qml @@ -1,830 +1,851 @@ /* * Copyright 2016-2018 Matthieu Gallien * * 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 2.2 import QtQuick.Layouts 1.2 import QtQuick.Window 2.2 import org.kde.elisa 1.0 RowLayout { id: contentViewContainer spacing: 0 + property bool showPlaylist + property alias currentViewIndex: listViews.currentIndex + signal toggleSearch() function goBack() { viewManager.goBack() } ViewManager { id: viewManager onSwitchAllAlbumsView: { elisa.allAlbumsProxyModel.genreFilterText = '' listViews.currentIndex = 1 localArtistsLoader.opacity = 0 localTracksLoader.opacity = 0 localAlbumsLoader.opacity = 1 localGenresLoader.opacity = 0 localFilesLoader.opacity = 0 } onSwitchOneAlbumView: { elisa.singleAlbumProxyModel.loadAlbumData(databaseId) currentStackView.push(albumView, { mainTitle: mainTitle, secondaryTitle: secondaryTitle, image: imageUrl, stackView: currentStackView, }) oneAlbumViewIsLoaded() } onSwitchAllArtistsView: { elisa.allArtistsProxyModel.genreFilterText = '' listViews.currentIndex = 2 localArtistsLoader.opacity = 1 localTracksLoader.opacity = 0 localAlbumsLoader.opacity = 0 localGenresLoader.opacity = 0 localFilesLoader.opacity = 0 } onSwitchOneArtistView: { elisa.singleArtistProxyModel.setArtistFilterText(mainTitle) elisa.singleArtistProxyModel.genreFilterText = '' currentStackView.push(innerAlbumView, { mainTitle: mainTitle, secondaryTitle: secondaryTitle, image: imageUrl, stackView: currentStackView, }) oneArtistViewIsLoaded() } onSwitchOneArtistFromGenreView: { elisa.singleArtistProxyModel.setArtistFilterText(mainTitle) elisa.singleArtistProxyModel.genreFilterText = genreName currentStackView.push(innerAlbumView, { mainTitle: mainTitle, secondaryTitle: secondaryTitle, image: imageUrl, stackView: currentStackView, }) oneArtistViewIsLoaded() } onSwitchAllTracksView: { elisa.allTracksProxyModel.genreFilterText = '' listViews.currentIndex = 3 localArtistsLoader.opacity = 0 localTracksLoader.opacity = 1 localAlbumsLoader.opacity = 0 localGenresLoader.opacity = 0 localFilesLoader.opacity = 0 } onSwitchAllGenresView: { listViews.currentIndex = 4 localArtistsLoader.opacity = 0 localTracksLoader.opacity = 0 localAlbumsLoader.opacity = 0 localGenresLoader.opacity = 1 localFilesLoader.opacity = 0 } onSwitchFilesBrowserView: { listViews.currentIndex = 5 localArtistsLoader.opacity = 0 localTracksLoader.opacity = 0 localAlbumsLoader.opacity = 0 localGenresLoader.opacity = 0 localFilesLoader.opacity = 1 } onSwitchAllArtistsFromGenreView: { elisa.allArtistsProxyModel.genreFilterText = genreName currentStackView.push(innerArtistView, { contentModel: elisa.allArtistsProxyModel, mainTitle: genreName, secondaryTitle: '', image: elisaTheme.artistIcon, stackView: currentStackView, }) allArtistsFromGenreViewIsLoaded() } onSwitchOffAllViews: { localArtistsLoader.opacity = 0 localTracksLoader.opacity = 0 localAlbumsLoader.opacity = 0 localGenresLoader.opacity = 0 localFilesLoader.opacity = 0 } } ViewSelector { id: listViews Layout.fillHeight: true Layout.preferredWidth: mainWindow.width * 0.11 Layout.maximumWidth: mainWindow.width * 0.11 onSwitchView: if (index === 1) { viewManager.openAllAlbums() } else if (index === 2) { viewManager.openAllArtists() } else if (index === 3) { viewManager.openAllTracks() } else if (index === 4) { viewManager.openAllGenres() } else if (index === 5) { viewManager.openFilesBrowser() } else { viewManager.closeAllViews() } } Rectangle { id: viewSelectorSeparatorItem border.width: 1 border.color: myPalette.mid color: myPalette.mid visible: true Layout.bottomMargin: elisaTheme.layoutVerticalMargin Layout.topMargin: elisaTheme.layoutVerticalMargin Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.fillHeight: true Layout.preferredWidth: 1 Layout.minimumWidth: 1 Layout.maximumWidth: 1 } ColumnLayout { Layout.fillHeight: true Layout.fillWidth: true spacing: 0 TopNotification { id: invalidBalooConfiguration Layout.fillWidth: true musicManager: elisa.musicManager focus: true } Item { Layout.fillHeight: true Layout.fillWidth: true RowLayout { anchors.fill: parent spacing: 0 id: contentZone FocusScope { id: mainContentView focus: true Layout.fillHeight: true Layout.minimumWidth: 0 Layout.maximumWidth: 0 Layout.preferredWidth: 0 visible: Layout.minimumWidth != 0 Rectangle { border { color: (mainContentView.activeFocus ? myPalette.highlight : myPalette.base) width: 1 } radius: 3 color: myPalette.base anchors.fill: parent Loader { anchors.fill: parent anchors.leftMargin: parent.width / 3 anchors.rightMargin: parent.width / 3 anchors.topMargin: parent.height / 3 anchors.bottomMargin: parent.height / 3 z: 2 sourceComponent: BusyIndicator { id: busyScanningMusic hoverEnabled: false anchors.fill: parent opacity: 0.8 visible: true running: true z: 2 } active: elisa.musicManager.indexerBusy } Loader { id: localAlbumsLoader active: opacity > 0 visible: opacity > 0 anchors.fill: parent onLoaded: viewManager.allAlbumsViewIsLoaded(item.stackView) sourceComponent: MediaBrowser { id: localAlbums focus: true anchors { fill: parent leftMargin: elisaTheme.layoutHorizontalMargin rightMargin: elisaTheme.layoutHorizontalMargin } firstPage: GridBrowserView { id: allAlbumsView focus: true contentModel: elisa.allAlbumsProxyModel image: elisaTheme.albumIcon mainTitle: i18nc("Title of the view of all albums", "Albums") onOpen: { viewManager.openOneAlbum(localAlbums.stackView, innerMainTitle, innerSecondaryTitle, innerImage, databaseId) } onGoBack: viewManager.goBack() Binding { target: allAlbumsView property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } Behavior on opacity { NumberAnimation { easing.type: Easing.InOutQuad duration: 300 } } } Loader { id: localArtistsLoader active: opacity > 0 visible: opacity > 0 opacity: 0 anchors.fill: parent onLoaded: viewManager.allArtistsViewIsLoaded(item.stackView) sourceComponent: MediaBrowser { id: localArtists focus: true anchors { fill: parent leftMargin: elisaTheme.layoutHorizontalMargin rightMargin: elisaTheme.layoutHorizontalMargin } firstPage: GridBrowserView { id: allArtistsView focus: true showRating: false delegateDisplaySecondaryText: false contentModel: elisa.allArtistsProxyModel image: elisaTheme.artistIcon mainTitle: i18nc("Title of the view of all artists", "Artists") onOpen: { viewManager.openOneArtist(localArtists.stackView, innerMainTitle, innerImage, 0) } onGoBack: viewManager.goBack() Binding { target: allArtistsView property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } Behavior on opacity { NumberAnimation { easing.type: Easing.InOutQuad duration: 300 } } } Loader { id: localTracksLoader active: opacity > 0 visible: opacity > 0 opacity: 0 anchors.fill: parent onLoaded: viewManager.allTracksViewIsLoaded(item) sourceComponent: MediaBrowser { id: localTracks focus: true anchors { fill: parent leftMargin: elisaTheme.layoutHorizontalMargin rightMargin: elisaTheme.layoutHorizontalMargin } firstPage: ListBrowserView { id: allTracksView focus: true contentModel: elisa.allTracksProxyModel delegate: MediaTrackDelegate { id: entry width: allTracksView.delegateWidth height: elisaTheme.trackDelegateHeight focus: true trackData: model.containerData isFirstTrackOfDisc: false isSingleDiscAlbum: model.isSingleDiscAlbum onEnqueue: elisa.mediaPlayList.enqueue(data) onReplaceAndPlay: elisa.mediaPlayList.replaceAndPlay(data) onClicked: contentDirectoryView.currentIndex = index } image: elisaTheme.tracksIcon mainTitle: i18nc("Title of the view of all tracks", "Tracks") Binding { target: allTracksView property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } Behavior on opacity { NumberAnimation { easing.type: Easing.InOutQuad duration: 300 } } } Loader { id: localGenresLoader active: opacity > 0 visible: opacity > 0 opacity: 0 anchors.fill: parent onLoaded: viewManager.allGenresViewIsLoaded(item.stackView) sourceComponent: MediaBrowser { id: localGenres focus: true anchors { fill: parent leftMargin: elisaTheme.layoutHorizontalMargin rightMargin: elisaTheme.layoutHorizontalMargin } firstPage: GridBrowserView { id: allGenresView focus: true showRating: false delegateDisplaySecondaryText: false contentModel: elisa.allGenresProxyModel image: elisaTheme.genresIcon mainTitle: i18nc("Title of the view of all genres", "Genres") onOpen: { viewManager.openAllArtistsFromGenre(localGenres.stackView, innerMainTitle) } onGoBack: viewManager.goBack() Binding { target: allGenresView property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } Behavior on opacity { NumberAnimation { easing.type: Easing.InOutQuad duration: 300 } } } Loader { id: localFilesLoader anchors.fill: parent active: opacity > 0 visible: opacity > 0 opacity: 0 anchors { fill: parent leftMargin: elisaTheme.layoutHorizontalMargin rightMargin: elisaTheme.layoutHorizontalMargin } onLoaded: viewManager.filesBrowserViewIsLoaded(item) sourceComponent: FileBrowserView { id: localFiles focus: true contentModel: elisa.fileBrowserProxyModel Binding { target: localFiles property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } Behavior on opacity { NumberAnimation { easing.type: Easing.InOutQuad duration: 300 } } } Behavior on border.color { ColorAnimation { duration: 300 } } } } Rectangle { id: firstViewSeparatorItem border.width: 1 border.color: myPalette.mid color: myPalette.mid visible: true Layout.bottomMargin: elisaTheme.layoutVerticalMargin Layout.topMargin: elisaTheme.layoutVerticalMargin Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.fillHeight: true Layout.preferredWidth: 1 Layout.minimumWidth: 1 Layout.maximumWidth: 1 } MediaPlayListView { id: playList playListModel: elisa.mediaPlayList Layout.fillHeight: true Layout.leftMargin: elisaTheme.layoutHorizontalMargin Layout.rightMargin: elisaTheme.layoutHorizontalMargin Layout.minimumWidth: contentZone.width Layout.maximumWidth: contentZone.width Layout.preferredWidth: contentZone.width onStartPlayback: elisa.audioControl.ensurePlay() onPausePlayback: elisa.audioControl.playPause() onDisplayError: messageNotification.showNotification(errorText) } Rectangle { id: viewSeparatorItem border.width: 1 border.color: myPalette.mid color: myPalette.mid visible: Layout.minimumWidth != 0 Layout.bottomMargin: elisaTheme.layoutVerticalMargin Layout.topMargin: elisaTheme.layoutVerticalMargin Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.fillHeight: true Layout.preferredWidth: 1 Layout.minimumWidth: 1 Layout.maximumWidth: 1 } ContextView { id: albumContext Layout.fillHeight: true Layout.minimumWidth: contentZone.width Layout.maximumWidth: contentZone.width Layout.preferredWidth: contentZone.width visible: Layout.minimumWidth != 0 artistName: elisa.manageHeaderBar.artist albumName: elisa.manageHeaderBar.album albumArtUrl: elisa.manageHeaderBar.image } } } states: [ State { name: 'playList' when: listViews.currentIndex === 0 PropertyChanges { target: mainContentView Layout.fillWidth: false Layout.minimumWidth: 0 Layout.maximumWidth: 0 Layout.preferredWidth: 0 } PropertyChanges { target: firstViewSeparatorItem Layout.minimumWidth: 0 Layout.maximumWidth: 0 Layout.preferredWidth: 0 } PropertyChanges { target: playList Layout.minimumWidth: contentZone.width / 2 Layout.maximumWidth: contentZone.width / 2 Layout.preferredWidth: contentZone.width / 2 } PropertyChanges { target: viewSeparatorItem Layout.minimumWidth: 1 Layout.maximumWidth: 1 Layout.preferredWidth: 1 } PropertyChanges { target: albumContext Layout.minimumWidth: contentZone.width / 2 Layout.maximumWidth: contentZone.width / 2 Layout.preferredWidth: contentZone.width / 2 } }, + State { + name: "browsingViewsNoPlaylist" + when: listViews.currentIndex !== 0 && contentViewContainer.showPlaylist !== true + extend: "browsingViews" + PropertyChanges { + target: mainContentView + Layout.fillWidth: true + Layout.minimumWidth: contentZone.width + Layout.maximumWidth: contentZone.width + Layout.preferredWidth: contentZone.width + } + PropertyChanges { + target: playList + Layout.minimumWidth: 0 + Layout.maximumWidth: 0 + Layout.preferredWidth: 0 + } + }, State { name: 'browsingViews' when: listViews.currentIndex !== 0 PropertyChanges { target: mainContentView Layout.fillWidth: true Layout.minimumWidth: contentZone.width * 0.66 Layout.maximumWidth: contentZone.width * 0.68 Layout.preferredWidth: contentZone.width * 0.68 } PropertyChanges { target: firstViewSeparatorItem Layout.minimumWidth: 1 Layout.maximumWidth: 1 Layout.preferredWidth: 1 } PropertyChanges { target: playList Layout.minimumWidth: contentZone.width * 0.33 Layout.maximumWidth: contentZone.width * 0.33 Layout.preferredWidth: contentZone.width * 0.33 } PropertyChanges { target: viewSeparatorItem Layout.minimumWidth: 0 Layout.maximumWidth: 0 Layout.preferredWidth: 0 } PropertyChanges { target: albumContext Layout.minimumWidth: 0 Layout.maximumWidth: 0 Layout.preferredWidth: 0 } } ] transitions: Transition { NumberAnimation { properties: "Layout.minimumWidth, Layout.maximumWidth, Layout.preferredWidth, opacity" easing.type: Easing.InOutQuad duration: 300 } } } Component { id: innerAlbumView GridBrowserView { id: innerAlbumGridView contentModel: elisa.singleArtistProxyModel isSubPage: true onOpen: { viewManager.openOneAlbum(stackView, innerMainTitle, innerSecondaryTitle, innerImage, databaseId) } onGoBack: viewManager.goBack() Binding { target: innerAlbumGridView property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } Component { id: innerArtistView GridBrowserView { id: innerAlbumGridView delegateDisplaySecondaryText: false isSubPage: true onOpen: { viewManager.openOneArtist(stackView, innerMainTitle, innerImage, 0) } onGoBack: viewManager.goBack() Binding { target: innerAlbumGridView property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } Component { id: albumView ListBrowserView { id: albumGridView contentModel: elisa.singleAlbumProxyModel isSubPage: true delegate: MediaAlbumTrackDelegate { id: entry width: albumGridView.delegateWidth height: ((model.isFirstTrackOfDisc && !isSingleDiscAlbum) ? elisaTheme.delegateHeight*2 : elisaTheme.delegateHeight) focus: true mediaTrack.trackData: model.containerData mediaTrack.isFirstTrackOfDisc: model.isFirstTrackOfDisc mediaTrack.isSingleDiscAlbum: model.isSingleDiscAlbum mediaTrack.onEnqueue: elisa.mediaPlayList.enqueue(data) mediaTrack.onReplaceAndPlay: elisa.mediaPlayList.replaceAndPlay(data) mediaTrack.isAlternateColor: (index % 2) === 1 mediaTrack.onClicked: albumGridView.currentIndex = index } allowArtistNavigation: true onShowArtist: { viewManager.openOneArtist(stackView, name, elisaTheme.artistIcon, 0) } onGoBack: viewManager.goBack() expandedFilterView: true Binding { target: albumGridView property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } } diff --git a/src/qml/ElisaMainWindow.qml b/src/qml/ElisaMainWindow.qml index d0442ded..2b3e35a7 100644 --- a/src/qml/ElisaMainWindow.qml +++ b/src/qml/ElisaMainWindow.qml @@ -1,286 +1,290 @@ /* * Copyright 2016-2018 Matthieu Gallien * * 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 2.2 import QtQuick.Controls 1.4 as Controls1 import QtQuick.Layouts 1.1 import QtQuick.Window 2.2 import org.kde.elisa 1.0 import Qt.labs.settings 1.0 ApplicationWindow { id: mainWindow visible: true - minimumWidth: 1100 + minimumWidth: contentView.showPlaylist ? 1100 : 700 minimumHeight: 600 LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft LayoutMirroring.childrenInherit: true x: persistentSettings.x y: persistentSettings.y width: persistentSettings.width height: persistentSettings.height title: i18n("Elisa") property var goBackAction: elisa.action("go_back") property var findAction: elisa.action("edit_find") Controls1.Action { shortcut: findAction.shortcut onTriggered: { if ( persistentSettings.expandedFilterView == true) { persistentSettings.expandedFilterView = false } else { persistentSettings.expandedFilterView = true } } } - Controls1.Action { + + Controls1.Action { shortcut: goBackAction.shortcut onTriggered: contentView.goBack() } Controls1.Action { id: applicationMenuAction text: i18nc("open application menu", "Application Menu") iconName: "application-menu" onTriggered: applicationMenu.popup() } ApplicationMenu { id: applicationMenu } SystemPalette { id: myPalette colorGroup: SystemPalette.Active } Theme { id: elisaTheme } Settings { id: persistentSettings property int x property int y property int width : 1100 property int height : 600 property var playListState property var audioPlayerState property double playControlItemVolume : 100.0 property bool playControlItemMuted : false property bool playControlItemRepeat : false property bool playControlItemShuffle : false property bool expandedFilterView: false + property bool showPlaylist: true } Connections { target: Qt.application onAboutToQuit: { persistentSettings.x = mainWindow.x; persistentSettings.y = mainWindow.y; persistentSettings.width = mainWindow.width; persistentSettings.height = mainWindow.height; persistentSettings.playListState = elisa.mediaPlayList.persistentState; persistentSettings.audioPlayerState = elisa.audioControl.persistentState persistentSettings.playControlItemVolume = headerBar.playerControl.volume persistentSettings.playControlItemMuted = headerBar.playerControl.muted persistentSettings.playControlItemRepeat = headerBar.playerControl.repeat persistentSettings.playControlItemShuffle = headerBar.playerControl.shuffle + persistentSettings.showPlaylist = contentView.showPlaylist } } Loader { id: mprisloader active: false sourceComponent: PlatformIntegration { id: platformInterface playListModel: elisa.mediaPlayList audioPlayerManager: elisa.audioControl player: elisa.audioPlayer headerBarManager: elisa.manageHeaderBar manageMediaPlayerControl: elisa.playerControl onRaisePlayer: { mainWindow.show() mainWindow.raise() mainWindow.requestActivate() } } } Connections { target: elisa.audioPlayer onVolumeChanged: headerBar.playerControl.volume = elisa.audioPlayer.volume onMutedChanged: headerBar.playerControl.muted = elisa.audioPlayer.muted } Connections { target: elisa.mediaPlayList onPlayListLoadFailed: { messageNotification.showNotification(i18nc("message of passive notification when playlist load failed", "Load of playlist failed"), 3000) } } PassiveNotification { id: messageNotification } Rectangle { color: myPalette.base anchors.fill: parent ColumnLayout { anchors.fill: parent spacing: 0 Item { Layout.preferredHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight Layout.minimumHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight Layout.maximumHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight Layout.fillWidth: true HeaderBar { id: headerBar focus: true anchors.fill: parent tracksCount: elisa.manageHeaderBar.remainingTracks album: elisa.manageHeaderBar.album title: elisa.manageHeaderBar.title artist: elisa.manageHeaderBar.artist image: elisa.manageHeaderBar.image ratingVisible: false playerControl.duration: elisa.audioPlayer.duration playerControl.seekable: elisa.audioPlayer.seekable playerControl.volume: persistentSettings.playControlItemVolume playerControl.muted: persistentSettings.playControlItemMuted playerControl.position: elisa.audioPlayer.position playerControl.skipBackwardEnabled: elisa.playerControl.skipBackwardControlEnabled playerControl.skipForwardEnabled: elisa.playerControl.skipForwardControlEnabled playerControl.playEnabled: elisa.playerControl.playControlEnabled playerControl.isPlaying: elisa.playerControl.musicPlaying playerControl.repeat: persistentSettings.playControlItemRepeat playerControl.shuffle: persistentSettings.playControlItemShuffle playerControl.onSeek: elisa.audioPlayer.seek(position) playerControl.onPlay: elisa.audioControl.playPause() playerControl.onPause: elisa.audioControl.playPause() playerControl.onPlayPrevious: elisa.mediaPlayList.skipPreviousTrack() playerControl.onPlayNext: elisa.mediaPlayList.skipNextTrack() TrackImportNotification { id: importedTracksCountNotification anchors { right: headerBar.right top: headerBar.top rightMargin: elisaTheme.layoutHorizontalMargin * 1.75 topMargin: elisaTheme.layoutHorizontalMargin * 3 } } Binding { target: importedTracksCountNotification property: 'musicManager' value: elisa.musicManager when: elisa.musicManager !== undefined } Loader { sourceComponent: Binding { target: importedTracksCountNotification property: 'indexingRunning' value: elisa.musicManager.indexingRunning } active: elisa.musicManager !== undefined } Loader { sourceComponent: Binding { target: importedTracksCountNotification property: 'importedTracksCount' value: elisa.musicManager.importedTracksCount } active: elisa.musicManager !== undefined } } } ContentView { id: contentView Layout.fillHeight: true Layout.fillWidth: true + showPlaylist: persistentSettings.showPlaylist } } } Component.onCompleted: { elisa.initialize() elisa.mediaPlayList.randomPlay = Qt.binding(function() { return headerBar.playerControl.shuffle }) elisa.mediaPlayList.repeatPlay = Qt.binding(function() { return headerBar.playerControl.repeat }) elisa.playerControl.randomOrContinuePlay = Qt.binding(function() { return headerBar.playerControl.shuffle || headerBar.playerControl.repeat}) if (persistentSettings.playListState) { elisa.mediaPlayList.persistentState = persistentSettings.playListState } if (persistentSettings.audioPlayerState) { elisa.audioControl.persistentState = persistentSettings.audioPlayerState } elisa.audioPlayer.muted = Qt.binding(function() { return headerBar.playerControl.muted }) elisa.audioPlayer.volume = Qt.binding(function() { return headerBar.playerControl.volume }) mprisloader.active = true } }