diff --git a/src/elisa_core.kcfg b/src/elisa_core.kcfg
index 08b8f2d2..6502b3e6 100644
--- a/src/elisa_core.kcfg
+++ b/src/elisa_core.kcfg
@@ -1,17 +1,25 @@
+
+ true
+
+
+
+
+ false
+
diff --git a/src/elisaapplication.cpp b/src/elisaapplication.cpp
index f5799021..8256ae27 100644
--- a/src/elisaapplication.cpp
+++ b/src/elisaapplication.cpp
@@ -1,553 +1,561 @@
/*
* Copyright 2017 Matthieu Gallien
* Copyright (C) 2012 Aleix Pol Gonzalez
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#include "elisaapplication.h"
#include "musiclistenersmanager.h"
#include "mediaplaylist.h"
#include "mediaplaylistproxymodel.h"
#include "audiowrapper.h"
#include "manageaudioplayer.h"
#include "managemediaplayercontrol.h"
#include "manageheaderbar.h"
#include "databaseinterface.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
#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();
mConfigFileWatcher.addPath(configurationFileName);
}
#if defined KF5XmlGui_FOUND && KF5XmlGui_FOUND
KActionCollection mCollection;
#endif
ElisaUtils::EntryDataList mArguments;
std::unique_ptr mMusicManager;
std::unique_ptr mMediaPlayList;
std::unique_ptr mMediaPlayListProxyModel;
std::unique_ptr mAudioWrapper;
std::unique_ptr mAudioControl;
std::unique_ptr mPlayerControl;
std::unique_ptr mManageHeaderBar;
QQmlApplicationEngine *mEngine = nullptr;
QFileSystemWatcher mConfigFileWatcher;
};
ElisaApplication::ElisaApplication(QObject *parent) : QObject(parent), d(std::make_unique(this))
{
connect(Elisa::ElisaConfiguration::self(), &Elisa::ElisaConfiguration::configChanged,
this, &ElisaApplication::configChanged);
connect(&d->mConfigFileWatcher, &QFileSystemWatcher::fileChanged,
this, &ElisaApplication::configChanged);
}
ElisaApplication::~ElisaApplication()
= default;
void ElisaApplication::setupActions(const QString &actionName)
{
#if defined KF5XmlGui_FOUND && KF5XmlGui_FOUND
if (actionName == QLatin1String("file_quit")) {
auto quitAction = KStandardAction::quit(QCoreApplication::instance(), &QCoreApplication::quit, &d->mCollection);
d->mCollection.addAction(actionName, quitAction);
}
if (actionName == QLatin1String("help_contents") && KAuthorized::authorizeAction(actionName)) {
auto handBookAction = KStandardAction::helpContents(this, &ElisaApplication::appHelpActivated, &d->mCollection);
d->mCollection.addAction(handBookAction->objectName(), handBookAction);
}
if (actionName == QLatin1String("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 == QLatin1String("help_about_app") && KAuthorized::authorizeAction(actionName)) {
auto aboutAppAction = KStandardAction::aboutApp(this, &ElisaApplication::aboutApplication, this);
d->mCollection.addAction(aboutAppAction->objectName(), aboutAppAction);
}
if (actionName == QLatin1String("options_configure") && KAuthorized::authorizeAction(actionName)) {
auto preferencesAction = KStandardAction::preferences(this, &ElisaApplication::configureElisa, this);
d->mCollection.addAction(preferencesAction->objectName(), preferencesAction);
}
if (actionName == QLatin1String("options_configure_keybinding") && KAuthorized::authorizeAction(actionName)) {
auto keyBindingsAction = KStandardAction::keyBindings(this, &ElisaApplication::configureShortcuts, this);
d->mCollection.addAction(keyBindingsAction->objectName(), keyBindingsAction);
}
if (actionName == QLatin1String("go_back") && KAuthorized::authorizeAction(actionName)) {
auto goBackAction = KStandardAction::back(this, &ElisaApplication::goBack, this);
d->mCollection.addAction(goBackAction->objectName(), goBackAction);
}
if (actionName == QLatin1String("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 == QLatin1String("Seek") && KAuthorized::authorizeAction(actionName)) {
auto seekAction = d->mCollection.addAction(actionName, this, &ElisaApplication::seek);
d->mCollection.setDefaultShortcut(seekAction, QKeySequence(Qt::SHIFT + Qt::Key_Right));
}
if (actionName == QLatin1String("Scrub") && KAuthorized::authorizeAction(actionName)) {
auto scrubAction = d->mCollection.addAction(actionName, this, &ElisaApplication::scrub);
d->mCollection.setDefaultShortcut(scrubAction, QKeySequence(Qt::SHIFT + Qt::Key_Left));
}
if (actionName == QLatin1String("Play-Pause") && KAuthorized::authorizeAction(actionName)) {
auto playPauseAction = d->mCollection.addAction(actionName, this, &ElisaApplication::playPause);
d->mCollection.setDefaultShortcut(playPauseAction, QKeySequence(Qt::Key_Space));
}
if (actionName == QLatin1String("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 ElisaUtils::EntryDataList &newArguments)
{
if (d->mArguments == newArguments) {
return;
}
d->mArguments = checkFileListAndMakeAbsolute(newArguments, QDir::currentPath());
Q_EMIT argumentsChanged();
if (!d->mArguments.isEmpty()) {
Q_EMIT enqueue(d->mArguments, ElisaUtils::FileName,
ElisaUtils::PlayListEnqueueMode::AppendPlayList,
ElisaUtils::PlayListEnqueueTriggerPlay::TriggerPlay);
}
}
void ElisaApplication::activateActionRequested(const QString &actionName, const QVariant ¶meter)
{
Q_UNUSED(actionName)
Q_UNUSED(parameter)
}
void ElisaApplication::activateRequested(const QStringList &arguments, const QString &workingDirectory)
{
if (arguments.size() > 1) {
auto realArguments = ElisaUtils::EntryDataList{};
bool isFirst = true;
for (const auto &oneArgument : arguments) {
if (isFirst) {
isFirst = false;
continue;
}
realArguments.push_back(ElisaUtils::EntryData{{}, {}, QUrl(oneArgument)});
}
Q_EMIT enqueue(checkFileListAndMakeAbsolute(realArguments, workingDirectory),
ElisaUtils::FileName,
ElisaUtils::PlayListEnqueueMode::AppendPlayList,
ElisaUtils::PlayListEnqueueTriggerPlay::TriggerPlay);
}
}
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 (!d->mEngine)
{
return;
}
d->mEngine->load(QUrl(QStringLiteral("qrc:/qml/ElisaConfigurationDialog.qml")));
}
void ElisaApplication::goBack() {}
void ElisaApplication::find() {}
void ElisaApplication::togglePlaylist() {}
void ElisaApplication::seek() {}
void ElisaApplication::scrub() {}
void ElisaApplication::playPause() {}
void ElisaApplication::configChanged()
{
auto currentConfiguration = Elisa::ElisaConfiguration::self();
d->mConfigFileWatcher.addPath(currentConfiguration->config()->name());
currentConfiguration->load();
currentConfiguration->read();
Q_EMIT showProgressOnTaskBarChanged();
+ Q_EMIT showSystemTrayIconChanged();
}
ElisaUtils::EntryDataList ElisaApplication::checkFileListAndMakeAbsolute(const ElisaUtils::EntryDataList &filesList,
const QString &workingDirectory) const
{
auto filesToOpen = ElisaUtils::EntryDataList{};
for (const auto &oneFile : filesList) {
if (std::get<2>(oneFile).scheme().isEmpty() || std::get<2>(oneFile).isLocalFile()) {
auto newFile = QFileInfo(std::get<2>(oneFile).toLocalFile());
if (std::get<2>(oneFile).scheme().isEmpty()) {
newFile = QFileInfo(std::get<2>(oneFile).toString());
}
if (newFile.isRelative()) {
if (std::get<2>(oneFile).scheme().isEmpty()) {
newFile.setFile(workingDirectory, std::get<2>(oneFile).toString());
} else {
newFile.setFile(workingDirectory, std::get<2>(oneFile).toLocalFile());
}
}
if (newFile.exists()) {
filesToOpen.push_back(ElisaUtils::EntryData{{}, {}, QUrl::fromLocalFile(newFile.canonicalFilePath())});
}
} else {
filesToOpen.push_back(ElisaUtils::EntryData{{}, {}, std::get<2>(oneFile)});
}
}
return filesToOpen;
}
void ElisaApplication::initialize()
{
initializeModels();
initializePlayer();
Q_EMIT initializationDone();
}
void ElisaApplication::setQmlEngine(QQmlApplicationEngine *engine)
{
d->mEngine = engine;
}
void ElisaApplication::initializeModels()
{
d->mMusicManager = std::make_unique();
Q_EMIT musicManagerChanged();
d->mMediaPlayList = std::make_unique();
d->mMusicManager->subscribeForTracks(d->mMediaPlayList.get());
Q_EMIT mediaPlayListChanged();
d->mMediaPlayListProxyModel = std::make_unique();
d->mMediaPlayListProxyModel->setPlayListModel(d->mMediaPlayList.get());
Q_EMIT mediaPlayListProxyModelChanged();
d->mMusicManager->setElisaApplication(this);
QObject::connect(this, &ElisaApplication::enqueue,
d->mMediaPlayListProxyModel.get(), static_cast(&MediaPlayListProxyModel::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->mMediaPlayListProxyModel.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::saveUndoPositionInAudioWrapper, d->mAudioWrapper.get(), &AudioWrapper::saveUndoPosition);
QObject::connect(d->mAudioControl.get(), &ManageAudioPlayer::restoreUndoPositionInAudioWrapper, d->mAudioWrapper.get(), &AudioWrapper::restoreUndoPosition);
QObject::connect(d->mAudioControl.get(), &ManageAudioPlayer::skipNextTrack, d->mMediaPlayListProxyModel.get(), &MediaPlayListProxyModel::skipNextTrack);
QObject::connect(d->mAudioControl.get(), &ManageAudioPlayer::sourceInError, d->mMediaPlayListProxyModel.get(), &MediaPlayListProxyModel::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->mAudioControl.get(), &ManageAudioPlayer::startedPlayingTrack,
d->mMusicManager->viewDatabase(), &DatabaseInterface::trackHasStartedPlaying);
QObject::connect(d->mAudioControl.get(), &ManageAudioPlayer::updateData, d->mMediaPlayList.get(), &MediaPlayList::setData);
QObject::connect(d->mMediaPlayListProxyModel.get(), &MediaPlayListProxyModel::ensurePlay, d->mAudioControl.get(), &ManageAudioPlayer::ensurePlay);
QObject::connect(d->mMediaPlayListProxyModel.get(), &MediaPlayListProxyModel::playListFinished, d->mAudioControl.get(), &ManageAudioPlayer::playListFinished);
QObject::connect(d->mMediaPlayListProxyModel.get(), &MediaPlayListProxyModel::currentTrackChanged, d->mAudioControl.get(), &ManageAudioPlayer::setCurrentTrack);
QObject::connect(d->mMediaPlayListProxyModel.get(), &MediaPlayListProxyModel::clearPlayListPlayer, d->mAudioControl.get(), &ManageAudioPlayer::saveForUndoClearPlaylist);
QObject::connect(d->mMediaPlayListProxyModel.get(), &MediaPlayListProxyModel::undoClearPlayListPlayer, d->mAudioControl.get(), &ManageAudioPlayer::restoreForUndoClearPlaylist);
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);
QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::currentPlayingForRadiosChanged, d->mAudioControl.get(), &ManageAudioPlayer::setCurrentPlayingForRadios);
QObject::connect(d->mMediaPlayListProxyModel.get(), &MediaPlayListProxyModel::currentTrackChanged, d->mPlayerControl.get(), &ManageMediaPlayerControl::setCurrentTrack);
QObject::connect(d->mMediaPlayListProxyModel.get(), &MediaPlayListProxyModel::previousTrackChanged, d->mPlayerControl.get(), &ManageMediaPlayerControl::setPreviousTrack);
QObject::connect(d->mMediaPlayListProxyModel.get(), &MediaPlayListProxyModel::nextTrackChanged, d->mPlayerControl.get(), &ManageMediaPlayerControl::setNextTrack);
QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::playing, d->mPlayerControl.get(), &ManageMediaPlayerControl::playerPlaying);
QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::paused, d->mPlayerControl.get(), &ManageMediaPlayerControl::playerPausedOrStopped);
QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::stopped, d->mPlayerControl.get(), &ManageMediaPlayerControl::playerPausedOrStopped);
d->mManageHeaderBar->setTitleRole(MediaPlayList::TitleRole);
d->mManageHeaderBar->setAlbumRole(MediaPlayList::AlbumRole);
d->mManageHeaderBar->setAlbumArtistRole(MediaPlayList::AlbumArtistRole);
d->mManageHeaderBar->setArtistRole(MediaPlayList::ArtistRole);
d->mManageHeaderBar->setFileNameRole(MediaPlayList::ResourceRole);
d->mManageHeaderBar->setImageRole(MediaPlayList::ImageUrlRole);
d->mManageHeaderBar->setDatabaseIdRole(MediaPlayList::DatabaseIdRole);
d->mManageHeaderBar->setTrackTypeRole(MediaPlayList::ElementTypeRole);
d->mManageHeaderBar->setAlbumIdRole(MediaPlayList::AlbumIdRole);
d->mManageHeaderBar->setIsValidRole(MediaPlayList::IsValidRole);
QObject::connect(d->mMediaPlayListProxyModel.get(), &MediaPlayListProxyModel::currentTrackChanged, d->mManageHeaderBar.get(), &ManageHeaderBar::setCurrentTrack);
QObject::connect(d->mMediaPlayListProxyModel.get(), &MediaPlayListProxyModel::currentTrackDataChanged, d->mManageHeaderBar.get(), &ManageHeaderBar::updateCurrentTrackData);
if (!d->mArguments.isEmpty()) {
Q_EMIT enqueue(d->mArguments, ElisaUtils::FileName,
ElisaUtils::PlayListEnqueueMode::AppendPlayList,
ElisaUtils::PlayListEnqueueTriggerPlay::TriggerPlay);
}
}
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();
}
void ElisaApplication::installKeyEventFilter(QObject *object)
{
if(!object) {
return;
}
object->installEventFilter(this);
}
bool ElisaApplication::eventFilter(QObject *object, QEvent *event)
{
Q_UNUSED(object);
QKeyEvent *keyEvent = static_cast(event);
auto playPauseAction = d->mCollection.action(tr("Play-Pause"));
if (keyEvent->key() == Qt::Key_Space && playPauseAction->shortcut()[0] == Qt::Key_Space) {
return true;
}
return false;
}
const ElisaUtils::EntryDataList &ElisaApplication::arguments() const
{
return d->mArguments;
}
MusicListenersManager *ElisaApplication::musicManager() const
{
return d->mMusicManager.get();
}
MediaPlayList *ElisaApplication::mediaPlayList() const
{
return d->mMediaPlayList.get();
}
MediaPlayListProxyModel *ElisaApplication::mediaPlayListProxyModel() const
{
return d->mMediaPlayListProxyModel.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();
}
bool ElisaApplication::showProgressOnTaskBar() const
{
auto currentConfiguration = Elisa::ElisaConfiguration::self();
return currentConfiguration->showProgressOnTaskBar();
}
+bool ElisaApplication::showSystemTrayIcon() const
+{
+ auto currentConfiguration = Elisa::ElisaConfiguration::self();
+
+ return currentConfiguration->showSystemTrayIcon();
+}
+
#include "moc_elisaapplication.cpp"
diff --git a/src/elisaapplication.h b/src/elisaapplication.h
index 41667e5c..6d02824a 100644
--- a/src/elisaapplication.h
+++ b/src/elisaapplication.h
@@ -1,203 +1,211 @@
/*
* Copyright 2017 Matthieu Gallien
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#ifndef ELISAAPPLICATION_H
#define ELISAAPPLICATION_H
#include "elisaLib_export.h"
#include "config-upnp-qt.h"
#include "elisautils.h"
#include
#include
#include
class QIcon;
class QAction;
class MusicListenersManager;
class MediaPlayList;
class MediaPlayListProxyModel;
class AudioWrapper;
class ManageAudioPlayer;
class ManageMediaPlayerControl;
class ManageHeaderBar;
class QQmlApplicationEngine;
class ElisaApplicationPrivate;
class ELISALIB_EXPORT ElisaApplication : public QObject
{
Q_OBJECT
Q_PROPERTY(ElisaUtils::EntryDataList arguments
READ arguments
WRITE setArguments
NOTIFY argumentsChanged)
Q_PROPERTY(MusicListenersManager *musicManager
READ musicManager
NOTIFY musicManagerChanged)
Q_PROPERTY(MediaPlayList *mediaPlayList
READ mediaPlayList
NOTIFY mediaPlayListChanged)
Q_PROPERTY(MediaPlayListProxyModel *mediaPlayListProxyModel
READ mediaPlayListProxyModel
NOTIFY mediaPlayListProxyModelChanged)
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)
Q_PROPERTY(bool showProgressOnTaskBar
READ showProgressOnTaskBar
NOTIFY showProgressOnTaskBarChanged)
+ Q_PROPERTY(bool showSystemTrayIcon
+ READ showSystemTrayIcon
+ NOTIFY showSystemTrayIconChanged)
+
public:
explicit ElisaApplication(QObject *parent = nullptr);
~ElisaApplication() override;
Q_INVOKABLE QAction* action(const QString& name);
Q_INVOKABLE QString iconName(const QIcon& icon);
Q_INVOKABLE void installKeyEventFilter(QObject *object);
bool eventFilter(QObject *object, QEvent *event) override;
const ElisaUtils::EntryDataList &arguments() const;
MusicListenersManager *musicManager() const;
MediaPlayList *mediaPlayList() const;
MediaPlayListProxyModel *mediaPlayListProxyModel() const;
AudioWrapper *audioPlayer() const;
ManageAudioPlayer *audioControl() const;
ManageMediaPlayerControl *playerControl() const;
ManageHeaderBar *manageHeaderBar() const;
bool showProgressOnTaskBar() const;
+ bool showSystemTrayIcon() const;
+
Q_SIGNALS:
void argumentsChanged();
void musicManagerChanged();
void mediaPlayListChanged();
void mediaPlayListProxyModelChanged();
void audioPlayerChanged();
void audioControlChanged();
void playerControlChanged();
void manageHeaderBarChanged();
void enqueue(const ElisaUtils::EntryDataList &newEntries,
ElisaUtils::PlayListEntryType databaseIdType,
ElisaUtils::PlayListEnqueueMode enqueueMode,
ElisaUtils::PlayListEnqueueTriggerPlay triggerPlay);
void initializationDone();
void showProgressOnTaskBarChanged();
+ void showSystemTrayIconChanged();
+
public Q_SLOTS:
void appHelpActivated();
void aboutApplication();
void reportBug();
void configureShortcuts();
void configureElisa();
void setArguments(const ElisaUtils::EntryDataList &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();
public:
void setQmlEngine(QQmlApplicationEngine *engine);
private Q_SLOTS:
void goBack();
void find();
void togglePlaylist();
void seek();
void scrub();
void playPause();
void configChanged();
private:
void initializeModels();
void initializePlayer();
void setupActions(const QString &actionName);
ElisaUtils::EntryDataList checkFileListAndMakeAbsolute(const ElisaUtils::EntryDataList &filesList,
const QString &workingDirectory) const;
std::unique_ptr d;
};
#endif // ELISAAPPLICATION_H
diff --git a/src/localFileConfiguration/elisaconfigurationdialog.cpp b/src/localFileConfiguration/elisaconfigurationdialog.cpp
index f7e2517f..c8229a54 100644
--- a/src/localFileConfiguration/elisaconfigurationdialog.cpp
+++ b/src/localFileConfiguration/elisaconfigurationdialog.cpp
@@ -1,133 +1,147 @@
/*
* Copyright 2017 Matthieu Gallien
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#include "elisaconfigurationdialog.h"
#include "elisa-version.h"
#include "elisa_settings.h"
#include
#include
ElisaConfigurationDialog::ElisaConfigurationDialog(QObject* parent)
: QObject(parent)
{
connect(Elisa::ElisaConfiguration::self(), &Elisa::ElisaConfiguration::configChanged,
this, &ElisaConfigurationDialog::configChanged);
connect(&mConfigFileWatcher, &QFileSystemWatcher::fileChanged,
this, &ElisaConfigurationDialog::configChanged);
setRootPath(Elisa::ElisaConfiguration::rootPath());
setShowProgressInTaskBar(Elisa::ElisaConfiguration::showProgressOnTaskBar());
+ setShowSystemTrayIcon(Elisa::ElisaConfiguration::showSystemTrayIcon());
setForceUsageOfFastFileSearch(Elisa::ElisaConfiguration::forceUsageOfFastFileSearch());
save();
mConfigFileWatcher.addPath(Elisa::ElisaConfiguration::self()->config()->name());
}
ElisaConfigurationDialog::~ElisaConfigurationDialog()
= default;
QStringList ElisaConfigurationDialog::rootPath() const
{
return mRootPath;
}
void ElisaConfigurationDialog::setRootPath(const QStringList &rootPath)
{
if (mRootPath == rootPath && !mRootPath.isEmpty()) {
return;
}
mRootPath.clear();
for (const auto &onePath : rootPath) {
auto workPath = onePath;
if (workPath.startsWith(QLatin1String("file:/"))) {
auto urlPath = QUrl{workPath};
workPath = urlPath.toLocalFile();
}
QFileInfo pathFileInfo(workPath);
auto directoryPath = pathFileInfo.canonicalFilePath();
if (!directoryPath.isEmpty()) {
mRootPath.push_back(directoryPath);
}
}
if (mRootPath.isEmpty()) {
auto systemMusicPaths = QStandardPaths::standardLocations(QStandardPaths::MusicLocation);
for (const auto &musicPath : qAsConst(systemMusicPaths)) {
mRootPath.push_back(musicPath);
}
}
Q_EMIT rootPathChanged(mRootPath);
setDirty();
}
void ElisaConfigurationDialog::save()
{
Elisa::ElisaConfiguration::setRootPath(mRootPath);
Elisa::ElisaConfiguration::setShowProgressOnTaskBar(mShowProgressInTaskBar);
+ Elisa::ElisaConfiguration::setShowSystemTrayIcon(mShowSystemTrayIcon);
Elisa::ElisaConfiguration::setForceUsageOfFastFileSearch(mForceUsageOfFastFileSearch);
Elisa::ElisaConfiguration::self()->save();
mIsDirty = false;
Q_EMIT isDirtyChanged();
}
void ElisaConfigurationDialog::setShowProgressInTaskBar(bool showProgressInTaskBar)
{
if (mShowProgressInTaskBar == showProgressInTaskBar) {
return;
}
mShowProgressInTaskBar = showProgressInTaskBar;
Q_EMIT showProgressInTaskBarChanged();
setDirty();
}
+void ElisaConfigurationDialog::setShowSystemTrayIcon(bool showSystemTrayIcon)
+{
+ if (mShowSystemTrayIcon == showSystemTrayIcon) {
+ return;
+ }
+
+ mShowSystemTrayIcon = showSystemTrayIcon;
+ Q_EMIT showSystemTrayIconChanged();
+
+ setDirty();
+}
+
void ElisaConfigurationDialog::setForceUsageOfFastFileSearch(bool forceUsageOfFastFileSearch)
{
if (mForceUsageOfFastFileSearch == forceUsageOfFastFileSearch) {
return;
}
mForceUsageOfFastFileSearch = forceUsageOfFastFileSearch;
Q_EMIT forceUsageOfFastFileSearchChanged();
setDirty();
}
void ElisaConfigurationDialog::configChanged()
{
setRootPath(Elisa::ElisaConfiguration::rootPath());
}
void ElisaConfigurationDialog::setDirty()
{
mIsDirty = true;
Q_EMIT isDirtyChanged();
}
#include "moc_elisaconfigurationdialog.cpp"
diff --git a/src/localFileConfiguration/elisaconfigurationdialog.h b/src/localFileConfiguration/elisaconfigurationdialog.h
index da17678b..b236db9b 100644
--- a/src/localFileConfiguration/elisaconfigurationdialog.h
+++ b/src/localFileConfiguration/elisaconfigurationdialog.h
@@ -1,111 +1,128 @@
/*
* Copyright 2017 Matthieu Gallien
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#if !defined ELISACONFIGURATIONDIALOG_H_
#define ELISACONFIGURATIONDIALOG_H_
#include "elisaLib_export.h"
#include
#include
class ELISALIB_EXPORT ElisaConfigurationDialog : public QObject
{
Q_OBJECT
Q_PROPERTY(QStringList rootPath
READ rootPath
WRITE setRootPath
NOTIFY rootPathChanged)
Q_PROPERTY(bool forceUsageOfFastFileSearch
READ forceUsageOfFastFileSearch
WRITE setForceUsageOfFastFileSearch
NOTIFY forceUsageOfFastFileSearchChanged)
Q_PROPERTY(bool showProgressInTaskBar
READ showProgressInTaskBar
WRITE setShowProgressInTaskBar
NOTIFY showProgressInTaskBarChanged)
+ Q_PROPERTY(bool showSystemTrayIcon
+ READ showSystemTrayIcon
+ WRITE setShowSystemTrayIcon
+ NOTIFY showSystemTrayIconChanged)
+
Q_PROPERTY(bool isDirty
READ isDirty
NOTIFY isDirtyChanged)
public:
explicit ElisaConfigurationDialog(QObject *parent = nullptr);
~ElisaConfigurationDialog() override;
QStringList rootPath() const;
bool isDirty() const
{
return mIsDirty;
}
bool showProgressInTaskBar() const
{
return mShowProgressInTaskBar;
}
+ bool showSystemTrayIcon() const
+ {
+ return mShowSystemTrayIcon;
+ }
+
bool forceUsageOfFastFileSearch() const
{
return mForceUsageOfFastFileSearch;
}
Q_SIGNALS:
void rootPathChanged(const QStringList &rootPath);
void isDirtyChanged();
void showProgressInTaskBarChanged();
+ void showSystemTrayIconChanged();
+
void forceUsageOfFastFileSearchChanged();
public Q_SLOTS:
void setRootPath(const QStringList &rootPath);
void save();
void setShowProgressInTaskBar(bool showProgressInTaskBar);
+ void setShowSystemTrayIcon(bool showSystemTrayIcon);
+
void setForceUsageOfFastFileSearch(bool forceUsageOfFastFileSearch);
private Q_SLOTS:
void configChanged();
private:
void setDirty();
QStringList mRootPath;
QFileSystemWatcher mConfigFileWatcher;
bool mIsDirty = false;
bool mShowProgressInTaskBar = true;
- bool mForceUsageOfFastFileSearch;
+
+ bool mShowSystemTrayIcon = false;
+
+ bool mForceUsageOfFastFileSearch = true;
};
#endif
diff --git a/src/musiclistenersmanager.cpp b/src/musiclistenersmanager.cpp
index 9fa3cc88..6b61b13b 100644
--- a/src/musiclistenersmanager.cpp
+++ b/src/musiclistenersmanager.cpp
@@ -1,571 +1,572 @@
/*
* Copyright 2016-2017 Matthieu Gallien
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#include "musiclistenersmanager.h"
#include "config-upnp-qt.h"
#include "indexersManager.h"
#if defined UPNPQT_FOUND && UPNPQT_FOUND
#include "upnp/upnplistener.h"
#endif
#if defined KF5Baloo_FOUND && KF5Baloo_FOUND
#include "baloo/baloolistener.h"
#include "baloo/baloodetector.h"
#endif
#if defined Qt5AndroidExtras_FOUND && Qt5AndroidExtras_FOUND
#include "android/androidmusiclistener.h"
#endif
#include "databaseinterface.h"
#include "mediaplaylist.h"
#include "file/filelistener.h"
#include "file/localfilelisting.h"
#include "trackslistener.h"
#include "elisaapplication.h"
#include "elisa_settings.h"
#include "modeldataloader.h"
#include
#include
#include
#include
#include
#include
#include
#include
class MusicListenersManagerPrivate
{
public:
QThread mDatabaseThread;
QThread mListenerThread;
#if defined UPNPQT_FOUND && UPNPQT_FOUND
UpnpListener mUpnpListener;
#endif
#if defined KF5Baloo_FOUND && KF5Baloo_FOUND
BalooDetector mBalooDetector;
BalooListener mBalooListener;
#endif
FileListener mFileListener;
#if defined Qt5AndroidExtras_FOUND && Qt5AndroidExtras_FOUND
std::unique_ptr mAndroidMusicListener;
#endif
DatabaseInterface mDatabaseInterface;
std::unique_ptr mTracksListener;
QFileSystemWatcher mConfigFileWatcher;
QStringList mPreviousRootPathValue;
ElisaApplication *mElisaApplication = nullptr;
int mImportedTracksCount = 0;
bool mIndexerBusy = false;
bool mFileSystemIndexerActive = false;
bool mBalooIndexerActive = false;
bool mBalooIndexerAvailable = false;
bool mAndroidIndexerActive = false;
bool mAndroidIndexerAvailable = false;
};
MusicListenersManager::MusicListenersManager(QObject *parent)
: QObject(parent), d(std::make_unique())
{
connect(&d->mDatabaseInterface, &DatabaseInterface::tracksAdded,
this, &MusicListenersManager::increaseImportedTracksCount);
#if defined KF5Baloo_FOUND && KF5Baloo_FOUND
connect(&d->mBalooDetector, &BalooDetector::balooAvailabilityChanged,
this, &MusicListenersManager::balooAvailabilityChanged);
#endif
connect(&d->mDatabaseInterface, &DatabaseInterface::requestsInitDone,
this, &MusicListenersManager::databaseReady);
connect(this, &MusicListenersManager::clearDatabase,
&d->mDatabaseInterface, &DatabaseInterface::clearData);
connect(&d->mDatabaseInterface, &DatabaseInterface::cleanedDatabase,
this, &MusicListenersManager::cleanedDatabase);
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit,
this, &MusicListenersManager::applicationAboutToQuit);
connect(&d->mConfigFileWatcher, &QFileSystemWatcher::fileChanged,
this, &MusicListenersManager::configChanged);
d->mListenerThread.start();
d->mDatabaseThread.start();
d->mDatabaseInterface.moveToThread(&d->mDatabaseThread);
const auto &localDataPaths = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
auto databaseFileName = QString();
if (!localDataPaths.isEmpty()) {
QDir myDataDirectory;
myDataDirectory.mkpath(localDataPaths.first());
databaseFileName = localDataPaths.first() + QStringLiteral("/elisaDatabase.db");
}
QMetaObject::invokeMethod(&d->mDatabaseInterface, "init", Qt::QueuedConnection,
Q_ARG(QString, QStringLiteral("listeners")), Q_ARG(QString, databaseFileName));
qCInfo(orgKdeElisaIndexersManager) << "Local file system indexer is inactive";
qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is unavailable";
qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is inactive";
}
MusicListenersManager::~MusicListenersManager()
= default;
DatabaseInterface *MusicListenersManager::viewDatabase() const
{
return &d->mDatabaseInterface;
}
void MusicListenersManager::subscribeForTracks(MediaPlayList *client)
{
createTracksListener();
connect(d->mTracksListener.get(), &TracksListener::trackHasChanged, client, &MediaPlayList::trackChanged);
connect(d->mTracksListener.get(), &TracksListener::trackHasBeenRemoved, client, &MediaPlayList::trackRemoved);
connect(d->mTracksListener.get(), &TracksListener::tracksListAdded, client, &MediaPlayList::tracksListAdded);
connect(client, &MediaPlayList::newEntryInList, d->mTracksListener.get(), &TracksListener::newEntryInList);
connect(client, &MediaPlayList::newUrlInList, d->mTracksListener.get(), &TracksListener::newUrlInList);
connect(client, &MediaPlayList::newTrackByNameInList, d->mTracksListener.get(), &TracksListener::trackByNameInList);
}
int MusicListenersManager::importedTracksCount() const
{
return d->mImportedTracksCount;
}
ElisaApplication *MusicListenersManager::elisaApplication() const
{
return d->mElisaApplication;
}
TracksListener *MusicListenersManager::tracksListener() const
{
return d->mTracksListener.get();
}
bool MusicListenersManager::indexerBusy() const
{
return d->mIndexerBusy;
}
bool MusicListenersManager::fileSystemIndexerActive() const
{
return d->mFileSystemIndexerActive;
}
bool MusicListenersManager::balooIndexerActive() const
{
return d->mBalooIndexerActive;
}
bool MusicListenersManager::balooIndexerAvailable() const
{
return d->mBalooIndexerAvailable;
}
bool MusicListenersManager::androidIndexerActive() const
{
return d->mAndroidIndexerActive;
}
bool MusicListenersManager::androidIndexerAvailable() const
{
return d->mAndroidIndexerAvailable;
}
auto MusicListenersManager::initializeRootPath()
{
auto initialRootPath = QStringList{};
auto systemMusicPaths = QStandardPaths::standardLocations(QStandardPaths::MusicLocation);
for (const auto &musicPath : qAsConst(systemMusicPaths)) {
initialRootPath.push_back(musicPath);
}
Elisa::ElisaConfiguration::setRootPath(initialRootPath);
Elisa::ElisaConfiguration::setShowProgressOnTaskBar(true);
+ Elisa::ElisaConfiguration::setShowSystemTrayIcon(false);
Elisa::ElisaConfiguration::setForceUsageOfFastFileSearch(true);
Elisa::ElisaConfiguration::self()->save();
return initialRootPath;
}
void MusicListenersManager::databaseReady()
{
auto initialRootPath = Elisa::ElisaConfiguration::rootPath();
if (initialRootPath.isEmpty()) {
initializeRootPath();
}
d->mConfigFileWatcher.addPath(Elisa::ElisaConfiguration::self()->config()->name());
configChanged();
}
void MusicListenersManager::applicationAboutToQuit()
{
d->mDatabaseInterface.applicationAboutToQuit();
Q_EMIT applicationIsTerminating();
d->mDatabaseThread.exit();
d->mDatabaseThread.wait();
d->mListenerThread.exit();
d->mListenerThread.wait();
}
void MusicListenersManager::showConfiguration()
{
auto configureAction = d->mElisaApplication->action(QStringLiteral("options_configure"));
configureAction->trigger();
}
void MusicListenersManager::setElisaApplication(ElisaApplication *elisaApplication)
{
if (d->mElisaApplication == elisaApplication) {
return;
}
d->mElisaApplication = elisaApplication;
emit elisaApplicationChanged();
}
void MusicListenersManager::playBackError(const QUrl &sourceInError, QMediaPlayer::Error playerError)
{
qCDebug(orgKdeElisaIndexersManager) << "MusicListenersManager::playBackError" << sourceInError;
if (playerError == QMediaPlayer::ResourceError) {
Q_EMIT removeTracksInError({sourceInError});
if (sourceInError.isLocalFile()) {
Q_EMIT displayTrackError(sourceInError.toLocalFile());
} else {
Q_EMIT displayTrackError(sourceInError.toString());
}
}
}
void MusicListenersManager::deleteElementById(ElisaUtils::PlayListEntryType entryType, qulonglong databaseId)
{
switch(entryType)
{
case ElisaUtils::Radio:
QMetaObject::invokeMethod(&d->mDatabaseInterface, "removeRadio", Qt::QueuedConnection,
Q_ARG(qulonglong, databaseId));
break;
case ElisaUtils::Album:
case ElisaUtils::Artist:
case ElisaUtils::Genre:
case ElisaUtils::Lyricist:
case ElisaUtils::Composer:
case ElisaUtils::Track:
case ElisaUtils::FileName:
case ElisaUtils::Unknown:
break;
}
}
void MusicListenersManager::connectModel(ModelDataLoader *dataLoader)
{
dataLoader->moveToThread(&d->mDatabaseThread);
}
void MusicListenersManager::resetMusicData()
{
Q_EMIT clearDatabase();
}
void MusicListenersManager::configChanged()
{
auto currentConfiguration = Elisa::ElisaConfiguration::self();
d->mConfigFileWatcher.addPath(currentConfiguration->config()->name());
currentConfiguration->load();
currentConfiguration->read();
bool configurationHasChanged = false;
#if defined KF5Baloo_FOUND && KF5Baloo_FOUND
if (d->mBalooIndexerAvailable && d->mBalooIndexerActive && d->mBalooListener.canHandleRootPaths() && !currentConfiguration->forceUsageOfFastFileSearch()) {
configurationHasChanged = true;
} else if (d->mBalooIndexerAvailable && !d->mBalooIndexerActive && d->mBalooListener.canHandleRootPaths() && currentConfiguration->forceUsageOfFastFileSearch()) {
configurationHasChanged = true;
}
#endif
auto inputRootPath = currentConfiguration->rootPath();
configurationHasChanged = configurationHasChanged || (d->mPreviousRootPathValue != inputRootPath);
if (configurationHasChanged) {
d->mPreviousRootPathValue = inputRootPath;
} else {
qCDebug(orgKdeElisaIndexersManager()) << "music paths configuration and scanning has not changed";
return;
}
//resolve symlinks
QStringList allRootPaths;
for (const auto &onePath : inputRootPath) {
auto workPath = onePath;
if (workPath.startsWith(QLatin1String("file:/"))) {
auto urlPath = QUrl{workPath};
workPath = urlPath.toLocalFile();
}
QFileInfo pathFileInfo(workPath);
auto directoryPath = pathFileInfo.canonicalFilePath();
if (!directoryPath.isEmpty()) {
if (directoryPath.rightRef(1) != QLatin1Char('/'))
{
directoryPath.append(QLatin1Char('/'));
}
allRootPaths.push_back(directoryPath);
}
}
if (allRootPaths.isEmpty()) {
allRootPaths = initializeRootPath();
}
d->mFileListener.setAllRootPaths(allRootPaths);
#if defined KF5Baloo_FOUND && KF5Baloo_FOUND
d->mBalooListener.setAllRootPaths(allRootPaths);
#endif
if (!d->mBalooIndexerActive && !d->mFileSystemIndexerActive) {
testBalooIndexerAvailability();
}
#if defined KF5Baloo_FOUND && KF5Baloo_FOUND
if (d->mBalooIndexerAvailable && !d->mBalooIndexerActive && d->mBalooListener.canHandleRootPaths() && currentConfiguration->forceUsageOfFastFileSearch()) {
qCDebug(orgKdeElisaIndexersManager()) << "trigger start of baloo file indexer";
QMetaObject::invokeMethod(d->mFileListener.fileListing(), "stop", Qt::BlockingQueuedConnection);
d->mFileSystemIndexerActive = false;
startBalooIndexing();
} else if ((!d->mFileSystemIndexerActive && d->mBalooIndexerActive && !d->mBalooListener.canHandleRootPaths()) ||
!currentConfiguration->forceUsageOfFastFileSearch()) {
if (d->mBalooIndexerActive) {
qCDebug(orgKdeElisaIndexersManager()) << "trigger stop of baloo file indexer";
QMetaObject::invokeMethod(d->mBalooListener.fileListing(), "stop", Qt::BlockingQueuedConnection);
}
d->mBalooIndexerActive = false;
startLocalFileSystemIndexing();
}
#endif
if (d->mBalooIndexerActive) {
qCInfo(orgKdeElisaIndexersManager()) << "trigger init of baloo file indexer";
#if defined KF5Baloo_FOUND && KF5Baloo_FOUND
QMetaObject::invokeMethod(d->mBalooListener.fileListing(), "init", Qt::QueuedConnection);
#endif
} else if (d->mFileSystemIndexerActive) {
qCInfo(orgKdeElisaIndexersManager()) << "trigger init of local file indexer";
QMetaObject::invokeMethod(d->mFileListener.fileListing(), "init", Qt::QueuedConnection);
}
#if defined UPNPQT_FOUND && UPNPQT_FOUND
d->mUpnpListener.setDatabaseInterface(&d->mDatabaseInterface);
d->mUpnpListener.moveToThread(&d->mDatabaseThread);
connect(this, &MusicListenersManager::applicationIsTerminating,
&d->mUpnpListener, &UpnpListener::applicationAboutToQuit, Qt::DirectConnection);
#endif
#if defined Qt5AndroidExtras_FOUND && Qt5AndroidExtras_FOUND
if (!d->mAndroidMusicListener) {
d->mAndroidMusicListener = std::make_unique();
d->mAndroidMusicListener->moveToThread(&d->mListenerThread);
d->mAndroidMusicListener->setDatabaseInterface(&d->mDatabaseInterface);
connect(this, &MusicListenersManager::applicationIsTerminating,
d->mAndroidMusicListener.get(), &AndroidMusicListener::applicationAboutToQuit, Qt::DirectConnection);
connect(d->mAndroidMusicListener.get(), &AndroidMusicListener::indexingStarted,
this, &MusicListenersManager::monitorStartingListeners);
connect(d->mAndroidMusicListener.get(), &AndroidMusicListener::indexingFinished,
this, &MusicListenersManager::monitorEndingListeners);
}
#endif
}
void MusicListenersManager::increaseImportedTracksCount(const DataTypes::ListTrackDataType &allTracks)
{
d->mImportedTracksCount += allTracks.size();
Q_EMIT importedTracksCountChanged();
}
void MusicListenersManager::decreaseImportedTracksCount()
{
--d->mImportedTracksCount;
Q_EMIT importedTracksCountChanged();
}
void MusicListenersManager::monitorStartingListeners()
{
d->mIndexerBusy = true;
Q_EMIT indexerBusyChanged();
}
void MusicListenersManager::monitorEndingListeners()
{
d->mIndexerBusy = false;
Q_EMIT indexerBusyChanged();
}
void MusicListenersManager::cleanedDatabase()
{
d->mImportedTracksCount = 0;
Q_EMIT importedTracksCountChanged();
Q_EMIT clearedDatabase();
}
void MusicListenersManager::balooAvailabilityChanged()
{
#if defined KF5Baloo_FOUND && KF5Baloo_FOUND
if (!d->mBalooDetector.balooAvailability() || !d->mBalooListener.canHandleRootPaths()) {
if (d->mBalooDetector.balooAvailability()) {
qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is available";
d->mBalooIndexerAvailable = true;
}
#else
if (true) {
#endif
#if defined KF5Baloo_FOUND && KF5Baloo_FOUND
if (!d->mBalooListener.canHandleRootPaths() && d->mBalooDetector.balooAvailability())
{
qCInfo(orgKdeElisaIndexersManager()) << "Baloo cannot handle all configured paths: falling back to plain filex indexer";
}
#endif
if (!d->mFileSystemIndexerActive) {
startLocalFileSystemIndexing();
}
return;
}
qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is available";
d->mBalooIndexerAvailable = true;
Q_EMIT balooIndexerAvailableChanged();
startBalooIndexing();
}
void MusicListenersManager::testBalooIndexerAvailability()
{
#if defined KF5Baloo_FOUND && KF5Baloo_FOUND
d->mBalooDetector.checkBalooAvailability();
#else
qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is unavailable";
d->mBalooIndexerAvailable = false;
Q_EMIT balooIndexerAvailableChanged();
qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is inactive";
d->mBalooIndexerActive = false;
Q_EMIT balooIndexerActiveChanged();
startLocalFileSystemIndexing();
#endif
}
void MusicListenersManager::startLocalFileSystemIndexing()
{
if (d->mFileSystemIndexerActive) {
return;
}
d->mFileListener.setDatabaseInterface(&d->mDatabaseInterface);
d->mFileListener.moveToThread(&d->mListenerThread);
connect(this, &MusicListenersManager::applicationIsTerminating,
&d->mFileListener, &FileListener::applicationAboutToQuit, Qt::DirectConnection);
connect(&d->mFileListener, &FileListener::indexingStarted,
this, &MusicListenersManager::monitorStartingListeners);
connect(&d->mFileListener, &FileListener::indexingFinished,
this, &MusicListenersManager::monitorEndingListeners);
qCInfo(orgKdeElisaIndexersManager) << "Local file system indexer is active";
d->mFileSystemIndexerActive = true;
Q_EMIT fileSystemIndexerActiveChanged();
}
void MusicListenersManager::startBalooIndexing()
{
#if defined KF5Baloo_FOUND && KF5Baloo_FOUND
d->mBalooListener.moveToThread(&d->mListenerThread);
d->mBalooListener.setDatabaseInterface(&d->mDatabaseInterface);
connect(this, &MusicListenersManager::applicationIsTerminating,
&d->mBalooListener, &BalooListener::applicationAboutToQuit, Qt::DirectConnection);
connect(&d->mBalooListener, &BalooListener::indexingStarted,
this, &MusicListenersManager::monitorStartingListeners);
connect(&d->mBalooListener, &BalooListener::indexingFinished,
this, &MusicListenersManager::monitorEndingListeners);
connect(&d->mBalooListener, &BalooListener::clearDatabase,
&d->mDatabaseInterface, &DatabaseInterface::clearData);
qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is active";
d->mBalooIndexerActive = true;
Q_EMIT balooIndexerActiveChanged();
#endif
}
void MusicListenersManager::createTracksListener()
{
if (!d->mTracksListener) {
d->mTracksListener = std::make_unique(&d->mDatabaseInterface);
d->mTracksListener->moveToThread(&d->mDatabaseThread);
connect(this, &MusicListenersManager::removeTracksInError,
&d->mDatabaseInterface, &DatabaseInterface::removeTracksList);
connect(&d->mDatabaseInterface, &DatabaseInterface::trackRemoved, d->mTracksListener.get(), &TracksListener::trackRemoved);
connect(&d->mDatabaseInterface, &DatabaseInterface::tracksAdded, d->mTracksListener.get(), &TracksListener::tracksAdded);
connect(&d->mDatabaseInterface, &DatabaseInterface::trackModified, d->mTracksListener.get(), &TracksListener::trackModified);
Q_EMIT tracksListenerChanged();
}
}
#include "moc_musiclistenersmanager.cpp"
diff --git a/src/qml/ElisaMainWindow.qml b/src/qml/ElisaMainWindow.qml
index 6bdaef25..8811caab 100644
--- a/src/qml/ElisaMainWindow.qml
+++ b/src/qml/ElisaMainWindow.qml
@@ -1,336 +1,339 @@
/*
* Copyright 2016-2018 Matthieu Gallien
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
import QtQuick 2.7
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.1
import QtQuick.Window 2.2
import org.kde.kirigami 2.5 as Kirigami
import org.kde.elisa 1.0
import Qt.labs.settings 1.0
+import Qt.labs.platform 1.1
ApplicationWindow {
id: mainWindow
visible: true
minimumWidth: 590
property int minHeight: 320
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")
Accessible.role: Accessible.Application
Accessible.name: title
property var goBackAction: elisa.action("go_back")
property var seekAction: elisa.action("Seek")
property var scrubAction: elisa.action("Scrub")
property var playPauseAction: elisa.action("Play-Pause")
property var findAction: elisa.action("edit_find")
Action {
shortcut: goBackAction.shortcut
onTriggered: contentView.goBack()
}
Action {
shortcut: seekAction.shortcut
onTriggered: elisa.audioControl.seek(headerBar.playerControl.position + 10000)
}
Action {
shortcut: scrubAction.shortcut
onTriggered: elisa.audioControl.seek(headerBar.playerControl.position - 10000)
}
Action {
shortcut: playPauseAction.shortcut
onTriggered: elisa.audioControl.playPause()
}
Action {
shortcut: findAction.shortcut
onTriggered: persistentSettings.expandedFilterView = !persistentSettings.expandedFilterView
}
ApplicationMenu {
id: applicationMenu
}
SystemPalette {
id: myPalette
colorGroup: SystemPalette.Active
}
Theme {
id: elisaTheme
}
Settings {
id: persistentSettings
property int x
property int y
property int width : 900
property int height : 650
property var playListState
property var audioPlayerState
property double playControlItemVolume : 100.0
property bool playControlItemMuted : false
property bool expandedFilterView: false
property bool showPlaylist: true
property bool headerBarIsMaximized: false
}
Connections {
target: headerBar.playerControl
onOpenMenu: {
if (applicationMenu.visible) {
applicationMenu.close()
} else {
applicationMenu.popup(mainWindow.width - applicationMenu.width, headerBar.height)
}
}
}
Connections {
target: Qt.application
onAboutToQuit:
{
persistentSettings.x = mainWindow.x;
persistentSettings.y = mainWindow.y;
persistentSettings.width = mainWindow.width;
persistentSettings.height = mainWindow.height;
persistentSettings.playListState = elisa.mediaPlayListProxyModel.persistentState;
persistentSettings.audioPlayerState = elisa.audioControl.persistentState
persistentSettings.playControlItemVolume = headerBar.playerControl.volume
persistentSettings.playControlItemMuted = headerBar.playerControl.muted
persistentSettings.showPlaylist = contentView.showPlaylist
persistentSettings.headerBarIsMaximized = headerBar.isMaximized
}
}
Loader {
id: mprisloader
active: false
sourceComponent: PlatformIntegration {
id: platformInterface
playListModel: elisa.mediaPlayListProxyModel
audioPlayerManager: elisa.audioControl
player: elisa.audioPlayer
headerBarManager: elisa.manageHeaderBar
manageMediaPlayerControl: elisa.playerControl
showProgressOnTaskBar: elisa.showProgressOnTaskBar
+ showSystemTrayIcon: elisa.showSystemTrayIcon
+ elisaMainWindow: mainWindow
onRaisePlayer: {
- mainWindow.show()
+ mainWindow.visible = true
mainWindow.raise()
mainWindow.requestActivate()
}
}
}
Connections {
target: elisa.audioPlayer
onVolumeChanged: headerBar.playerControl.volume = elisa.audioPlayer.volume
onMutedChanged: headerBar.playerControl.muted = elisa.audioPlayer.muted
}
Rectangle {
color: myPalette.base
anchors.fill: parent
ColumnLayout {
anchors.fill: parent
spacing: 0
HeaderBar {
id: headerBar
focus: true
Layout.minimumHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight
Layout.maximumHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight
Layout.fillWidth: true
tracksCount: elisa.mediaPlayListProxyModel.remainingTracks
album: (elisa.manageHeaderBar.album !== undefined ? elisa.manageHeaderBar.album : '')
title: elisa.manageHeaderBar.title
artist: (elisa.manageHeaderBar.artist !== undefined ? elisa.manageHeaderBar.artist : '')
albumArtist: (elisa.manageHeaderBar.albumArtist !== undefined ? elisa.manageHeaderBar.albumArtist : '')
image: elisa.manageHeaderBar.image
albumID: elisa.manageHeaderBar.albumId
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: elisa.mediaPlayListProxyModel.repeatPlay
playerControl.shuffle: elisa.mediaPlayListProxyModel.shufflePlayList
playerControl.onSeek: elisa.audioPlayer.seek(position)
playerControl.onPlay: elisa.audioControl.playPause()
playerControl.onPause: elisa.audioControl.playPause()
playerControl.onPlayPrevious: elisa.mediaPlayListProxyModel.skipPreviousTrack()
playerControl.onPlayNext: elisa.mediaPlayListProxyModel.skipNextTrack()
playerControl.isMaximized: persistentSettings.headerBarIsMaximized
onOpenArtist: { contentView.openArtist(artist) }
onOpenNowPlaying: { contentView.openNowPlaying() }
onOpenAlbum: { contentView.openAlbum(album, albumArtist, image, albumID) }
TrackImportNotification {
id: importedTracksCountNotification
anchors
{
right: headerBar.right
top: headerBar.top
rightMargin: Kirigami.Units.largeSpacing * 2
topMargin: Kirigami.Units.largeSpacing * 3
}
}
Binding {
id: indexerBusyBinding
target: importedTracksCountNotification
property: 'indexingRunning'
value: elisa.musicManager.indexerBusy
when: elisa.musicManager !== undefined
}
Binding {
target: importedTracksCountNotification
property: 'importedTracksCount'
value: elisa.musicManager.importedTracksCount
when: elisa.musicManager !== undefined
}
}
Rectangle {
Layout.fillWidth: true
height: 1
color: myPalette.mid
}
ContentView {
id: contentView
Layout.fillHeight: true
Layout.fillWidth: true
showPlaylist: persistentSettings.showPlaylist
showExpandedFilterView: persistentSettings.expandedFilterView
}
}
}
StateGroup {
id: mainWindowState
states: [
State {
name: "headerBarIsNormal"
when: !headerBar.isMaximized
changes: [
PropertyChanges {
target: mainWindow
minimumHeight: mainWindow.minHeight * 1.5
explicit: true
},
PropertyChanges {
target: headerBar
Layout.minimumHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight
Layout.maximumHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight
}
]
},
State {
name: "headerBarIsMaximized"
when: headerBar.isMaximized
changes: [
PropertyChanges {
target: mainWindow
minimumHeight: mainWindow.minHeight
explicit: true
},
PropertyChanges {
target: headerBar
Layout.minimumHeight: mainWindow.height
Layout.maximumHeight: mainWindow.height
}
]
}
]
transitions: Transition {
NumberAnimation {
properties: "Layout.minimumHeight, Layout.maximumHeight, minimumHeight"
easing.type: Easing.InOutQuad
duration: Kirigami.Units.longDuration
}
}
}
Component.onCompleted:
{
elisa.initialize()
if (persistentSettings.playListState) {
elisa.mediaPlayListProxyModel.persistentState = persistentSettings.playListState
}
if (persistentSettings.audioPlayerState) {
elisa.audioControl.persistentState = persistentSettings.audioPlayerState
}
elisa.mediaPlayListProxyModel.shufflePlayList = Qt.binding(function() { return headerBar.playerControl.shuffle })
elisa.mediaPlayListProxyModel.repeatPlay = Qt.binding(function() { return headerBar.playerControl.repeat })
elisa.audioPlayer.muted = Qt.binding(function() { return headerBar.playerControl.muted })
elisa.audioPlayer.volume = Qt.binding(function() { return headerBar.playerControl.volume })
mprisloader.active = true
}
}
diff --git a/src/qml/GeneralConfiguration.qml b/src/qml/GeneralConfiguration.qml
index c5ad86fa..8564d4c9 100644
--- a/src/qml/GeneralConfiguration.qml
+++ b/src/qml/GeneralConfiguration.qml
@@ -1,37 +1,45 @@
/*
* Copyright 2017 Matthieu Gallien
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
import QtQuick 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.3
ColumnLayout {
spacing: 0
SystemPalette {
id: myPalette
colorGroup: SystemPalette.Active
}
CheckBox {
checked: config.showProgressInTaskBar
text: i18n("Show progress on Task Manager entries")
onCheckedChanged: config.showProgressInTaskBar = checked
}
+
+ CheckBox {
+ checked: config.showSystemTrayIcon
+
+ text: i18n("Keep running in System Tray when main window is closed")
+
+ onToggled: config.showSystemTrayIcon = checked
+ }
}
diff --git a/src/qml/PlatformIntegration.qml b/src/qml/PlatformIntegration.qml
index 4b3fe671..6867edee 100644
--- a/src/qml/PlatformIntegration.qml
+++ b/src/qml/PlatformIntegration.qml
@@ -1,49 +1,81 @@
/*
* Copyright 2017 Matthieu Gallien
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
import QtQuick 2.7
-import Qt.labs.platform 1.0 as NativeMenu
+import Qt.labs.platform 1.1 as NativeMenu
import org.kde.elisa 1.0
Item {
id: rootItem
property alias playListModel: mpris2Interface.playListModel
property alias audioPlayerManager: mpris2Interface.audioPlayerManager
property alias player: mpris2Interface.audioPlayer
property alias headerBarManager: mpris2Interface.headerBarManager
property alias manageMediaPlayerControl: mpris2Interface.manageMediaPlayerControl
property alias showProgressOnTaskBar: mpris2Interface.showProgressOnTaskBar
+ property bool showSystemTrayIcon
+ property var elisaMainWindow
signal raisePlayer()
+ Connections {
+ target: elisaMainWindow
+
+ onClosing: {
+ if (systemTrayIcon.available && showSystemTrayIcon) {
+ close.accepted = false
+ elisaMainWindow.hide()
+ }
+ }
+ }
+
NativeMenu.MenuBar {
NativeApplicationMenu {
+ id: globalMenu
}
}
Mpris2 {
id: mpris2Interface
playerName: 'elisa'
onRaisePlayer:
{
rootItem.raisePlayer()
}
}
+
+ NativeMenu.SystemTrayIcon {
+ id: systemTrayIcon
+
+ icon.name: 'elisa'
+ tooltip: mainWindow.title
+ visible: available && showSystemTrayIcon && !mainWindow.visible
+
+ menu: globalMenu
+
+ onActivated: {
+ if (reason === NativeMenu.SystemTrayIcon.Trigger && !elisaMainWindow.visible) {
+ elisaMainWindow.visible = true
+ } else if (reason === NativeMenu.SystemTrayIcon.Trigger && elisaMainWindow.visible) {
+ raisePlayer()
+ }
+ }
+ }
}