Refactoring

This commit is contained in:
Jonas Kvinge
2024-10-22 18:12:33 +02:00
parent dfcf715291
commit 8da2b9cd94
623 changed files with 9071 additions and 5126 deletions

View File

@@ -1,182 +0,0 @@
/*
* SBSystemPreferences.h
*
* Generated with:
* sdef "/Applications/System Preferences.app" | sdp -fh --basename
*SBSystemPreferences -o SBSystemPreferences.h
*/
#import <AppKit/AppKit.h>
#import <ScriptingBridge/ScriptingBridge.h>
@class SBSystemPreferencesApplication, SBSystemPreferencesDocument,
SBSystemPreferencesWindow, SBSystemPreferencesPane,
SBSystemPreferencesAnchor;
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmultichar"
#pragma GCC diagnostic ignored "-Wfour-char-constants"
#endif
enum SBSystemPreferencesSaveOptions {
SBSystemPreferencesSaveOptionsYes = 'yes ' /* Save the file. */,
SBSystemPreferencesSaveOptionsNo = 'no ' /* Do not save the file. */,
SBSystemPreferencesSaveOptionsAsk =
'ask ' /* Ask the user whether or not to save the file. */
};
typedef enum SBSystemPreferencesSaveOptions SBSystemPreferencesSaveOptions;
enum SBSystemPreferencesPrintingErrorHandling {
SBSystemPreferencesPrintingErrorHandlingStandard =
'lwst' /* Standard PostScript error handling */,
SBSystemPreferencesPrintingErrorHandlingDetailed =
'lwdt' /* print a detailed report of PostScript errors */
};
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
typedef enum SBSystemPreferencesPrintingErrorHandling
SBSystemPreferencesPrintingErrorHandling;
/*
* Standard Suite
*/
// The application's top-level scripting object.
@interface SBSystemPreferencesApplication : SBApplication
- (SBElementArray*)documents;
- (SBElementArray*)windows;
@property(copy, readonly) NSString* name; // The name of the application.
@property(readonly) BOOL frontmost; // Is this the active application?
@property(copy, readonly)
NSString* version; // The version number of the application.
- (id)open:(id)x; // Open a document.
- (void)print:(id)x
withProperties:(NSDictionary*)withProperties
printDialog:(BOOL)printDialog; // Print a document.
- (void)quitSaving:
(SBSystemPreferencesSaveOptions)saving; // Quit the application.
- (BOOL)exists:(id)x; // Verify that an object exists.
@end
// A document.
@interface SBSystemPreferencesDocument : SBObject
@property(copy, readonly) NSString* name; // Its name.
@property(readonly) BOOL modified; // Has it been modified since the last save?
@property(copy, readonly) NSURL* file; // Its location on disk, if it has one.
- (void)closeSaving:(SBSystemPreferencesSaveOptions)saving
savingIn:(NSURL*)savingIn; // Close a document.
- (void)saveIn:(NSURL*)in_ as:(id)as; // Save a document.
- (void)printWithProperties:(NSDictionary*)withProperties
printDialog:(BOOL)printDialog; // Print a document.
- (void) delete; // Delete an object.
- (void)duplicateTo:(SBObject*)to
withProperties:(NSDictionary*)withProperties; // Copy an object.
- (void)moveTo:(SBObject*)to; // Move an object to a new location.
@end
// A window.
@interface SBSystemPreferencesWindow : SBObject
@property(copy, readonly) NSString* name; // The title of the window.
- (NSInteger)id; // The unique identifier of the window.
@property NSInteger index; // The index of the window, ordered front to back.
@property NSRect bounds; // The bounding rectangle of the window.
@property(readonly) BOOL closeable; // Does the window have a close button?
@property(readonly)
BOOL miniaturizable; // Does the window have a minimize button?
@property BOOL miniaturized; // Is the window minimized right now?
@property(readonly) BOOL resizable; // Can the window be resized?
@property BOOL visible; // Is the window visible right now?
@property(readonly) BOOL zoomable; // Does the window have a zoom button?
@property BOOL zoomed; // Is the window zoomed right now?
@property(copy, readonly) SBSystemPreferencesDocument*
document; // The document whose contents are displayed in the window.
- (void)closeSaving:(SBSystemPreferencesSaveOptions)saving
savingIn:(NSURL*)savingIn; // Close a document.
- (void)saveIn:(NSURL*)in_ as:(id)as; // Save a document.
- (void)printWithProperties:(NSDictionary*)withProperties
printDialog:(BOOL)printDialog; // Print a document.
- (void) delete; // Delete an object.
- (void)duplicateTo:(SBObject*)to
withProperties:(NSDictionary*)withProperties; // Copy an object.
- (void)moveTo:(SBObject*)to; // Move an object to a new location.
@end
/*
* System Preferences
*/
// System Preferences top level scripting object
@interface SBSystemPreferencesApplication (SystemPreferences)
- (SBElementArray*)panes;
@property(copy)
SBSystemPreferencesPane* currentPane; // the currently selected pane
@property(copy, readonly) SBSystemPreferencesWindow*
preferencesWindow; // the main preferences window
@property BOOL showAll; // Is SystemPrefs in show all view. (Setting to false
// will do nothing)
@end
// a preference pane
@interface SBSystemPreferencesPane : SBObject
- (SBElementArray*)anchors;
- (NSString*)id; // locale independent name of the preference pane; can refer
// to a pane using the expression: pane id "<name>"
@property(copy, readonly)
NSString* localizedName; // localized name of the preference pane
@property(copy, readonly) NSString* name; // name of the preference pane as it
// appears in the title bar; can
// refer to a pane using the
// expression: pane "<name>"
- (void)closeSaving:(SBSystemPreferencesSaveOptions)saving
savingIn:(NSURL*)savingIn; // Close a document.
- (void)saveIn:(NSURL*)in_ as:(id)as; // Save a document.
- (void)printWithProperties:(NSDictionary*)withProperties
printDialog:(BOOL)printDialog; // Print a document.
- (void) delete; // Delete an object.
- (void)duplicateTo:(SBObject*)to
withProperties:(NSDictionary*)withProperties; // Copy an object.
- (void)moveTo:(SBObject*)to; // Move an object to a new location.
- (id)reveal; // Reveals an anchor within a preference pane or preference pane
// itself
@end
// an anchor within a preference pane
@interface SBSystemPreferencesAnchor : SBObject
@property(copy, readonly)
NSString* name; // name of the anchor within a preference pane
- (void)closeSaving:(SBSystemPreferencesSaveOptions)saving
savingIn:(NSURL*)savingIn; // Close a document.
- (void)saveIn:(NSURL*)in_ as:(id)as; // Save a document.
- (void)printWithProperties:(NSDictionary*)withProperties
printDialog:(BOOL)printDialog; // Print a document.
- (void) delete; // Delete an object.
- (void)duplicateTo:(SBObject*)to
withProperties:(NSDictionary*)withProperties; // Copy an object.
- (void)moveTo:(SBObject*)to; // Move an object to a new location.
- (id)reveal; // Reveals an anchor within a preference pane or preference pane
// itself
@end

View File

@@ -34,18 +34,17 @@
#include "core/logging.h"
#include "shared_ptr.h"
#include "lazy.h"
#include "database.h"
#include "taskmanager.h"
#include "player.h"
#include "networkaccessmanager.h"
#include "includes/shared_ptr.h"
#include "includes/lazy.h"
#include "core/database.h"
#include "core/taskmanager.h"
#include "core/networkaccessmanager.h"
#include "core/player.h"
#include "tagreader/tagreaderclient.h"
#include "engine/devicefinders.h"
#ifndef Q_OS_WIN
# include "device/devicemanager.h"
#endif
#include "collection/collection.h"
#include "core/urlhandlers.h"
#include "device/devicemanager.h"
#include "collection/collectionlibrary.h"
#include "playlist/playlistbackend.h"
#include "playlist/playlistmanager.h"
#include "covermanager/albumcoverloader.h"
@@ -114,58 +113,57 @@ using namespace std::chrono_literals;
class ApplicationImpl {
public:
explicit ApplicationImpl(Application *app) :
tag_reader_client_([app](){
tagreader_client_([app](){
TagReaderClient *client = new TagReaderClient();
app->MoveToNewThread(client);
return client;
}),
database_([app]() {
Database *db = new Database(app);
app->MoveToNewThread(db);
QTimer::singleShot(30s, db, &Database::DoBackup);
return db;
Database *database = new Database(app->task_manager());
app->MoveToNewThread(database);
QTimer::singleShot(30s, database, &Database::DoBackup);
return database;
}),
task_manager_([]() { return new TaskManager(); }),
player_([app]() { return new Player(app); }),
player_([app]() { return new Player(app->task_manager(), app->url_handlers(), app->playlist_manager()); }),
network_([]() { return new NetworkAccessManager(); }),
device_finders_([]() { return new DeviceFinders(); }),
#ifndef Q_OS_WIN
device_manager_([app]() { return new DeviceManager(app); }),
#endif
collection_([app]() { return new SCollection(app); }),
url_handlers_([]() { return new UrlHandlers(); }),
device_manager_([app]() { return new DeviceManager(app->task_manager(), app->database(), app->tagreader_client(), app->albumcover_loader()); }),
collection_([app]() { return new CollectionLibrary(app->database(), app->task_manager(), app->tagreader_client(), app->albumcover_loader()); }),
playlist_backend_([this, app]() {
PlaylistBackend *backend = new PlaylistBackend(app);
app->MoveToThread(backend, database_->thread());
return backend;
PlaylistBackend *playlist_backend = new PlaylistBackend(app->database(), app->tagreader_client(), app->collection_backend());
app->MoveToThread(playlist_backend, database_->thread());
return playlist_backend;
}),
playlist_manager_([app]() { return new PlaylistManager(app); }),
playlist_manager_([app]() { return new PlaylistManager(app->task_manager(), app->tagreader_client(), app->url_handlers(), app->playlist_backend(), app->collection_backend(), app->current_albumcover_loader()); }),
cover_providers_([app]() {
CoverProviders *cover_providers = new CoverProviders();
// Initialize the repository of cover providers.
cover_providers->AddProvider(new LastFmCoverProvider(app, app->network()));
cover_providers->AddProvider(new MusicbrainzCoverProvider(app, app->network()));
cover_providers->AddProvider(new DiscogsCoverProvider(app, app->network()));
cover_providers->AddProvider(new DeezerCoverProvider(app, app->network()));
cover_providers->AddProvider(new MusixmatchCoverProvider(app, app->network()));
cover_providers->AddProvider(new OpenTidalCoverProvider(app, app->network()));
cover_providers->AddProvider(new LastFmCoverProvider(app->network()));
cover_providers->AddProvider(new MusicbrainzCoverProvider(app->network()));
cover_providers->AddProvider(new DiscogsCoverProvider(app->network()));
cover_providers->AddProvider(new DeezerCoverProvider(app->network()));
cover_providers->AddProvider(new MusixmatchCoverProvider(app->network()));
cover_providers->AddProvider(new OpenTidalCoverProvider(app->network()));
#ifdef HAVE_TIDAL
cover_providers->AddProvider(new TidalCoverProvider(app, app->network()));
cover_providers->AddProvider(new TidalCoverProvider(app->streaming_services()->Service<TidalService>(), app->network()));
#endif
#ifdef HAVE_SPOTIFY
cover_providers->AddProvider(new SpotifyCoverProvider(app, app->network()));
cover_providers->AddProvider(new SpotifyCoverProvider(app->streaming_services()->Service<SpotifyService>(), app->network()));
#endif
#ifdef HAVE_QOBUZ
cover_providers->AddProvider(new QobuzCoverProvider(app, app->network()));
cover_providers->AddProvider(new QobuzCoverProvider(app->streaming_services()->Service<QobuzService>(), app->network()));
#endif
cover_providers->ReloadSettings();
return cover_providers;
}),
album_cover_loader_([app]() {
AlbumCoverLoader *loader = new AlbumCoverLoader();
albumcover_loader_([app]() {
AlbumCoverLoader *loader = new AlbumCoverLoader(app->tagreader_client());
app->MoveToNewThread(loader);
return loader;
}),
current_albumcover_loader_([app]() { return new CurrentAlbumCoverLoader(app); }),
current_albumcover_loader_([app]() { return new CurrentAlbumCoverLoader(app->albumcover_loader()); }),
lyrics_providers_([app]() {
LyricsProviders *lyrics_providers = new LyricsProviders(app);
// Initialize the repository of lyrics providers.
@@ -185,51 +183,50 @@ class ApplicationImpl {
streaming_services_([app]() {
StreamingServices *streaming_services = new StreamingServices();
#ifdef HAVE_SUBSONIC
streaming_services->AddService(make_shared<SubsonicService>(app));
streaming_services->AddService(make_shared<SubsonicService>(app->task_manager(), app->database(), app->url_handlers(), app->albumcover_loader()));
#endif
#ifdef HAVE_TIDAL
streaming_services->AddService(make_shared<TidalService>(app));
streaming_services->AddService(make_shared<TidalService>(app->task_manager(), app->database(), app->network(), app->url_handlers(), app->albumcover_loader()));
#endif
#ifdef HAVE_SPOTIFY
streaming_services->AddService(make_shared<SpotifyService>(app));
streaming_services->AddService(make_shared<SpotifyService>(app->task_manager(), app->database(), app->network(), app->albumcover_loader()));
#endif
#ifdef HAVE_QOBUZ
streaming_services->AddService(make_shared<QobuzService>(app));
streaming_services->AddService(make_shared<QobuzService>(app->task_manager(), app->database(), app->network(), app->url_handlers(), app->albumcover_loader()));
#endif
return streaming_services;
}),
radio_services_([app]() { return new RadioServices(app); }),
radio_services_([app]() { return new RadioServices(app->task_manager(), app->network(), app->database(), app->albumcover_loader()); }),
scrobbler_([app]() {
AudioScrobbler *scrobbler = new AudioScrobbler(app);
scrobbler->AddService(make_shared<LastFMScrobbler>(scrobbler->settings(), app->network()));
scrobbler->AddService(make_shared<LibreFMScrobbler>(scrobbler->settings(), app->network()));
scrobbler->AddService(make_shared<ListenBrainzScrobbler>(scrobbler->settings(), app->network()));
#ifdef HAVE_SUBSONIC
scrobbler->AddService(make_shared<SubsonicScrobbler>(scrobbler->settings(), app));
scrobbler->AddService(make_shared<SubsonicScrobbler>(scrobbler->settings(), app->streaming_services()->Service<SubsonicService>(), app));
#endif
return scrobbler;
}),
#ifdef HAVE_MOODBAR
moodbar_loader_([app]() { return new MoodbarLoader(app); }),
moodbar_controller_([app]() { return new MoodbarController(app); }),
moodbar_controller_([app]() { return new MoodbarController(app->player(), app->moodbar_loader()); }),
#endif
lastfm_import_([app]() { return new LastFMImport(app->network()); })
{}
Lazy<TagReaderClient> tag_reader_client_;
Lazy<TagReaderClient> tagreader_client_;
Lazy<Database> database_;
Lazy<TaskManager> task_manager_;
Lazy<Player> player_;
Lazy<NetworkAccessManager> network_;
Lazy<DeviceFinders> device_finders_;
#ifndef Q_OS_WIN
Lazy<UrlHandlers> url_handlers_;
Lazy<DeviceManager> device_manager_;
#endif
Lazy<SCollection> collection_;
Lazy<CollectionLibrary> collection_;
Lazy<PlaylistBackend> playlist_backend_;
Lazy<PlaylistManager> playlist_manager_;
Lazy<CoverProviders> cover_providers_;
Lazy<AlbumCoverLoader> album_cover_loader_;
Lazy<AlbumCoverLoader> albumcover_loader_;
Lazy<CurrentAlbumCoverLoader> current_albumcover_loader_;
Lazy<LyricsProviders> lyrics_providers_;
Lazy<StreamingServices> streaming_services_;
@@ -250,9 +247,7 @@ Application::Application(QObject *parent)
device_finders()->Init();
collection()->Init();
tag_reader_client();
QObject::connect(&*database(), &Database::Error, this, &Application::ErrorAdded);
tagreader_client();
}
@@ -294,32 +289,28 @@ void Application::MoveToThread(QObject *object, QThread *thread) {
void Application::Exit() {
wait_for_exit_ << &*tag_reader_client()
wait_for_exit_ << &*tagreader_client()
<< &*collection()
<< &*playlist_backend()
<< &*album_cover_loader()
#ifndef Q_OS_WIN
<< &*albumcover_loader()
<< &*device_manager()
#endif
<< &*streaming_services()
<< &*radio_services()->radio_backend();
QObject::connect(&*tag_reader_client(), &TagReaderClient::ExitFinished, this, &Application::ExitReceived);
tag_reader_client()->ExitAsync();
QObject::connect(&*tagreader_client(), &TagReaderClient::ExitFinished, this, &Application::ExitReceived);
tagreader_client()->ExitAsync();
QObject::connect(&*collection(), &SCollection::ExitFinished, this, &Application::ExitReceived);
QObject::connect(&*collection(), &CollectionLibrary::ExitFinished, this, &Application::ExitReceived);
collection()->Exit();
QObject::connect(&*playlist_backend(), &PlaylistBackend::ExitFinished, this, &Application::ExitReceived);
playlist_backend()->ExitAsync();
QObject::connect(&*album_cover_loader(), &AlbumCoverLoader::ExitFinished, this, &Application::ExitReceived);
album_cover_loader()->ExitAsync();
QObject::connect(&*albumcover_loader(), &AlbumCoverLoader::ExitFinished, this, &Application::ExitReceived);
albumcover_loader()->ExitAsync();
#ifndef Q_OS_WIN
QObject::connect(&*device_manager(), &DeviceManager::ExitFinished, this, &Application::ExitReceived);
device_manager()->Exit();
#endif
QObject::connect(&*streaming_services(), &StreamingServices::ExitFinished, this, &Application::ExitReceived);
streaming_services()->Exit();
@@ -345,23 +336,18 @@ void Application::ExitReceived() {
}
void Application::AddError(const QString &message) { Q_EMIT ErrorAdded(message); }
void Application::ReloadSettings() { Q_EMIT SettingsChanged(); }
void Application::OpenSettingsDialogAtPage(SettingsDialog::Page page) { Q_EMIT SettingsDialogRequested(page); }
SharedPtr<TagReaderClient> Application::tag_reader_client() const { return p_->tag_reader_client_.ptr(); }
SharedPtr<TagReaderClient> Application::tagreader_client() const { return p_->tagreader_client_.ptr(); }
SharedPtr<Database> Application::database() const { return p_->database_.ptr(); }
SharedPtr<TaskManager> Application::task_manager() const { return p_->task_manager_.ptr(); }
SharedPtr<Player> Application::player() const { return p_->player_.ptr(); }
SharedPtr<NetworkAccessManager> Application::network() const { return p_->network_.ptr(); }
SharedPtr<DeviceFinders> Application::device_finders() const { return p_->device_finders_.ptr(); }
#ifndef Q_OS_WIN
SharedPtr<UrlHandlers> Application::url_handlers() const { return p_->url_handlers_.ptr(); }
SharedPtr<DeviceManager> Application::device_manager() const { return p_->device_manager_.ptr(); }
#endif
SharedPtr<SCollection> Application::collection() const { return p_->collection_.ptr(); }
SharedPtr<CollectionLibrary> Application::collection() const { return p_->collection_.ptr(); }
SharedPtr<CollectionBackend> Application::collection_backend() const { return collection()->backend(); }
CollectionModel *Application::collection_model() const { return collection()->model(); }
SharedPtr<AlbumCoverLoader> Application::album_cover_loader() const { return p_->album_cover_loader_.ptr(); }
SharedPtr<AlbumCoverLoader> Application::albumcover_loader() const { return p_->albumcover_loader_.ptr(); }
SharedPtr<CoverProviders> Application::cover_providers() const { return p_->cover_providers_.ptr(); }
SharedPtr<CurrentAlbumCoverLoader> Application::current_albumcover_loader() const { return p_->current_albumcover_loader_.ptr(); }
SharedPtr<LyricsProviders> Application::lyrics_providers() const { return p_->lyrics_providers_.ptr(); }

View File

@@ -29,10 +29,8 @@
#include <QList>
#include <QString>
#include "scoped_ptr.h"
#include "shared_ptr.h"
#include "settings/settingsdialog.h"
#include "includes/scoped_ptr.h"
#include "includes/shared_ptr.h"
class QThread;
@@ -41,16 +39,15 @@ class ApplicationImpl;
class TagReaderClient;
class Database;
class DeviceFinders;
class UrlHandlers;
class Player;
class NetworkAccessManager;
class SCollection;
class CollectionLibrary;
class CollectionBackend;
class CollectionModel;
class PlaylistBackend;
class PlaylistManager;
#ifndef Q_OS_WIN
class DeviceManager;
#endif
class CoverProviders;
class AlbumCoverLoader;
class CurrentAlbumCoverLoader;
@@ -72,17 +69,16 @@ class Application : public QObject {
explicit Application(QObject *parent = nullptr);
~Application() override;
SharedPtr<TagReaderClient> tag_reader_client() const;
SharedPtr<TagReaderClient> tagreader_client() const;
SharedPtr<Database> database() const;
SharedPtr<TaskManager> task_manager() const;
SharedPtr<Player> player() const;
SharedPtr<NetworkAccessManager> network() const;
SharedPtr<DeviceFinders> device_finders() const;
#ifndef Q_OS_WIN
SharedPtr<UrlHandlers> url_handlers() const;
SharedPtr<DeviceManager> device_manager() const;
#endif
SharedPtr<SCollection> collection() const;
SharedPtr<CollectionLibrary> collection() const;
SharedPtr<CollectionBackend> collection_backend() const;
CollectionModel *collection_model() const;
@@ -90,7 +86,7 @@ class Application : public QObject {
SharedPtr<PlaylistManager> playlist_manager() const;
SharedPtr<CoverProviders> cover_providers() const;
SharedPtr<AlbumCoverLoader> album_cover_loader() const;
SharedPtr<AlbumCoverLoader> albumcover_loader() const;
SharedPtr<CurrentAlbumCoverLoader> current_albumcover_loader() const;
SharedPtr<LyricsProviders> lyrics_providers() const;
@@ -115,23 +111,13 @@ class Application : public QObject {
private Q_SLOTS:
void ExitReceived();
public Q_SLOTS:
void AddError(const QString &message);
void ReloadSettings();
void OpenSettingsDialogAtPage(SettingsDialog::Page page);
Q_SIGNALS:
void ErrorAdded(const QString &message);
void SettingsChanged();
void SettingsDialogRequested(const SettingsDialog::Page page);
void ExitFinished();
void ClearPixmapDiskCache();
private:
ScopedPtr<ApplicationImpl> p_;
QList<QThread*> threads_;
QList<QObject*> wait_for_exit_;
};
#endif // APPLICATION_H

View File

@@ -1,34 +0,0 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// From Chromium src/base/macros.h
#include <cstddef> // For size_t.
// The arraysize(arr) macro returns the # of elements in an array arr.
// The expression is a compile-time constant, and therefore can be
// used in defining new arrays, for example. If you use arraysize on
// a pointer by mistake, you will get a compile-time error.
//
// One caveat is that arraysize() doesn't accept any array of an
// anonymous type or a type defined inside a function. In these rare
// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
// due to a limitation in C++'s template system. The limitation might
// eventually be removed, but it hasn't happened yet.
// This template function declaration is used in defining arraysize.
// Note that the function doesn't need an implementation, as we only
// use its type.
template<typename T, size_t N>
char (&ArraySizeHelper(T (&array)[N]))[N];
// That gcc wants both of these prototypes seems mysterious. VC, for
// its part, can't decide which to use (another mystery). Matching of
// template overloads: the final frontier.
#ifndef _MSC_VER
template<typename T, size_t N>
char (&ArraySizeHelper(const T (&array)[N]))[N];
#endif
#define arraysize(array) (sizeof(ArraySizeHelper(array)))

View File

@@ -45,7 +45,6 @@
#include "core/logging.h"
#include "taskmanager.h"
#include "database.h"
#include "application.h"
#include "sqlquery.h"
#include "scopedtransaction.h"
@@ -62,9 +61,9 @@ constexpr char kMagicAllSongsTables[] = "%allsongstables";
int Database::sNextConnectionId = 1;
QMutex Database::sNextConnectionIdMutex;
Database::Database(Application *app, QObject *parent, const QString &database_name) :
Database::Database(SharedPtr<TaskManager> task_manager, QObject *parent, const QString &database_name) :
QObject(parent),
app_(app),
task_manager_(task_manager),
injected_database_name_(database_name),
query_hash_(0),
startup_schema_version_(-1),
@@ -145,7 +144,7 @@ QSqlDatabase Database::Connect() {
}
if (!db.open()) {
app_->AddError(u"Database: "_s + db.lastError().text());
Q_EMIT Error(u"Database: "_s + db.lastError().text());
return db;
}
@@ -492,7 +491,7 @@ void Database::ReportErrors(const SqlQuery &query) {
bool Database::IntegrityCheck(const QSqlDatabase &db) {
qLog(Debug) << "Starting database integrity check";
const int task_id = app_->task_manager()->StartTask(tr("Integrity check"));
const int task_id = task_manager_->StartTask(tr("Integrity check"));
bool ok = false;
// Ask for 10 error messages at most.
@@ -509,8 +508,8 @@ bool Database::IntegrityCheck(const QSqlDatabase &db) {
break;
}
else {
if (!error_reported) { app_->AddError(tr("Database corruption detected.")); }
app_->AddError(u"Database: "_s + message);
if (!error_reported) { Q_EMIT Error(tr("Database corruption detected.")); }
Q_EMIT Error(u"Database: "_s + message);
error_reported = true;
}
}
@@ -519,7 +518,7 @@ bool Database::IntegrityCheck(const QSqlDatabase &db) {
ReportErrors(q);
}
app_->task_manager()->SetTaskFinished(task_id);
task_manager_->SetTaskFinished(task_id);
return ok;
@@ -563,7 +562,7 @@ void Database::BackupFile(const QString &filename) {
qLog(Debug) << "Starting database backup";
QString dest_filename = QStringLiteral("%1.bak").arg(filename);
const int task_id = app_->task_manager()->StartTask(tr("Backing up database"));
const int task_id = task_manager_->StartTask(tr("Backing up database"));
sqlite3 *source_connection = nullptr;
sqlite3 *dest_connection = nullptr;
@@ -575,7 +574,7 @@ void Database::BackupFile(const QString &filename) {
if (dest_connection) {
sqlite3_close(dest_connection);
}
app_->task_manager()->SetTaskFinished(task_id);
task_manager_->SetTaskFinished(task_id);
});
bool success = OpenDatabase(filename, &source_connection);
@@ -599,7 +598,7 @@ void Database::BackupFile(const QString &filename) {
do {
ret = sqlite3_backup_step(backup, 16);
const int page_count = sqlite3_backup_pagecount(backup);
app_->task_manager()->SetTaskProgress(task_id, page_count - sqlite3_backup_remaining(backup), page_count);
task_manager_->SetTaskProgress(task_id, page_count - sqlite3_backup_remaining(backup), page_count);
}
while (ret == SQLITE_OK);

View File

@@ -36,16 +36,17 @@
#include <QStringList>
#include <QRecursiveMutex>
#include "includes/shared_ptr.h"
#include "sqlquery.h"
class QThread;
class Application;
class TaskManager;
class Database : public QObject {
Q_OBJECT
public:
explicit Database(Application *app, QObject *parent = nullptr, const QString &database_name = QString());
explicit Database(SharedPtr<TaskManager> task_manager, QObject *parent = nullptr, const QString &database_name = QString());
~Database() override;
static const int kSchemaVersion;
@@ -102,7 +103,7 @@ class Database : public QObject {
void BackupFile(const QString &filename);
static bool OpenDatabase(const QString &filename, sqlite3 **connection);
Application *app_;
SharedPtr<TaskManager> task_manager_;
// Alias -> filename
QMap<QString, AttachedDatabase> attached_databases_;
@@ -130,16 +131,4 @@ class Database : public QObject {
};
class MemoryDatabase : public Database {
Q_OBJECT
public:
explicit MemoryDatabase(Application *app, QObject *parent = nullptr)
: Database(app, parent, QStringLiteral(":memory:")) {}
~MemoryDatabase() override {
// Make sure Qt doesn't reuse the same database
QSqlDatabase::removeDatabase(Connect().connectionName());
}
};
#endif // DATABASE_H

View File

@@ -1,19 +0,0 @@
#ifndef DBUS_METATYPES_H
#define DBUS_METATYPES_H
#include <QMetaType>
#include <QByteArray>
#include <QByteArrayList>
#include <QMap>
#include <QString>
#include <QDBusObjectPath>
Q_DECLARE_METATYPE(QByteArrayList)
using InterfacesAndProperties = QMap<QString, QVariantMap>;
using ManagedObjectList = QMap<QDBusObjectPath, InterfacesAndProperties>;
Q_DECLARE_METATYPE(InterfacesAndProperties)
Q_DECLARE_METATYPE(ManagedObjectList)
#endif // DBUS_METATYPES_H

View File

@@ -28,7 +28,7 @@
#include <QUrl>
#include <QMetaObject>
#include "shared_ptr.h"
#include "includes/shared_ptr.h"
#include "taskmanager.h"
#include "song.h"
#include "deletefiles.h"

View File

@@ -27,7 +27,7 @@
#include <QObject>
#include <QStringList>
#include "shared_ptr.h"
#include "includes/shared_ptr.h"
#include "song.h"
class QThread;

View File

@@ -0,0 +1,31 @@
/*
* Strawberry Music Player
* Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "enginemetadata.h"
#include "core/song.h"
EngineMetadata::EngineMetadata()
: type(Type::Any),
length(-1),
year(-1),
track(-1),
filetype(Song::FileType::Unknown),
samplerate(-1),
bitdepth(-1),
bitrate(-1) {}

56
src/core/enginemetadata.h Normal file
View File

@@ -0,0 +1,56 @@
/*
* Strawberry Music Player
* Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ENGINEMETADATA_H
#define ENGINEMETADATA_H
#include <QMetaType>
#include <QString>
#include <QUrl>
#include "core/song.h"
class EngineMetadata {
public:
EngineMetadata();
enum class Type {
Any,
Current,
Next
};
Type type;
QUrl media_url;
QUrl stream_url;
QString title;
QString artist;
QString album;
QString comment;
QString genre;
qint64 length;
int year;
int track;
Song::FileType filetype;
int samplerate;
int bitdepth;
int bitrate;
QString lyrics;
};
Q_DECLARE_METATYPE(EngineMetadata)
#endif // ENGINEMETADATA_H

View File

@@ -30,11 +30,11 @@
#include <QStandardPaths>
#include <QSettings>
#include "core/logging.h"
#include "logging.h"
#include "settings.h"
#include "iconmapper.h"
#include "settings/appearancesettingspage.h"
#include "includes/iconmapper.h"
#include "iconloader.h"
#include "constants/appearancesettings.h"
using namespace Qt::Literals::StringLiterals;
@@ -45,7 +45,7 @@ void IconLoader::Init() {
#if !defined(Q_OS_MACOS) && !defined(Q_OS_WIN)
Settings s;
s.beginGroup(AppearanceSettingsPage::kSettingsGroup);
s.beginGroup(AppearanceSettings::kSettingsGroup);
system_icons_ = s.value("system_icons", false).toBool();
s.endGroup();
#endif

View File

@@ -1,142 +0,0 @@
/*
* Strawberry Music Player
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ICONMAPPER_H
#define ICONMAPPER_H
#include "config.h"
#include <QtGlobal>
#include <QMap>
namespace IconMapper {
using namespace Qt::Literals::StringLiterals;
struct IconProperties {
explicit IconProperties() : min_size(0), max_size(0), allow_system_icon(true) {}
IconProperties(const QStringList &_names, const int _min_size = 16, const int _max_size = 512, const bool _allow_system_icon = true) : names(_names), min_size(_min_size), max_size(_max_size), allow_system_icon(_allow_system_icon) {}
QStringList names;
int min_size;
int max_size;
bool allow_system_icon;
};
static const QMap<QString, IconProperties> iconmapper_ = { // clazy:exclude=non-pod-global-static
{ u"albums"_s, { {u"media-optical"_s}} },
{ u"alsa"_s, { {}} },
{ u"application-exit"_s, { {}} },
{ u"applications-internet"_s, { {}} },
{ u"bluetooth"_s, { {u"preferences-system-bluetooth"_s, u"bluetooth-active"_s}} },
{ u"cdcase"_s, { {u"cdcover"_s, u"media-optical"_s}} },
{ u"media-optical"_s, { {u"cd"_s}} },
{ u"configure"_s, { {}} },
{ u"device-ipod-nano"_s, { {}} },
{ u"device-ipod"_s, { {}} },
{ u"device-phone"_s, { {}} },
{ u"device"_s, { {u"drive-removable-media-usb-pendrive"_s}} },
{ u"device-usb-drive"_s, { {}} },
{ u"device-usb-flash"_s, { {}} },
{ u"dialog-error"_s, { {}} },
{ u"dialog-information"_s, { {}} },
{ u"dialog-ok-apply"_s, { {}} },
{ u"dialog-password"_s, { {}} },
{ u"dialog-warning"_s, { {}} },
{ u"document-download"_s, { {u"download"_s}} },
{ u"document-new"_s, { {}} },
{ u"document-open-folder"_s, { {}} },
{ u"document-open"_s, { {}} },
{ u"document-save"_s, { {}} },
{ u"document-search"_s, { {}} },
{ u"document-open-remote"_s, { {}} },
{ u"download"_s, { {u"applications-internet"_s, u"network-workgroup"_s}} },
{ u"edit-clear-list"_s, { {u"edit-clear-list"_s, u"edit-clear-all"_s}} },
{ u"edit-clear-locationbar-ltr"_s, { {u"edit-clear-locationbar-ltr"_s}} },
{ u"edit-copy"_s, { {}} },
{ u"edit-delete"_s, { {}} },
{ u"edit-find"_s, { {}} },
{ u"edit-redo"_s, { {}} },
{ u"edit-rename"_s, { {}} },
{ u"edit-undo"_s, { {}} },
{ u"electrocompaniet"_s, { {}} },
{ u"equalizer"_s, { {u"view-media-equalizer"_s}} },
{ u"folder-new"_s, { {}} },
{ u"folder"_s, { {}} },
{ u"folder-sound"_s, { {u"folder-music"_s}} },
{ u"footsteps"_s, { {u"go-jump"_s}} },
{ u"go-down"_s, { {}} },
{ u"go-home"_s, { {}} },
{ u"go-jump"_s, { {}} },
{ u"go-next"_s, { {}} },
{ u"go-previous"_s, { {}} },
{ u"go-up"_s, { {}} },
{ u"gstreamer"_s, { {u"phonon-gstreamer"_s}} },
{ u"headset"_s, { {u"audio-headset"_s}} },
{ u"help-hint"_s, { {}} },
{ u"intel"_s, { {}} },
{ u"jack"_s, { {u"audio-input-line"_s}} },
{ u"keyboard"_s, { {u"input-keyboard"_s}} },
{ u"list-add"_s, { {}} },
{ u"list-remove"_s, { {}} },
{ u"love"_s, { {u"heart"_s, u"emblem-favorite"_s}} },
{ u"mcintosh-player"_s, { {}} },
{ u"mcintosh"_s, { {}} },
{ u"mcintosh-text"_s, { {}} },
{ u"media-eject"_s, { {}} },
{ u"media-playback-pause"_s, { {u"media-pause"_s}} },
{ u"media-playlist-repeat"_s, { {}} },
{ u"media-playlist-shuffle"_s, { {""_L1}} },
{ u"media-playback-start"_s, { {u"media-play"_s, u"media-playback-playing"_s}} },
{ u"media-seek-backward"_s, { {}} },
{ u"media-seek-forward"_s, { {}} },
{ u"media-skip-backward"_s, { {}} },
{ u"media-skip-forward"_s, { {}} },
{ u"media-playback-stop"_s, { {u"media-stop"_s}} },
{ u"moodbar"_s, { {u"preferences-desktop-icons"_s}} },
{ u"nvidia"_s, { {}} },
{ u"pulseaudio"_s, { {}} },
{ u"realtek"_s, { {}} },
{ u"scrobble-disabled"_s, { {}} },
{ u"scrobble"_s, { {u"love"_s}} },
{ u"search"_s, { {}} },
{ u"soundcard"_s, { {u"audiocard"_s, u"audio-card"_s}} },
{ u"speaker"_s, { {}} },
{ u"star-grey"_s, { {}} },
{ u"star"_s, { {}} },
{ u"strawberry"_s, { {}} },
{ u"subsonic"_s, { {}} },
{ u"tidal"_s, { {}} },
{ u"tools-wizard"_s, { {}} },
{ u"view-choose"_s, { {}} },
{ u"view-fullscreen"_s, { {}} },
{ u"view-media-lyrics"_s, { {}} },
{ u"view-media-playlist"_s, { {}} },
{ u"view-media-visualization"_s, { {u"preferences-desktop-theme"_s}} },
{ u"view-refresh"_s, { {}} },
{ u"library-music"_s, { {u"vinyl"_s}} },
{ u"vlc"_s, { {}} },
{ u"zoom-in"_s, { {}} },
{ u"zoom-out"_s, { {}, 0, 0 } }
};
} // namespace IconMapper
#endif // ICONMAPPER_H

View File

@@ -1,75 +0,0 @@
/* This file is part of Strawberry.
Copyright 2016, John Maguire <john.maguire@gmail.com>
Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Strawberry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LAZY_H
#define LAZY_H
#include <functional>
#include <type_traits>
#include "core/logging.h"
#include "shared_ptr.h"
// Helper for lazy initialization of objects.
// Usage:
// Lazy<Foo> my_lazy_object([]() { return new Foo; });
template<typename T>
class Lazy {
public:
explicit Lazy(std::function<T*()> init) : init_(init) {}
// Convenience constructor that will lazily default construct the object.
Lazy() : init_([]() { return new T; }) {}
T* get() const {
CheckInitialized();
return ptr_.get();
}
SharedPtr<T> ptr() const {
CheckInitialized();
return ptr_;
}
typename std::add_lvalue_reference<T>::type operator*() const {
CheckInitialized();
return *ptr_;
}
T* operator->() const { return get(); }
// Returns true if the object is not yet initialized.
explicit operator bool() const { return ptr_; }
// Deletes the underlying object and will re-run the initialization function if the object is requested again.
void reset() { ptr_.reset(); }
private:
void CheckInitialized() const {
if (!ptr_) {
ptr_ = SharedPtr<T>(init_(), [](T*obj) { qLog(Debug) << obj << "deleted"; delete obj; });
qLog(Debug) << &*ptr_ << "created";
}
}
const std::function<T*()> init_;
mutable SharedPtr<T> ptr_;
};
#endif // LAZY_H

View File

@@ -1,33 +0,0 @@
#import <AppKit/NSApplication.h>
#include "config.h"
#include "globalshortcuts/globalshortcutsbackend-macos.h"
class PlatformInterface;
@class SPMediaKeyTap;
@interface AppDelegate : NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate> {
PlatformInterface *application_handler_;
NSMenu *dock_menu_;
GlobalShortcutsBackendMacOS *shortcut_handler_;
SPMediaKeyTap *key_tap_;
}
- (id) initWithHandler: (PlatformInterface*)handler;
// NSApplicationDelegate
- (BOOL) applicationShouldHandleReopen: (NSApplication*)app hasVisibleWindows:(BOOL)flag;
- (NSMenu*) applicationDockMenu: (NSApplication*)sender;
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification;
- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*)sender;
// NSUserNotificationCenterDelegate
- (BOOL) userNotificationCenter: (id)center
shouldPresentNotification: (id)notification;
- (void) setDockMenu: (NSMenu*)menu;
- (GlobalShortcutsBackendMacOS*) shortcut_handler;
- (void) setShortcutHandler: (GlobalShortcutsBackendMacOS*)backend;
- (void) mediaKeyTap: (SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event;
@end

View File

@@ -45,15 +45,6 @@
#include "config.h"
#include "platforminterface.h"
#include "mac_delegate.h"
#include "mac_startup.h"
#include "scoped_cftyperef.h"
#include "core/logging.h"
#include "scoped_nsautorelease_pool.h"
#include "globalshortcuts/globalshortcutsmanager.h"
#include "globalshortcuts/globalshortcutsbackend-macos.h"
#include <QApplication>
#include <QCoreApplication>
#include <QWidget>
@@ -61,7 +52,14 @@
#include <QEvent>
#include <QFile>
#include <QtDebug>
#include "includes/mac_delegate.h"
#include "includes/scoped_cftyperef.h"
#include "core/scoped_nsautorelease_pool.h"
#include "core/logging.h"
#include "core/platforminterface.h"
#include "mac_startup.h"
#include "globalshortcuts/globalshortcutsmanager.h"
#include "globalshortcuts/globalshortcutsbackend-macos.h"
QDebug operator<<(QDebug dbg, NSObject *object) {

View File

@@ -1,101 +0,0 @@
/*
*Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MACSYSTEMTRAYICON_H
#define MACSYSTEMTRAYICON_H
#include "config.h"
#include <QObject>
#include <QUrl>
#include <QPixmap>
#include <QAction>
#include "scoped_ptr.h"
#include "song.h"
class MacSystemTrayIconPrivate;
class SystemTrayIcon : public QObject {
Q_OBJECT
public:
explicit SystemTrayIcon(QObject *parent = nullptr);
~SystemTrayIcon();
bool isSystemTrayAvailable() const { return true; }
bool IsSystemTrayAvailable() const { return true; }
bool isVisible() const { return true; }
void setVisible(const bool) {}
void SetTrayiconProgress(const bool enabled);
void SetupMenu(QAction *previous, QAction *play, QAction *stop, QAction *stop_after, QAction *next, QAction *mute, QAction *love, QAction *quit);
void ShowPopup(const QString&, const QString&, const int) {}
bool MuteEnabled() const { return false; }
void SetMuteEnabled(const bool) {}
void MuteButtonStateChanged(const bool) {}
void SetPlaying(bool enable_play_pause = false);
void SetPaused();
void SetStopped();
void SetNowPlaying(const Song &song, const QUrl&);
void ClearNowPlaying();
void SetProgress(const int percentage);
void LoveVisibilityChanged(const bool) {}
void LoveStateChanged(const bool) {}
private:
void SetupMenuItem(QAction *action);
QPixmap CreateIcon(const QPixmap &icon, const QPixmap &grey_icon);
void UpdateIcon();
private Q_SLOTS:
void ActionChanged();
Q_SIGNALS:
void ChangeVolume(const int delta);
void SeekForward();
void SeekBackward();
void NextTrack();
void PreviousTrack();
void ShowHide();
void PlayPause();
private:
ScopedPtr<MacSystemTrayIconPrivate> p_;
QPixmap normal_icon_;
QPixmap grey_icon_;
QPixmap playing_icon_;
QPixmap paused_icon_;
QPixmap current_state_icon_;
bool trayicon_progress_;
int song_progress_;
Q_DISABLE_COPY(SystemTrayIcon);
};
#endif // MACSYSTEMTRAYICON_H

View File

@@ -1,248 +0,0 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <QApplication>
#include <QHash>
#include <QString>
#include <QUrl>
#include <QIcon>
#include <QAction>
#include <AppKit/NSMenu.h>
#include <AppKit/NSMenuItem.h>
#include "macsystemtrayicon.h"
#include "mac_delegate.h"
#include "song.h"
#include "iconloader.h"
using namespace Qt::Literals::StringLiterals;
@interface Target :NSObject {
QAction *action_;
}
- (id) initWithQAction: (QAction*)action;
- (void) clicked;
@end
@implementation Target // <NSMenuValidation>
- (id) init {
return [super init];
}
- (id) initWithQAction: (QAction*)action {
action_ = action;
return self;
}
- (BOOL) validateMenuItem: (NSMenuItem*)menuItem {
Q_UNUSED(menuItem);
// This is called when the menu is shown.
return action_->isEnabled();
}
- (void) clicked {
action_->trigger();
}
@end
class MacSystemTrayIconPrivate {
public:
MacSystemTrayIconPrivate() {
dock_menu_ = [[NSMenu alloc] initWithTitle:@"DockMenu"];
QString title = QT_TR_NOOP(u"Now Playing"_s);
NSString *t = [[NSString alloc] initWithUTF8String:title.toUtf8().constData()];
now_playing_ = [[NSMenuItem alloc] initWithTitle:t action:nullptr keyEquivalent:@""];
now_playing_artist_ = [[NSMenuItem alloc] initWithTitle:@"Nothing to see here" action:nullptr keyEquivalent:@""];
now_playing_title_ = [[NSMenuItem alloc] initWithTitle:@"Nothing to see here" action:nullptr keyEquivalent:@""];
[dock_menu_ insertItem:now_playing_title_ atIndex:0];
[dock_menu_ insertItem:now_playing_artist_ atIndex:0];
[dock_menu_ insertItem:now_playing_ atIndex:0];
// Don't look now.
// This must be called after our custom NSApplicationDelegate has been set.
[reinterpret_cast<AppDelegate*>([NSApp delegate]) setDockMenu:dock_menu_];
ClearNowPlaying();
}
void AddMenuItem(QAction *action) {
// Strip accelarators from name.
QString text = action->text().remove(u'&');
NSString *title = [[NSString alloc] initWithUTF8String: text.toUtf8().constData()];
// Create an object that can receive user clicks and pass them on to the QAction.
Target *target = [[Target alloc] initWithQAction:action];
NSMenuItem *item = [[[NSMenuItem alloc] initWithTitle:title action:@selector(clicked) keyEquivalent:@""] autorelease];
[item setEnabled:action->isEnabled()];
[item setTarget:target];
[dock_menu_ addItem:item];
actions_[action] = item;
}
void ActionChanged(QAction *action) {
NSMenuItem *item = actions_[action];
NSString *title = [[NSString alloc] initWithUTF8String: action->text().toUtf8().constData()];
[item setTitle:title];
}
void AddSeparator() {
NSMenuItem *separator = [NSMenuItem separatorItem];
[dock_menu_ addItem:separator];
}
void ShowNowPlaying(const QString &artist, const QString &title) {
ClearNowPlaying(); // Makes sure the order is consistent.
[now_playing_artist_ setTitle: [[NSString alloc] initWithUTF8String: artist.toUtf8().constData()]];
[now_playing_title_ setTitle: [[NSString alloc] initWithUTF8String: title.toUtf8().constData()]];
title.isEmpty() ? HideItem(now_playing_title_) : ShowItem(now_playing_title_);
artist.isEmpty() ? HideItem(now_playing_artist_) : ShowItem(now_playing_artist_);
artist.isEmpty() && title.isEmpty() ? HideItem(now_playing_) : ShowItem(now_playing_);
}
void ClearNowPlaying() {
// Hiding doesn't seem to work in the dock menu.
HideItem(now_playing_);
HideItem(now_playing_artist_);
HideItem(now_playing_title_);
}
private:
void HideItem(NSMenuItem *item) {
if ([dock_menu_ indexOfItem:item] != -1) {
[dock_menu_ removeItem:item];
}
}
void ShowItem(NSMenuItem *item, int index = 0) {
if ([dock_menu_ indexOfItem:item] == -1) {
[dock_menu_ insertItem:item atIndex:index];
}
}
QHash<QAction*, NSMenuItem*> actions_;
NSMenu *dock_menu_;
NSMenuItem *now_playing_;
NSMenuItem *now_playing_artist_;
NSMenuItem *now_playing_title_;
Q_DISABLE_COPY(MacSystemTrayIconPrivate);
};
SystemTrayIcon::SystemTrayIcon(QObject *parent)
: QObject(parent),
normal_icon_(QPixmap(u":/pictures/strawberry.png"_s).scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)),
grey_icon_(QPixmap(u":/pictures/strawberry-grey.png"_s).scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)),
playing_icon_(u":/pictures/tiny-play.png"_s),
paused_icon_(u":/pictures/tiny-pause.png"_s),
trayicon_progress_(false),
song_progress_(0) {
QApplication::setWindowIcon(normal_icon_);
}
SystemTrayIcon::~SystemTrayIcon() {}
void SystemTrayIcon::SetTrayiconProgress(const bool enabled) {
trayicon_progress_ = enabled;
UpdateIcon();
}
void SystemTrayIcon::SetupMenu(QAction *previous, QAction *play, QAction *stop, QAction *stop_after, QAction *next, QAction *mute, QAction *love, QAction *quit) {
p_ = std::make_unique<MacSystemTrayIconPrivate>();
SetupMenuItem(previous);
SetupMenuItem(play);
SetupMenuItem(stop);
SetupMenuItem(stop_after);
SetupMenuItem(next);
p_->AddSeparator();
SetupMenuItem(mute);
p_->AddSeparator();
SetupMenuItem(love);
Q_UNUSED(quit); // Mac already has a Quit item.
}
void SystemTrayIcon::SetupMenuItem(QAction *action) {
p_->AddMenuItem(action);
QObject::connect(action, &QAction::changed, this, &SystemTrayIcon::ActionChanged);
}
void SystemTrayIcon::UpdateIcon() {
QApplication::setWindowIcon(CreateIcon(normal_icon_, grey_icon_));
}
void SystemTrayIcon::ActionChanged() {
QAction *action = qobject_cast<QAction*>(sender());
p_->ActionChanged(action);
}
void SystemTrayIcon::SetPlaying(const bool enable_play_pause) {
Q_UNUSED(enable_play_pause);
current_state_icon_ = playing_icon_;
UpdateIcon();
}
void SystemTrayIcon::SetPaused() {
current_state_icon_ = paused_icon_;
UpdateIcon();
}
void SystemTrayIcon::SetStopped() {
current_state_icon_ = QPixmap();
UpdateIcon();
}
void SystemTrayIcon::SetProgress(const int percentage) {
song_progress_ = percentage;
if (trayicon_progress_) UpdateIcon();
}
void SystemTrayIcon::ClearNowPlaying() {
p_->ClearNowPlaying();
}
void SystemTrayIcon::SetNowPlaying(const Song &song, const QUrl&) {
p_->ShowNowPlaying(song.artist(), song.PrettyTitle());
}

View File

@@ -84,52 +84,46 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "shared_ptr.h"
#include "commandlineoptions.h"
#include "mimedata.h"
#include "iconloader.h"
#include "taskmanager.h"
#include "song.h"
#include "stylehelper.h"
#include "stylesheetloader.h"
#include "constants/filefilterconstants.h"
#include "constants/timeconstants.h"
#include "constants/mainwindowsettings.h"
#include "includes/shared_ptr.h"
#include "core/commandlineoptions.h"
#include "core/mimedata.h"
#include "core/iconloader.h"
#include "core/taskmanager.h"
#include "core/song.h"
#include "core/stylehelper.h"
#include "core/stylesheetloader.h"
#include "application.h"
#include "database.h"
#include "player.h"
#include "filesystemmusicstorage.h"
#include "deletefiles.h"
#ifdef Q_OS_MACOS
# include "mac_startup.h"
# include "macsystemtrayicon.h"
# include "utilities/macosutils.h"
#else
# include "qtsystemtrayicon.h"
#endif
#include "networkaccessmanager.h"
#include "settings.h"
#include "core/database.h"
#include "core/filesystemmusicstorage.h"
#include "core/deletefiles.h"
#include "core/settings.h"
#include "core/player.h"
#include "utilities/envutils.h"
#include "utilities/filemanagerutils.h"
#include "utilities/timeconstants.h"
#include "utilities/screenutils.h"
#include "engine/enginebase.h"
#include "dialogs/errordialog.h"
#include "dialogs/about.h"
#include "dialogs/console.h"
#include "dialogs/trackselectiondialog.h"
#include "dialogs/edittagdialog.h"
#include "dialogs/addstreamdialog.h"
#include "dialogs/deleteconfirmationdialog.h"
#include "dialogs/lastfmimportdialog.h"
#include "dialogs/snapdialog.h"
#include "dialogs/edittagdialog.h"
#include "dialogs/trackselectiondialog.h"
#include "organize/organizedialog.h"
#include "widgets/fancytabwidget.h"
#include "widgets/playingwidget.h"
#include "widgets/volumeslider.h"
#include "widgets/fileview.h"
#include "widgets/multiloadingindicator.h"
#include "widgets/trackslider.h"
#include "fileview/fileview.h"
#include "osd/osdbase.h"
#include "context/contextview.h"
#include "collection/collection.h"
#include "collection/collectionlibrary.h"
#include "collection/collectionbackend.h"
#include "collection/collectiondirectorymodel.h"
#include "collection/collectionviewcontainer.h"
@@ -160,30 +154,31 @@
#include "covermanager/coverproviders.h"
#include "covermanager/albumcoverimageresult.h"
#include "lyrics/lyricsproviders.h"
#include "device/devicemanager.h"
#include "device/devicestatefiltermodel.h"
#ifndef Q_OS_WIN
# include "device/devicemanager.h"
# include "device/devicestatefiltermodel.h"
# include "device/deviceview.h"
# include "device/deviceviewcontainer.h"
#endif
#include "transcoder/transcodedialog.h"
#include "settings/settingsdialog.h"
#include "settings/behavioursettingspage.h"
#include "settings/backendsettingspage.h"
#include "settings/collectionsettingspage.h"
#include "settings/playlistsettingspage.h"
#include "constants/behavioursettings.h"
#include "constants/appearancesettings.h"
#include "constants/backendsettings.h"
#include "constants/collectionsettings.h"
#include "constants/playlistsettings.h"
#ifdef HAVE_SUBSONIC
# include "settings/subsonicsettingspage.h"
# include "constants/subsonicsettings.h"
#endif
#ifdef HAVE_TIDAL
# include "tidal/tidalservice.h"
# include "settings/tidalsettingspage.h"
# include "constants/tidalsettings.h"
#endif
#ifdef HAVE_SPOTIFY
# include "settings/spotifysettingspage.h"
# include "constants/spotifysettings.h"
#endif
#ifdef HAVE_QOBUZ
# include "settings/qobuzsettingspage.h"
# include "constants/qobuzsettings.h"
#endif
#include "streaming/streamingservices.h"
@@ -205,6 +200,7 @@
#ifdef HAVE_MOODBAR
# include "moodbar/moodbarcontroller.h"
# include "moodbar/moodbarloader.h"
# include "moodbar/moodbarproxystyle.h"
#endif
@@ -213,7 +209,15 @@
#include "organize/organizeerrordialog.h"
#ifdef Q_OS_WIN
# include "windows7thumbbar.h"
# include "core/windows7thumbbar.h"
#endif
#ifdef Q_OS_MACOS
# include "core/mac_startup.h"
# include "systemtrayicon/macsystemtrayicon.h"
# include "utilities/macosutils.h"
#else
# include "systemtrayicon/qtsystemtrayicon.h"
#endif
#ifdef HAVE_QTSPARKLE
@@ -225,9 +229,6 @@ using std::make_shared;
using namespace std::chrono_literals;
using namespace Qt::Literals::StringLiterals;
const char *MainWindow::kSettingsGroup = "MainWindow";
const char *MainWindow::kAllFilesFilterSpec = QT_TR_NOOP("All Files (*)");
namespace {
const int kTrackSliderUpdateTimeMs = 200;
const int kTrackPositionUpdateTimeMs = 1000;
@@ -258,8 +259,9 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
app_(app),
tray_icon_(tray_icon),
osd_(osd),
console_([app]() {
Console *console = new Console(app);
console_([app, this]() {
Console *console = new Console(app->database());
QObject::connect(console, &Console::Error, this, &MainWindow::ShowErrorDialog);
return console;
}),
edit_tag_dialog_(std::bind(&MainWindow::CreateEditTagDialog, this)),
@@ -277,7 +279,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
queue_view_(new QueueView(this)),
settings_dialog_(std::bind(&MainWindow::CreateSettingsDialog, this)),
cover_manager_([this, app]() {
AlbumCoverManager *cover_manager = new AlbumCoverManager(app, app->collection_backend(), this);
AlbumCoverManager *cover_manager = new AlbumCoverManager(app->network(), app->collection_backend(), app->tagreader_client(), app->albumcover_loader(), app->current_albumcover_loader(), app->cover_providers(), app->streaming_services(), this);
cover_manager->Init();
// Cover manager connections
@@ -287,7 +289,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
}),
equalizer_(new Equalizer),
organize_dialog_([this, app]() {
OrganizeDialog *dialog = new OrganizeDialog(app->task_manager(), app->collection_backend(), this);
OrganizeDialog *dialog = new OrganizeDialog(app->task_manager(), app->tagreader_client(), app->collection_backend(), this);
dialog->SetDestinationModel(app->collection()->model()->directory_model());
return dialog;
}),
@@ -300,18 +302,25 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(add_stream_dialog, &AddStreamDialog::accepted, this, &MainWindow::AddStreamAccepted);
return add_stream_dialog;
}),
smartplaylists_view_(new SmartPlaylistsViewContainer(app, this)),
smartplaylists_view_(new SmartPlaylistsViewContainer(app->player(),
app->playlist_manager(),
app->collection_backend(),
#ifdef HAVE_MOODBAR
app->moodbar_loader(),
#endif
app->current_albumcover_loader(),
this)),
#ifdef HAVE_SUBSONIC
subsonic_view_(new StreamingSongsView(app_, app->streaming_services()->ServiceBySource(Song::Source::Subsonic), QLatin1String(SubsonicSettingsPage::kSettingsGroup), SettingsDialog::Page::Subsonic, this)),
subsonic_view_(new StreamingSongsView(app->streaming_services()->ServiceBySource(Song::Source::Subsonic), QLatin1String(SubsonicSettings::kSettingsGroup), this)),
#endif
#ifdef HAVE_TIDAL
tidal_view_(new StreamingTabsView(app_, app->streaming_services()->ServiceBySource(Song::Source::Tidal), QLatin1String(TidalSettingsPage::kSettingsGroup), SettingsDialog::Page::Tidal, this)),
tidal_view_(new StreamingTabsView(app->streaming_services()->ServiceBySource(Song::Source::Tidal), app->albumcover_loader(), QLatin1String(TidalSettings::kSettingsGroup), this)),
#endif
#ifdef HAVE_SPOTIFY
spotify_view_(new StreamingTabsView(app_, app->streaming_services()->ServiceBySource(Song::Source::Spotify), QLatin1String(SpotifySettingsPage::kSettingsGroup), SettingsDialog::Page::Spotify, this)),
spotify_view_(new StreamingTabsView(app->streaming_services()->ServiceBySource(Song::Source::Spotify), app->albumcover_loader(), QLatin1String(SpotifySettings::kSettingsGroup), this)),
#endif
#ifdef HAVE_QOBUZ
qobuz_view_(new StreamingTabsView(app_, app->streaming_services()->ServiceBySource(Song::Source::Qobuz), QLatin1String(QobuzSettingsPage::kSettingsGroup), SettingsDialog::Page::Qobuz, this)),
qobuz_view_(new StreamingTabsView(app->streaming_services()->ServiceBySource(Song::Source::Qobuz), app->albumcover_loader(), QLatin1String(QobuzSettings::kSettingsGroup), this)),
#endif
radio_view_(new RadioViewContainer(this)),
lastfm_import_dialog_(new LastFMImportDialog(app_->lastfm_import(), this)),
@@ -345,10 +354,10 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
#ifdef HAVE_DBUS
taskbar_progress_(false),
#endif
doubleclick_addmode_(BehaviourSettingsPage::AddBehaviour::Append),
doubleclick_playmode_(BehaviourSettingsPage::PlayBehaviour::Never),
doubleclick_playlist_addmode_(BehaviourSettingsPage::PlaylistAddBehaviour::Play),
menu_playmode_(BehaviourSettingsPage::PlayBehaviour::Never),
doubleclick_addmode_(BehaviourSettings::AddBehaviour::Append),
doubleclick_playmode_(BehaviourSettings::PlayBehaviour::Never),
doubleclick_playlist_addmode_(BehaviourSettings::PlaylistAddBehaviour::Play),
menu_playmode_(BehaviourSettings::PlayBehaviour::Never),
initialized_(false),
was_maximized_(true),
was_minimized_(false),
@@ -358,19 +367,18 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
qLog(Debug) << "Starting";
QObject::connect(app, &Application::ErrorAdded, this, &MainWindow::ShowErrorDialog);
QObject::connect(app, &Application::SettingsDialogRequested, this, &MainWindow::OpenSettingsDialogAtPage);
// Initialize the UI
ui_->setupUi(this);
setWindowIcon(IconLoader::Load(u"strawberry"_s));
album_cover_choice_controller_->Init(app);
QObject::connect(&*app->database(), &Database::Error, this, &MainWindow::ShowErrorDialog);
album_cover_choice_controller_->Init(app->network(), app->tagreader_client(), app->collection()->backend(), app->albumcover_loader(), app->current_albumcover_loader(), app->cover_providers(), app->streaming_services());
ui_->multi_loading_indicator->SetTaskManager(app_->task_manager());
context_view_->Init(app_, collection_view_->view(), album_cover_choice_controller_);
ui_->widget_playing->Init(app_, album_cover_choice_controller_);
context_view_->Init(collection_view_->view(), album_cover_choice_controller_, app_->lyrics_providers());
ui_->widget_playing->Init(album_cover_choice_controller_);
// Initialize the search widget
StyleHelper::setBaseColor(palette().color(QPalette::Highlight).darker());
@@ -402,7 +410,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
// Add the playing widget to the fancy tab widget
ui_->tabs->AddBottomWidget(ui_->widget_playing);
ui_->tabs->SetBackgroundPixmap(QPixmap(u":/pictures/sidebar-background.png"_s));
ui_->tabs->LoadSettings(QLatin1String(kSettingsGroup));
ui_->tabs->LoadSettings(QLatin1String(MainWindowSettings::kSettingsGroup));
track_position_timer_->setInterval(kTrackPositionUpdateTimeMs);
QObject::connect(track_position_timer_, &QTimer::timeout, this, &MainWindow::UpdateTrackPosition);
@@ -423,14 +431,20 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
ui_->playlist->SetManager(app_->playlist_manager());
ui_->playlist->view()->Init(app_);
ui_->playlist->view()->Init(app_->player(),
app_->playlist_manager(),
app_->collection_backend(),
#ifdef HAVE_MOODBAR
app_->moodbar_loader(),
#endif
app_->current_albumcover_loader());
collection_view_->view()->setModel(app_->collection()->model()->filter());
collection_view_->view()->SetApplication(app_);
collection_view_->view()->Init(app->task_manager(), app->tagreader_client(), app->network(), app->albumcover_loader(), app->current_albumcover_loader(), app->cover_providers(), app->lyrics_providers(), app->collection(), app->device_manager(), app->streaming_services());
#ifndef Q_OS_WIN
device_view_->view()->SetApplication(app_);
device_view_->view()->Init(app->task_manager(), app->tagreader_client(), app->device_manager(), app->collection_model()->directory_model());
#endif
playlist_list_->SetApplication(app_);
playlist_list_->Init(app_->task_manager(), app->tagreader_client(), app_->playlist_manager(), app_->playlist_backend(), app_->device_manager());
organize_dialog_->SetDestinationModel(app_->collection()->model()->directory_model());
@@ -541,9 +555,9 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(ui_->action_equalizer, &QAction::triggered, this, &MainWindow::ShowEqualizer);
QObject::connect(ui_->action_transcoder, &QAction::triggered, this, &MainWindow::ShowTranscodeDialog);
QObject::connect(ui_->action_jump, &QAction::triggered, ui_->playlist->view(), &PlaylistView::JumpToCurrentlyPlayingTrack);
QObject::connect(ui_->action_update_collection, &QAction::triggered, &*app_->collection(), &SCollection::IncrementalScan);
QObject::connect(ui_->action_full_collection_scan, &QAction::triggered, &*app_->collection(), &SCollection::FullScan);
QObject::connect(ui_->action_stop_collection_scan, &QAction::triggered, &*app_->collection(), &SCollection::StopScan);
QObject::connect(ui_->action_update_collection, &QAction::triggered, &*app_->collection(), &CollectionLibrary::IncrementalScan);
QObject::connect(ui_->action_full_collection_scan, &QAction::triggered, &*app_->collection(), &CollectionLibrary::FullScan);
QObject::connect(ui_->action_stop_collection_scan, &QAction::triggered, &*app_->collection(), &CollectionLibrary::StopScan);
QObject::connect(ui_->action_add_files_to_transcoder, &QAction::triggered, this, &MainWindow::AddFilesToTranscoder);
ui_->action_add_files_to_transcoder->setIcon(IconLoader::Load(u"tools-wizard"_s));
@@ -597,6 +611,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(&*app_->player(), &Player::Stopped, ui_->playlist, &PlaylistContainer::ActiveStopped);
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, osd_, &OSDBase::SongChanged);
QObject::connect(&*app_->player(), &Player::Paused, osd_, &OSDBase::Paused);
QObject::connect(&*app_->player(), &Player::Resumed, osd_, &OSDBase::Resumed);
QObject::connect(&*app_->player(), &Player::Stopped, osd_, &OSDBase::Stopped);
@@ -605,6 +620,16 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(&*app_->player(), &Player::VolumeChanged, ui_->volume, &VolumeSlider::SetValue);
QObject::connect(&*app_->player(), &Player::ForceShowOSD, this, &MainWindow::ForceShowOSD);
QObject::connect(&*app_->current_albumcover_loader(), &CurrentAlbumCoverLoader::ThumbnailLoaded, osd_, &OSDBase::AlbumCoverLoaded);
QObject::connect(&*app_->player(), &Player::Paused, &*app_->playlist_manager(), &PlaylistManager::SetActivePaused);
QObject::connect(&*app_->player(), &Player::Playing, &*app_->playlist_manager(), &PlaylistManager::SetActivePlaying);
QObject::connect(&*app_->player(), &Player::Stopped, &*app_->playlist_manager(), &PlaylistManager::SetActiveStopped);
QObject::connect(&*app_->player(), &Player::Paused, playlist_list_, &PlaylistListContainer::ActivePaused);
QObject::connect(&*app_->player(), &Player::Playing, playlist_list_, &PlaylistListContainer::ActivePlaying);
QObject::connect(&*app_->player(), &Player::Stopped, playlist_list_, &PlaylistListContainer::ActiveStopped);
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::AllPlaylistsLoaded, &*app->player(), &Player::PlaylistsLoaded);
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, this, &MainWindow::SongChanged);
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, &*app_->player(), &Player::CurrentMetadataChanged);
@@ -628,9 +653,9 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(ui_->track_slider, &TrackSlider::Next, &*app_->player(), &Player::Next);
// Collection connections
QObject::connect(&*app_->collection(), &SCollection::Error, this, &MainWindow::ShowErrorDialog);
QObject::connect(&*app_->collection(), &CollectionLibrary::Error, this, &MainWindow::ShowErrorDialog);
QObject::connect(collection_view_->view(), &CollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(collection_view_->view(), &CollectionView::ShowConfigDialog, this, &MainWindow::ShowCollectionConfig);
QObject::connect(collection_view_->view(), &CollectionView::ShowSettingsDialog, this, &MainWindow::OpenCollectionSettingsDialog);
QObject::connect(collection_view_->view(), &CollectionView::Error, this, &MainWindow::ShowErrorDialog);
QObject::connect(app_->collection_model(), &CollectionModel::TotalSongCountUpdated, collection_view_->view(), &CollectionView::TotalSongCountUpdated);
QObject::connect(app_->collection_model(), &CollectionModel::TotalArtistCountUpdated, collection_view_->view(), &CollectionView::TotalArtistCountUpdated);
@@ -638,9 +663,10 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(app_->collection_model(), &CollectionModel::modelAboutToBeReset, collection_view_->view(), &CollectionView::SaveFocus);
QObject::connect(app_->collection_model(), &CollectionModel::modelReset, collection_view_->view(), &CollectionView::RestoreFocus);
QObject::connect(&*app_->task_manager(), &TaskManager::PauseCollectionWatchers, &*app_->collection(), &SCollection::PauseWatcher);
QObject::connect(&*app_->task_manager(), &TaskManager::ResumeCollectionWatchers, &*app_->collection(), &SCollection::ResumeWatcher);
QObject::connect(&*app_->task_manager(), &TaskManager::PauseCollectionWatchers, &*app_->collection(), &CollectionLibrary::PauseWatcher);
QObject::connect(&*app_->task_manager(), &TaskManager::ResumeCollectionWatchers, &*app_->collection(), &CollectionLibrary::ResumeWatcher);
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, &*app_->current_albumcover_loader(), &CurrentAlbumCoverLoader::LoadAlbumCover);
QObject::connect(&*app_->current_albumcover_loader(), &CurrentAlbumCoverLoader::AlbumCoverLoaded, this, &MainWindow::AlbumCoverLoaded);
QObject::connect(album_cover_choice_controller_, &AlbumCoverChoiceController::Error, this, &MainWindow::ShowErrorDialog);
QObject::connect(album_cover_choice_controller_->cover_from_file_action(), &QAction::triggered, this, &MainWindow::LoadCoverFromFile);
@@ -674,8 +700,8 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(collection_view_group, &QActionGroup::triggered, this, &MainWindow::ChangeCollectionFilterMode);
QAction *collection_config_action = new QAction(IconLoader::Load(u"configure"_s), tr("Configure collection..."), this);
QObject::connect(collection_config_action, &QAction::triggered, this, &MainWindow::ShowCollectionConfig);
collection_view_->filter_widget()->SetSettingsGroup(QLatin1String(CollectionSettingsPage::kSettingsGroup));
QObject::connect(collection_config_action, &QAction::triggered, this, &MainWindow::OpenCollectionSettingsDialog);
collection_view_->filter_widget()->SetSettingsGroup(QLatin1String(CollectionSettings::kSettingsGroup));
collection_view_->filter_widget()->Init(app_->collection()->model(), app_->collection()->model()->filter());
QAction *separator = new QAction(this);
@@ -688,13 +714,16 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
collection_view_->filter_widget()->AddMenuAction(collection_config_action);
#ifdef HAVE_SUBSONIC
QObject::connect(subsonic_view_, &StreamingSongsView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
QObject::connect(subsonic_view_->view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
#endif
#ifdef HAVE_TIDAL
QObject::connect(tidal_view_, &StreamingTabsView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
QObject::connect(tidal_view_->artists_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(tidal_view_->albums_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(tidal_view_->songs_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(tidal_view_->search_view(), &StreamingSearchView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
QObject::connect(tidal_view_->search_view(), &StreamingSearchView::AddToPlaylist, this, &MainWindow::AddToPlaylist);
if (TidalServicePtr tidalservice = app_->streaming_services()->Service<TidalService>()) {
QObject::connect(this, &MainWindow::AuthorizationUrlReceived, &*tidalservice, &TidalService::AuthorizationUrlReceived);
@@ -702,16 +731,20 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
#endif
#ifdef HAVE_QOBUZ
QObject::connect(qobuz_view_, &StreamingTabsView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
QObject::connect(qobuz_view_->artists_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(qobuz_view_->albums_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(qobuz_view_->songs_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(qobuz_view_->search_view(), &StreamingSearchView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
QObject::connect(qobuz_view_->search_view(), &StreamingSearchView::AddToPlaylist, this, &MainWindow::AddToPlaylist);
#endif
#ifdef HAVE_SPOTIFY
QObject::connect(spotify_view_, &StreamingTabsView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
QObject::connect(spotify_view_->artists_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(spotify_view_->albums_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(spotify_view_->songs_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(spotify_view_->search_view(), &StreamingSearchView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
QObject::connect(spotify_view_->search_view(), &StreamingSearchView::AddToPlaylist, this, &MainWindow::AddToPlaylist);
#endif
@@ -773,14 +806,15 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(ui_->playlist, &PlaylistContainer::UndoRedoActionsChanged, this, &MainWindow::PlaylistUndoRedoChanged);
#ifndef Q_OS_WIN
playlist_copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0);
QObject::connect(&*app_->device_manager(), &DeviceManager::DeviceError, this, &MainWindow::ShowErrorDialog);
#ifndef WIN32
QObject::connect(app_->device_manager()->connected_devices_model(), &DeviceStateFilterModel::IsEmptyChanged, playlist_copy_to_device_, &QAction::setDisabled);
playlist_copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0);
#endif
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettings::ScrobblingEnabledChanged, this, &MainWindow::ScrobblingEnabledChanged);
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettings::ScrobbleButtonVisibilityChanged, this, &MainWindow::ScrobbleButtonVisibilityChanged);
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettings::LoveButtonVisibilityChanged, this, &MainWindow::LoveButtonVisibilityChanged);
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettingsService::ScrobblingEnabledChanged, this, &MainWindow::ScrobblingEnabledChanged);
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettingsService::ScrobbleButtonVisibilityChanged, this, &MainWindow::ScrobbleButtonVisibilityChanged);
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettingsService::LoveButtonVisibilityChanged, this, &MainWindow::LoveButtonVisibilityChanged);
#ifdef Q_OS_MACOS
mac::SetApplicationHandler(this);
@@ -849,11 +883,14 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
ui_->status_bar_stack->setCurrentWidget(ui_->playlist_summary_page);
QObject::connect(ui_->multi_loading_indicator, &MultiLoadingIndicator::TaskCountChange, this, &MainWindow::TaskCountChanged);
ui_->track_slider->SetApplication(app);
ui_->track_slider->Init();
#ifdef HAVE_MOODBAR
// Moodbar connections
QObject::connect(&*app_->moodbar_controller(), &MoodbarController::CurrentMoodbarDataChanged, ui_->track_slider->moodbar_style(), &MoodbarProxyStyle::SetMoodbarData);
QObject::connect(&*app_->moodbar_controller(), &MoodbarController::CurrentMoodbarDataChanged, ui_->track_slider->moodbar_proxy_style(), &MoodbarProxyStyle::SetMoodbarData);
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, &*app_->moodbar_controller(), &MoodbarController::CurrentSongChanged);
QObject::connect(&*app_->player(), &Player::Stopped, &*app_->moodbar_controller(), &MoodbarController::PlaybackStopped);
QObject::connect(ui_->track_slider->moodbar_proxy_style(), &MoodbarProxyStyle::StyleChanged, &*app_->moodbar_loader(), &MoodbarLoader::StyleChanged);
#endif
// Playing widget
@@ -875,7 +912,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
css_loader->SetStyleSheet(this, u":/style/strawberry.css"_s);
// Load playlists
app_->playlist_manager()->Init(app_->collection_backend(), app_->playlist_backend(), ui_->playlist_sequence, ui_->playlist);
app_->playlist_manager()->Init(ui_->playlist_sequence, ui_->playlist);
queue_view_->SetPlaylistManager(app_->playlist_manager());
@@ -902,10 +939,12 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(&*app_->lastfm_import(), &LastFMImport::FinishedWithError, lastfm_import_dialog_, &LastFMImportDialog::FinishedWithError);
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdateTotal, lastfm_import_dialog_, &LastFMImportDialog::UpdateTotal);
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdateProgress, lastfm_import_dialog_, &LastFMImportDialog::UpdateProgress);
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdateLastPlayed, &*app_->collection_backend(), &CollectionBackend::UpdateLastPlayed);
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdatePlayCount, &*app_->collection_backend(), &CollectionBackend::UpdatePlayCount);
// Load settings
qLog(Debug) << "Loading settings";
settings_.beginGroup(kSettingsGroup);
settings_.beginGroup(MainWindowSettings::kSettingsGroup);
// Set last used geometry to position window on the correct monitor
// Set window state only if the window was last maximized
@@ -913,7 +952,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
restoreGeometry(settings_.value("geometry").toByteArray());
}
if (!settings_.contains("splitter_state") || !ui_->splitter->restoreState(settings_.value("splitter_state").toByteArray())) {
if (!settings_.contains(MainWindowSettings::kSplitterState) || !ui_->splitter->restoreState(settings_.value(MainWindowSettings::kSplitterState).toByteArray())) {
ui_->splitter->setSizes(QList<int>() << 20 << (width() - 20));
}
@@ -941,40 +980,40 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
#ifdef Q_OS_MACOS // Always show the mainwindow on startup for macOS
show();
#else
BehaviourSettingsPage::StartupBehaviour startupbehaviour = BehaviourSettingsPage::StartupBehaviour::Remember;
BehaviourSettings::StartupBehaviour startupbehaviour = BehaviourSettings::StartupBehaviour::Remember;
{
Settings s;
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
startupbehaviour = static_cast<BehaviourSettingsPage::StartupBehaviour>(s.value("startupbehaviour", static_cast<int>(BehaviourSettingsPage::StartupBehaviour::Remember)).toInt());
s.beginGroup(BehaviourSettings::kSettingsGroup);
startupbehaviour = static_cast<BehaviourSettings::StartupBehaviour>(s.value(BehaviourSettings::kStartupBehaviour, static_cast<int>(BehaviourSettings::StartupBehaviour::Remember)).toInt());
s.endGroup();
}
switch (startupbehaviour) {
case BehaviourSettingsPage::StartupBehaviour::Show:
case BehaviourSettings::StartupBehaviour::Show:
show();
break;
case BehaviourSettingsPage::StartupBehaviour::ShowMaximized:
case BehaviourSettings::StartupBehaviour::ShowMaximized:
setWindowState(windowState() | Qt::WindowMaximized);
show();
break;
case BehaviourSettingsPage::StartupBehaviour::ShowMinimized:
case BehaviourSettings::StartupBehaviour::ShowMinimized:
setWindowState(windowState() | Qt::WindowMinimized);
show();
break;
case BehaviourSettingsPage::StartupBehaviour::Hide:
case BehaviourSettings::StartupBehaviour::Hide:
if (tray_icon_->IsSystemTrayAvailable() && tray_icon_->isVisible()) {
break;
}
[[fallthrough]];
case BehaviourSettingsPage::StartupBehaviour::Remember:
case BehaviourSettings::StartupBehaviour::Remember:
default:{
was_maximized_ = settings_.value("maximized", true).toBool();
was_maximized_ = settings_.value(MainWindowSettings::kMaximized, true).toBool();
if (was_maximized_) setWindowState(windowState() | Qt::WindowMaximized);
was_minimized_ = settings_.value("minimized", false).toBool();
was_minimized_ = settings_.value(MainWindowSettings::kMinimized, false).toBool();
if (was_minimized_) setWindowState(windowState() | Qt::WindowMinimized);
if (!tray_icon_->IsSystemTrayAvailable() || !tray_icon_->isVisible() || !settings_.value("hidden", false).toBool()) {
if (!tray_icon_->IsSystemTrayAvailable() || !tray_icon_->isVisible() || !settings_.value(MainWindowSettings::kHidden, false).toBool()) {
show();
}
break;
@@ -982,7 +1021,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
}
#endif
bool show_sidebar = settings_.value("show_sidebar", true).toBool();
bool show_sidebar = settings_.value(MainWindowSettings::kShowSidebar, true).toBool();
ui_->sidebar_layout->setVisible(show_sidebar);
ui_->action_toggle_show_sidebar->setChecked(show_sidebar);
@@ -1016,7 +1055,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
#ifdef Q_OS_LINUX
if (!Utilities::GetEnv(u"SNAP"_s).isEmpty() && !Utilities::GetEnv(u"SNAP_NAME"_s).isEmpty()) {
Settings s;
s.beginGroup(kSettingsGroup);
s.beginGroup(MainWindowSettings::kSettingsGroup);
const bool ignore_snap = s.value("ignore_snap", false).toBool();
s.endGroup();
if (!ignore_snap) {
@@ -1030,12 +1069,12 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
#if defined(Q_OS_MACOS)
if (Utilities::ProcessTranslated()) {
Settings s;
s.beginGroup(kSettingsGroup);
s.beginGroup(MainWindowSettings::kSettingsGroup);
const bool ignore_rosetta = s.value("ignore_rosetta", false).toBool();
s.endGroup();
if (!ignore_rosetta) {
MessageDialog *rosetta_message = new MessageDialog(this);
rosetta_message->set_settings_group(QLatin1String(kSettingsGroup));
rosetta_message->set_settings_group(QLatin1String(MainWindowSettings::kSettingsGroup));
rosetta_message->set_do_not_show_message_again(u"ignore_rosetta"_s);
rosetta_message->setAttribute(Qt::WA_DeleteOnClose);
rosetta_message->ShowMessage(tr("Strawberry running under Rosetta"), tr("You are running Strawberry under Rosetta. Running Strawberry under Rosetta is unsupported and known to have issues. You should download Strawberry for the correct CPU architecture from %1").arg(QLatin1String("<a href=\"https://downloads.strawberrymusicplayer.org/\">downloads.strawberrymusicplayer.org</a>")), IconLoader::Load(u"dialog-warning"_s));
@@ -1052,14 +1091,13 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
s.endGroup();
#endif
if (asked_permission) {
s.beginGroup(kSettingsGroup);
constexpr char do_not_show_sponsor_message_key[] = "do_not_show_sponsor_message";
const bool do_not_show_sponsor_message = s.value(do_not_show_sponsor_message_key, false).toBool();
s.beginGroup(MainWindowSettings::kSettingsGroup);
const bool do_not_show_sponsor_message = s.value(MainWindowSettings::kDoNotShowSponsorMessage, false).toBool();
s.endGroup();
if (!do_not_show_sponsor_message) {
MessageDialog *sponsor_message = new MessageDialog(this);
sponsor_message->set_settings_group(QLatin1String(kSettingsGroup));
sponsor_message->set_do_not_show_message_again(QLatin1String(do_not_show_sponsor_message_key));
sponsor_message->set_settings_group(QLatin1String(MainWindowSettings::kSettingsGroup));
sponsor_message->set_do_not_show_message_again(QLatin1String(MainWindowSettings::kDoNotShowSponsorMessage));
sponsor_message->setAttribute(Qt::WA_DeleteOnClose);
sponsor_message->ShowMessage(tr("Sponsoring Strawberry"), tr("Strawberry is free and open source software. If you like Strawberry, please consider sponsoring the project. For more information about sponsorship see our website %1").arg(u"<a href= \"https://www.strawberrymusicplayer.org/\">www.strawberrymusicplayer.org</a>"_s), IconLoader::Load(u"dialog-information"_s));
}
@@ -1083,8 +1121,8 @@ void MainWindow::ReloadSettings() {
constexpr bool keeprunning_available = true;
#else
const bool systemtray_available = tray_icon_->IsSystemTrayAvailable();
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
const bool showtrayicon = s.value("showtrayicon", systemtray_available).toBool();
s.beginGroup(BehaviourSettings::kSettingsGroup);
const bool showtrayicon = s.value(BehaviourSettings::kShowTrayIcon, systemtray_available).toBool();
s.endGroup();
const bool keeprunning_available = systemtray_available && showtrayicon;
if (systemtray_available) {
@@ -1095,22 +1133,22 @@ void MainWindow::ReloadSettings() {
}
#endif
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
keep_running_ = keeprunning_available && s.value("keeprunning", false).toBool();
playing_widget_ = s.value("playing_widget", true).toBool();
bool trayicon_progress = s.value("trayicon_progress", false).toBool();
s.beginGroup(BehaviourSettings::kSettingsGroup);
keep_running_ = keeprunning_available && s.value(BehaviourSettings::kKeepRunning, false).toBool();
playing_widget_ = s.value(BehaviourSettings::kPlayingWidget, true).toBool();
bool trayicon_progress = s.value(BehaviourSettings::kTrayIconProgress, false).toBool();
#ifdef HAVE_DBUS
const bool taskbar_progress = s.value("taskbar_progress", true).toBool();
const bool taskbar_progress = s.value(BehaviourSettings::kTaskbarProgress, true).toBool();
#endif
if (playing_widget_ != ui_->widget_playing->IsEnabled()) TabSwitched();
doubleclick_addmode_ = static_cast<BehaviourSettingsPage::AddBehaviour>(s.value("doubleclick_addmode", static_cast<int>(BehaviourSettingsPage::AddBehaviour::Append)).toInt());
doubleclick_playmode_ = static_cast<BehaviourSettingsPage::PlayBehaviour>(s.value("doubleclick_playmode", static_cast<int>(BehaviourSettingsPage::PlayBehaviour::Never)).toInt());
doubleclick_playlist_addmode_ = static_cast<BehaviourSettingsPage::PlaylistAddBehaviour>(s.value("doubleclick_playlist_addmode", static_cast<int>(BehaviourSettingsPage::PlayBehaviour::Never)).toInt());
menu_playmode_ = static_cast<BehaviourSettingsPage::PlayBehaviour>(s.value("menu_playmode", static_cast<int>(BehaviourSettingsPage::PlayBehaviour::Never)).toInt());
doubleclick_addmode_ = static_cast<BehaviourSettings::AddBehaviour>(s.value(BehaviourSettings::kDoubleClickAddMode, static_cast<int>(BehaviourSettings::AddBehaviour::Append)).toInt());
doubleclick_playmode_ = static_cast<BehaviourSettings::PlayBehaviour>(s.value(BehaviourSettings::kDoubleClickPlayMode, static_cast<int>(BehaviourSettings::PlayBehaviour::Never)).toInt());
doubleclick_playlist_addmode_ = static_cast<BehaviourSettings::PlaylistAddBehaviour>(s.value(BehaviourSettings::kDoubleClickPlaylistAddMode, static_cast<int>(BehaviourSettings::PlayBehaviour::Never)).toInt());
menu_playmode_ = static_cast<BehaviourSettings::PlayBehaviour>(s.value(BehaviourSettings::kMenuPlayMode, static_cast<int>(BehaviourSettings::PlayBehaviour::Never)).toInt());
s.endGroup();
s.beginGroup(AppearanceSettingsPage::kSettingsGroup);
int iconsize = s.value(AppearanceSettingsPage::kIconSizePlayControlButtons, 32).toInt();
s.beginGroup(AppearanceSettings::kSettingsGroup);
int iconsize = s.value(AppearanceSettings::kIconSizePlayControlButtons, 32).toInt();
s.endGroup();
tray_icon_->SetTrayiconProgress(trayicon_progress);
@@ -1128,7 +1166,7 @@ void MainWindow::ReloadSettings() {
ui_->forward_button->setIconSize(QSize(iconsize, iconsize));
ui_->button_love->setIconSize(QSize(iconsize, iconsize));
s.beginGroup(BackendSettingsPage::kSettingsGroup);
s.beginGroup(BackendSettings::kSettingsGroup);
bool volume_control = s.value("volume_control", true).toBool();
s.endGroup();
if (volume_control != ui_->volume->isEnabled()) {
@@ -1143,17 +1181,17 @@ void MainWindow::ReloadSettings() {
}
}
s.beginGroup(PlaylistSettingsPage::kSettingsGroup);
delete_files_ = s.value("delete_files", false).toBool();
s.beginGroup(PlaylistSettings::kSettingsGroup);
delete_files_ = s.value(PlaylistSettings::kDeleteFiles, false).toBool();
s.endGroup();
osd_->ReloadSettings();
album_cover_choice_controller_->search_cover_auto_action()->setChecked(settings_.value("search_for_cover_auto", true).toBool());
album_cover_choice_controller_->search_cover_auto_action()->setChecked(settings_.value(MainWindowSettings::kSearchForCoverAuto, true).toBool());
#ifdef HAVE_SUBSONIC
s.beginGroup(SubsonicSettingsPage::kSettingsGroup);
bool enable_subsonic = s.value("enabled", false).toBool();
s.beginGroup(SubsonicSettings::kSettingsGroup);
bool enable_subsonic = s.value(SubsonicSettings::kEnabled, false).toBool();
s.endGroup();
if (enable_subsonic) {
ui_->tabs->EnableTab(subsonic_view_);
@@ -1164,8 +1202,8 @@ void MainWindow::ReloadSettings() {
#endif
#ifdef HAVE_TIDAL
s.beginGroup(TidalSettingsPage::kSettingsGroup);
bool enable_tidal = s.value("enabled", false).toBool();
s.beginGroup(TidalSettings::kSettingsGroup);
bool enable_tidal = s.value(TidalSettings::kEnabled, false).toBool();
s.endGroup();
if (enable_tidal) {
ui_->tabs->EnableTab(tidal_view_);
@@ -1176,8 +1214,8 @@ void MainWindow::ReloadSettings() {
#endif
#ifdef HAVE_SPOTIFY
s.beginGroup(SpotifySettingsPage::kSettingsGroup);
bool enable_spotify = s.value("enabled", false).toBool();
s.beginGroup(SpotifySettings::kSettingsGroup);
bool enable_spotify = s.value(SpotifySettings::kEnabled, false).toBool();
s.endGroup();
if (enable_spotify) {
ui_->tabs->EnableTab(spotify_view_);
@@ -1188,8 +1226,8 @@ void MainWindow::ReloadSettings() {
#endif
#ifdef HAVE_QOBUZ
s.beginGroup(QobuzSettingsPage::kSettingsGroup);
bool enable_qobuz = s.value("enabled", false).toBool();
s.beginGroup(QobuzSettings::kSettingsGroup);
bool enable_qobuz = s.value(QobuzSettings::kEnabled, false).toBool();
s.endGroup();
if (enable_qobuz) {
ui_->tabs->EnableTab(qobuz_view_);
@@ -1208,7 +1246,6 @@ void MainWindow::ReloadAllSettings() {
ReloadSettings();
// Other settings
app_->ReloadSettings();
app_->collection()->ReloadSettings();
app_->player()->ReloadSettings();
collection_view_->ReloadSettings();
@@ -1228,18 +1265,23 @@ void MainWindow::ReloadAllSettings() {
app_->lyrics_providers()->ReloadSettings();
#ifdef HAVE_MOODBAR
app_->moodbar_controller()->ReloadSettings();
app_->moodbar_loader()->ReloadSettings();
ui_->track_slider->moodbar_proxy_style()->ReloadSettings();
#endif
#ifdef HAVE_SUBSONIC
subsonic_view_->ReloadSettings();
#endif
#ifdef HAVE_TIDAL
tidal_view_->ReloadSettings();
tidal_view_->search_view()->ReloadSettings();
#endif
#ifdef HAVE_SPOTIFY
spotify_view_->ReloadSettings();
spotify_view_->search_view()->ReloadSettings();
#endif
#ifdef HAVE_QOBUZ
qobuz_view_->ReloadSettings();
qobuz_view_->search_view()->ReloadSettings();
#endif
}
@@ -1255,12 +1297,12 @@ void MainWindow::SaveSettings() {
SaveGeometry();
app_->player()->SaveVolume();
app_->player()->SavePlaybackStatus();
ui_->tabs->SaveSettings(QLatin1String(kSettingsGroup));
ui_->tabs->SaveSettings(QLatin1String(MainWindowSettings::kSettingsGroup));
ui_->playlist->view()->SaveSettings();
app_->scrobbler()->WriteCache();
settings_.setValue("show_sidebar", ui_->action_toggle_show_sidebar->isChecked());
settings_.setValue("search_for_cover_auto", album_cover_choice_controller_->search_cover_auto_action()->isChecked());
settings_.setValue(MainWindowSettings::kShowSidebar, ui_->action_toggle_show_sidebar->isChecked());
settings_.setValue(MainWindowSettings::kSearchForCoverAuto, album_cover_choice_controller_->search_cover_auto_action()->isChecked());
}
@@ -1500,23 +1542,23 @@ void MainWindow::ToggleSidebar(const bool checked) {
ui_->sidebar_layout->setVisible(checked);
TabSwitched();
settings_.setValue("show_sidebar", checked);
settings_.setValue(MainWindowSettings::kShowSidebar, checked);
}
void MainWindow::ToggleSearchCoverAuto(const bool checked) {
settings_.setValue("search_for_cover_auto", checked);
settings_.setValue(MainWindowSettings::kSearchForCoverAuto, checked);
}
void MainWindow::SaveGeometry() {
if (!initialized_) return;
settings_.setValue("maximized", isMaximized());
settings_.setValue("minimized", isMinimized());
settings_.setValue("hidden", isHidden());
settings_.setValue("geometry", saveGeometry());
settings_.setValue("splitter_state", ui_->splitter->saveState());
settings_.setValue(MainWindowSettings::kMaximized, isMaximized());
settings_.setValue(MainWindowSettings::kMinimized, isMinimized());
settings_.setValue(MainWindowSettings::kHidden, isHidden());
settings_.setValue(MainWindowSettings::kGeometry, saveGeometry());
settings_.setValue(MainWindowSettings::kSplitterState, ui_->splitter->saveState());
}
@@ -1546,12 +1588,12 @@ void MainWindow::PlaylistDoubleClick(const QModelIndex &idx) {
}
switch (doubleclick_playlist_addmode_) {
case BehaviourSettingsPage::PlaylistAddBehaviour::Play:
case BehaviourSettings::PlaylistAddBehaviour::Play:
app_->playlist_manager()->SetActiveToCurrent();
app_->player()->PlayAt(source_idx.row(), false, 0, EngineBase::TrackChangeType::Manual, Playlist::AutoScroll::Never, true, true);
break;
case BehaviourSettingsPage::PlaylistAddBehaviour::Enqueue:
case BehaviourSettings::PlaylistAddBehaviour::Enqueue:
app_->playlist_manager()->current()->queue()->ToggleTracks(QModelIndexList() << source_idx);
if (app_->player()->GetState() != EngineBase::State::Playing) {
app_->playlist_manager()->SetActiveToCurrent();
@@ -1724,42 +1766,42 @@ void MainWindow::UpdateTaskbarProgress(const bool visible, const double progress
}
#endif
void MainWindow::ApplyAddBehaviour(const BehaviourSettingsPage::AddBehaviour b, MimeData *mimedata) {
void MainWindow::ApplyAddBehaviour(const BehaviourSettings::AddBehaviour b, MimeData *mimedata) {
switch (b) {
case BehaviourSettingsPage::AddBehaviour::Append:
case BehaviourSettings::AddBehaviour::Append:
mimedata->clear_first_ = false;
mimedata->enqueue_now_ = false;
break;
case BehaviourSettingsPage::AddBehaviour::Enqueue:
case BehaviourSettings::AddBehaviour::Enqueue:
mimedata->clear_first_ = false;
mimedata->enqueue_now_ = true;
break;
case BehaviourSettingsPage::AddBehaviour::Load:
case BehaviourSettings::AddBehaviour::Load:
mimedata->clear_first_ = true;
mimedata->enqueue_now_ = false;
break;
case BehaviourSettingsPage::AddBehaviour::OpenInNew:
case BehaviourSettings::AddBehaviour::OpenInNew:
mimedata->open_in_new_playlist_ = true;
break;
}
}
void MainWindow::ApplyPlayBehaviour(const BehaviourSettingsPage::PlayBehaviour b, MimeData *mimedata) const {
void MainWindow::ApplyPlayBehaviour(const BehaviourSettings::PlayBehaviour b, MimeData *mimedata) const {
switch (b) {
case BehaviourSettingsPage::PlayBehaviour::Always:
case BehaviourSettings::PlayBehaviour::Always:
mimedata->play_now_ = true;
break;
case BehaviourSettingsPage::PlayBehaviour::Never:
case BehaviourSettings::PlayBehaviour::Never:
mimedata->play_now_ = false;
break;
case BehaviourSettingsPage::PlayBehaviour::IfStopped:
case BehaviourSettings::PlayBehaviour::IfStopped:
mimedata->play_now_ = !(app_->player()->GetState() == EngineBase::State::Playing);
break;
}
@@ -1814,7 +1856,7 @@ void MainWindow::AddToPlaylistFromAction(QAction *action) {
// Save the current playlist to reactivate it
const int current_id = app_->playlist_manager()->current_id();
// Get the name from selection
app_->playlist_manager()->New(app_->playlist_manager()->GetNameForNewPlaylist(songs));
app_->playlist_manager()->New(Song::GetNameForNewPlaylist(songs));
if (app_->playlist_manager()->current()->id() != current_id) {
// I'm sure the new playlist was created and is selected, so I can just insert items
app_->playlist_manager()->current()->InsertItems(items);
@@ -2161,7 +2203,7 @@ void MainWindow::RenumberTracks() {
Song song = item->OriginalMetadata();
if (song.IsEditable()) {
song.set_track(track);
TagReaderReplyPtr reply = TagReaderClient::Instance()->WriteFileAsync(song.url().toLocalFile(), song);
TagReaderReplyPtr reply = app_->tagreader_client()->WriteFileAsync(song.url().toLocalFile(), song);
QPersistentModelIndex persistent_index = QPersistentModelIndex(source_index);
QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, persistent_index]() { SongSaveComplete(reply, persistent_index); }, Qt::QueuedConnection);
}
@@ -2192,7 +2234,7 @@ void MainWindow::SelectionSetValue() {
Song song = item->OriginalMetadata();
if (!song.is_valid()) continue;
if (song.url().isLocalFile() && Playlist::set_column_value(song, column, column_value)) {
TagReaderReplyPtr reply = TagReaderClient::Instance()->WriteFileAsync(song.url().toLocalFile(), song);
TagReaderReplyPtr reply = app_->tagreader_client()->WriteFileAsync(song.url().toLocalFile(), song);
QPersistentModelIndex persistent_index = QPersistentModelIndex(source_index);
QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, persistent_index]() { SongSaveComplete(reply, persistent_index); }, Qt::QueuedConnection);
}
@@ -2228,10 +2270,10 @@ void MainWindow::AddFile() {
// Last used directory
QString directory = settings_.value("add_media_path", QDir::currentPath()).toString();
PlaylistParser parser(app_->collection_backend());
PlaylistParser parser(app_->tagreader_client(), app_->collection_backend());
// Show dialog
const QStringList filenames = QFileDialog::getOpenFileNames(this, tr("Add file"), directory, QStringLiteral("%1 (%2);;%3;;%4").arg(tr("Music"), QLatin1String(FileView::kFileFilter), parser.filters(PlaylistParser::Type::Load), tr(kAllFilesFilterSpec)));
const QStringList filenames = QFileDialog::getOpenFileNames(this, tr("Add file"), directory, QStringLiteral("%1 (%2);;%3;;%4").arg(tr("Music"), QLatin1String(kFileFilter), parser.filters(PlaylistParser::Type::Load), tr(kAllFilesFilterSpec)));
if (filenames.isEmpty()) return;
@@ -2470,7 +2512,7 @@ void MainWindow::CommandlineOptionsReceived(const CommandlineOptions &options) {
break;
case CommandlineOptions::UrlListAction::CreateNew:
mimedata->name_for_new_playlist_ = options.playlist_name();
ApplyAddBehaviour(BehaviourSettingsPage::AddBehaviour::OpenInNew, mimedata);
ApplyAddBehaviour(BehaviourSettings::AddBehaviour::OpenInNew, mimedata);
break;
}
@@ -2563,10 +2605,34 @@ void MainWindow::AddFilesToTranscoder() {
}
void MainWindow::ShowCollectionConfig() {
void MainWindow::OpenCollectionSettingsDialog() {
settings_dialog_->OpenAtPage(SettingsDialog::Page::Collection);
}
void MainWindow::OpenServiceSettingsDialog(const Song::Source source) {
switch (source) {
case Song::Source::Collection:
settings_dialog_->OpenAtPage(SettingsDialog::Page::Collection);
break;
case Song::Source::Subsonic:
settings_dialog_->OpenAtPage(SettingsDialog::Page::Subsonic);
break;
case Song::Source::Tidal:
settings_dialog_->OpenAtPage(SettingsDialog::Page::Tidal);
break;
case Song::Source::Qobuz:
settings_dialog_->OpenAtPage(SettingsDialog::Page::Qobuz);
break;
case Song::Source::Spotify:
settings_dialog_->OpenAtPage(SettingsDialog::Page::Spotify);
break;
default:
break;
}
}
void MainWindow::TaskCountChanged(const int count) {
if (count == 0) {
@@ -2810,10 +2876,18 @@ void MainWindow::ShowEqualizer() {
SettingsDialog *MainWindow::CreateSettingsDialog() {
SettingsDialog *settings_dialog = new SettingsDialog(app_, osd_, this);
SettingsDialog *settings_dialog = new SettingsDialog(app_->player(),
app_->device_finders(),
app_->collection(),
app_->cover_providers(),
app_->lyrics_providers(),
app_->scrobbler(),
app_->streaming_services(),
#ifdef HAVE_GLOBALSHORTCUTS
settings_dialog->SetGlobalShortcutManager(globalshortcuts_manager_);
globalshortcuts_manager_,
#endif
osd_,
this);
// Settings
QObject::connect(settings_dialog, &SettingsDialog::ReloadSettings, this, &MainWindow::ReloadAllSettings);
@@ -2838,7 +2912,7 @@ void MainWindow::OpenSettingsDialogAtPage(SettingsDialog::Page page) {
EditTagDialog *MainWindow::CreateEditTagDialog() {
EditTagDialog *edit_tag_dialog = new EditTagDialog(app_);
EditTagDialog *edit_tag_dialog = new EditTagDialog(app_->network(), app_->tagreader_client(), app_->collection_backend(), app_->albumcover_loader(), app_->current_albumcover_loader(), app_->cover_providers(), app_->lyrics_providers(), app_->streaming_services());
QObject::connect(edit_tag_dialog, &EditTagDialog::accepted, this, &MainWindow::EditTagDialogAccepted);
QObject::connect(edit_tag_dialog, &EditTagDialog::Error, this, &MainWindow::ShowErrorDialog);
return edit_tag_dialog;
@@ -2938,7 +3012,7 @@ void MainWindow::AutoCompleteTags() {
// Create the tag fetching stuff if it hasn't been already
if (!tag_fetcher_) {
tag_fetcher_ = make_unique<TagFetcher>(app_->network());
track_selection_dialog_ = make_unique<TrackSelectionDialog>();
track_selection_dialog_ = make_unique<TrackSelectionDialog>(app_->tagreader_client());
track_selection_dialog_->set_save_on_close(true);
QObject::connect(&*tag_fetcher_, &TagFetcher::ResultAvailable, &*track_selection_dialog_, &TrackSelectionDialog::FetchTagFinished, Qt::QueuedConnection);
@@ -2986,7 +3060,7 @@ void MainWindow::AutoCompleteTagsAccepted() {
}
void MainWindow::HandleNotificationPreview(const OSDBase::Behaviour type, const QString &line1, const QString &line2) {
void MainWindow::HandleNotificationPreview(const OSDSettings::Type type, const QString &line1, const QString &line2) {
if (!app_->playlist_manager()->current()->GetAllSongs().isEmpty()) {
// Show a preview notification for the first song in the current playlist

View File

@@ -46,19 +46,19 @@
#include <QSettings>
#include <QtEvents>
#include "scoped_ptr.h"
#include "shared_ptr.h"
#include "lazy.h"
#include "platforminterface.h"
#include "song.h"
#include "settings.h"
#include "includes/scoped_ptr.h"
#include "includes/shared_ptr.h"
#include "includes/lazy.h"
#include "core/platforminterface.h"
#include "core/song.h"
#include "core/settings.h"
#include "tagreader/tagreaderclient.h"
#include "engine/enginebase.h"
#include "osd/osdbase.h"
#include "playlist/playlist.h"
#include "playlist/playlistitem.h"
#include "settings/settingsdialog.h"
#include "settings/behavioursettingspage.h"
#include "constants/behavioursettings.h"
#include "covermanager/albumcoverloaderresult.h"
#include "covermanager/albumcoverimageresult.h"
@@ -107,9 +107,6 @@ class MainWindow : public QMainWindow, public PlatformInterface {
explicit MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OSDBase *osd, const CommandlineOptions &options, QWidget *parent = nullptr);
~MainWindow() override;
static const char *kSettingsGroup;
static const char *kAllFilesFilterSpec;
void SetHiddenInTray(const bool hidden);
void CommandlineOptionsReceived(const CommandlineOptions &options);
@@ -202,7 +199,9 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void TaskCountChanged(const int count);
void ShowCollectionConfig();
void OpenCollectionSettingsDialog();
void OpenServiceSettingsDialog(const Song::Source source);
void ReloadSettings();
void ReloadAllSettings();
void RefreshStyleSheet();
@@ -237,7 +236,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void Exit();
void DoExit();
void HandleNotificationPreview(const OSDBase::Behaviour type, const QString &line1, const QString &line2);
void HandleNotificationPreview(const OSDSettings::Type type, const QString &line1, const QString &line2);
void ShowConsole();
@@ -274,8 +273,8 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void SaveSettings();
static void ApplyAddBehaviour(const BehaviourSettingsPage::AddBehaviour b, MimeData *mimedata);
void ApplyPlayBehaviour(const BehaviourSettingsPage::PlayBehaviour b, MimeData *mimedata) const;
static void ApplyAddBehaviour(const BehaviourSettings::AddBehaviour b, MimeData *mimedata);
void ApplyPlayBehaviour(const BehaviourSettings::PlayBehaviour b, MimeData *mimedata) const;
void CheckFullRescanRevisions();
@@ -385,10 +384,10 @@ class MainWindow : public QMainWindow, public PlatformInterface {
#ifdef HAVE_DBUS
bool taskbar_progress_;
#endif
BehaviourSettingsPage::AddBehaviour doubleclick_addmode_;
BehaviourSettingsPage::PlayBehaviour doubleclick_playmode_;
BehaviourSettingsPage::PlaylistAddBehaviour doubleclick_playlist_addmode_;
BehaviourSettingsPage::PlayBehaviour menu_playmode_;
BehaviourSettings::AddBehaviour doubleclick_addmode_;
BehaviourSettings::PlayBehaviour doubleclick_playmode_;
BehaviourSettings::PlaylistAddBehaviour doubleclick_playlist_addmode_;
BehaviourSettings::PlayBehaviour menu_playmode_;
bool initialized_;
bool was_maximized_;

View File

@@ -1,7 +1,6 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2011, David Sansome <me@davidsansome.com>
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,12 +17,14 @@
*
*/
#ifndef QT_BLURIMAGE_H
#define QT_BLURIMAGE_H
#include "memorydatabase.h"
#include "config.h"
using namespace Qt::Literals::StringLiterals;
// Exported by QtGui
void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0);
MemoryDatabase::MemoryDatabase(SharedPtr<TaskManager> task_manager, QObject *parent)
: Database(task_manager, parent, u":memory:"_s) {}
#endif // QT_BLURIMAGE_H
MemoryDatabase::~MemoryDatabase() {
// Make sure Qt doesn't reuse the same database
Close();
}

37
src/core/memorydatabase.h Normal file
View File

@@ -0,0 +1,37 @@
/*
* Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MEMORYDATABASE_H
#define MEMORYDATABASE_H
#include "includes/shared_ptr.h"
class TaskManager;
#include "database.h"
class MemoryDatabase : public Database {
Q_OBJECT
public:
explicit MemoryDatabase(SharedPtr<TaskManager> task_manager, QObject *parent = nullptr);
~MemoryDatabase() override;
};
#endif // MEMORYDATABASE_H

View File

@@ -35,7 +35,7 @@
#include <QString>
#include <QStringList>
#include "core/scoped_ptr.h"
#include "includes/scoped_ptr.h"
class QMimeData;

View File

@@ -43,10 +43,9 @@
# include <QDBusArgument>
#endif
#include "song.h"
#include "core/song.h"
#include "core/enginemetadata.h"
#include "engine/enginebase.h"
#include "engine/enginemetadata.h"
#include "engine/gstenginepipeline.h"
#include "collection/collectiondirectory.h"
#include "playlist/playlistitem.h"
@@ -57,7 +56,7 @@
#include "equalizer/equalizer.h"
#ifdef HAVE_DBUS
# include "dbus_metatypes.h"
# include "includes/dbus_metatypes.h"
#endif
#ifdef HAVE_MPRIS2
@@ -74,7 +73,7 @@
# include "device/mtpconnection.h"
#endif
#include "settings/playlistsettingspage.h"
#include "constants/playlistsettings.h"
#include "smartplaylists/smartplaylistsearchterm.h"
#include "smartplaylists/smartplaylistsitem.h"
@@ -153,7 +152,7 @@ void RegisterMetaTypes() {
qRegisterMetaType<MtpConnection*>("MtpConnection*");
#endif
qRegisterMetaType<PlaylistSettingsPage::PathType>("PlaylistSettingsPage::PathType");
qRegisterMetaType<PlaylistSettings::PathType>("PlaylistSettings::PathType");
qRegisterMetaType<PlaylistGeneratorPtr>("PlaylistGeneratorPtr");
qRegisterMetaType<SmartPlaylistSearchTerm::Field>("SmartPlaylistSearchTerm::Field");

View File

@@ -35,7 +35,7 @@
#include <QList>
#include <QImage>
#include "shared_ptr.h"
#include "includes/shared_ptr.h"
#include "song.h"
class MusicStorage {

View File

@@ -1,65 +0,0 @@
/*
* Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MUTEX_PROTECTED_H
#define MUTEX_PROTECTED_H
#include <boost/noncopyable.hpp>
#include <QMutex>
#include <QMutexLocker>
template<typename T>
class mutex_protected : public boost::noncopyable {
public:
mutex_protected(const mutex_protected &value) : value_(value.value()) {}
mutex_protected(const T value) : value_(value) {}
~mutex_protected() {}
T value() const {
QMutexLocker l(&mutex_);
return value_;
}
T operator==(const mutex_protected &value) const {
QMutexLocker l(&mutex_);
return value == value_;
}
T operator==(const T value) const {
QMutexLocker l(&mutex_);
return value == value_;
}
void operator=(const mutex_protected &value) {
QMutexLocker l(&mutex_);
value_ = value.value();
}
void operator=(const T value) {
QMutexLocker l(&mutex_);
value_ = value;
}
private:
T value_;
mutable QMutex mutex_;
};
#endif // MUTEX_PROTECTED_H

View File

@@ -1,6 +1,6 @@
/*
* Strawberry Music Player
* Copyright 2023, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,12 +17,10 @@
*
*/
#ifndef SHARED_PTR_H
#define SHARED_PTR_H
#include <QObject>
#include <QString>
#include <memory>
#include "platforminterface.h"
template<typename T>
using SharedPtr = std::shared_ptr<T>;
#endif // SHARED_PTR_H
PlatformInterface::PlatformInterface() = default;
PlatformInterface::~PlatformInterface() = default;

View File

@@ -1,7 +1,6 @@
/*
* Strawberry Music Player
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,8 +25,8 @@
class PlatformInterface {
public:
PlatformInterface() = default;
virtual ~PlatformInterface() {}
explicit PlatformInterface();
virtual ~PlatformInterface();
// Called when the application should show itself.
virtual void Activate() = 0;

View File

@@ -37,18 +37,21 @@
#include <QTimer>
#include <QSettings>
#include "constants/behavioursettings.h"
#include "constants/backendsettings.h"
#include "constants/playlistsettings.h"
#include "constants/timeconstants.h"
#include "includes/scoped_ptr.h"
#include "includes/shared_ptr.h"
#include "core/logging.h"
#include "core/settings.h"
#include "utilities/timeconstants.h"
#include "scoped_ptr.h"
#include "shared_ptr.h"
#include "song.h"
#include "urlhandler.h"
#include "application.h"
#include "core/song.h"
#include "core/urlhandlers.h"
#include "core/urlhandler.h"
#include "core/enginemetadata.h"
#include "engine/enginebase.h"
#include "engine/enginemetadata.h"
#include "engine/gstengine.h"
#include "engine/gststartup.h"
@@ -60,18 +63,22 @@
#include "playlist/playlistsequence.h"
#include "equalizer/equalizer.h"
#include "analyzer/analyzercontainer.h"
#include "settings/backendsettingspage.h"
#include "settings/behavioursettingspage.h"
#include "settings/playlistsettingspage.h"
using namespace std::chrono_literals;
using std::make_shared;
const char *Player::kSettingsGroup = "Player";
namespace {
constexpr char kSettingsGroup[] = "Player";
constexpr char kVolume[] = "volume";
constexpr char kPlaybackState[] = "playback_state";
constexpr char kPlaybackPlaylist[] = "playback_playlist";
constexpr char kPlaybackPosition[] = "playback_position";
} // namespace
Player::Player(Application *app, QObject *parent)
Player::Player(const SharedPtr<TaskManager> task_manager, const SharedPtr<UrlHandlers> url_handlers, const SharedPtr<PlaylistManager> playlist_manager, QObject *parent)
: PlayerInterface(parent),
app_(app),
task_manager_(task_manager),
url_handlers_(url_handlers),
playlist_manager_(playlist_manager),
engine_(nullptr),
gst_startup_(new GstStartup(this)),
analyzer_(nullptr),
@@ -89,7 +96,7 @@ Player::Player(Application *app, QObject *parent)
last_pressed_previous_(QDateTime::currentDateTime()),
continue_on_error_(false),
greyout_(true),
menu_previousmode_(BehaviourSettingsPage::PreviousBehaviour::DontRestart),
menu_previousmode_(BehaviourSettings::PreviousBehaviour::DontRestart),
seek_step_sec_(10),
volume_increment_(5),
play_offset_nanosec_(0) {
@@ -97,8 +104,8 @@ Player::Player(Application *app, QObject *parent)
setObjectName(QLatin1String(metaObject()->className()));
Settings s;
s.beginGroup(BackendSettingsPage::kSettingsGroup);
EngineBase::Type enginetype = EngineBase::TypeFromName(s.value("engine", EngineBase::Name(EngineBase::Type::GStreamer)).toString().toLower());
s.beginGroup(BackendSettings::kSettingsGroup);
EngineBase::Type enginetype = EngineBase::TypeFromName(s.value(BackendSettings::kEngine, EngineBase::Name(EngineBase::Type::GStreamer)).toString().toLower());
s.endGroup();
CreateEngine(enginetype);
@@ -107,6 +114,8 @@ Player::Player(Application *app, QObject *parent)
timer_save_volume_->setInterval(5s);
QObject::connect(timer_save_volume_, &QTimer::timeout, this, &Player::SaveVolume);
QObject::connect(&*url_handlers, &UrlHandlers::Registered, this, &Player::UrlHandlerRegistered);
}
EngineBase::Type Player::CreateEngine(EngineBase::Type enginetype) {
@@ -118,7 +127,7 @@ EngineBase::Type Player::CreateEngine(EngineBase::Type enginetype) {
case EngineBase::Type::None:
case EngineBase::Type::GStreamer:{
use_enginetype=EngineBase::Type::GStreamer;
ScopedPtr<GstEngine> gst_engine(new GstEngine(app_->task_manager()));
ScopedPtr<GstEngine> gst_engine(new GstEngine(task_manager_));
gst_engine->SetStartup(gst_startup_);
engine_.reset(gst_engine.release());
break;
@@ -134,10 +143,10 @@ EngineBase::Type Player::CreateEngine(EngineBase::Type enginetype) {
if (use_enginetype != enginetype) { // Engine was set to something else. Reset output and device.
Settings s;
s.beginGroup(BackendSettingsPage::kSettingsGroup);
s.setValue("engine", EngineBase::Name(use_enginetype));
s.setValue("output", engine_->DefaultOutput());
s.setValue("device", QVariant());
s.beginGroup(BackendSettings::kSettingsGroup);
s.setValue(BackendSettings::kEngine, EngineBase::Name(use_enginetype));
s.setValue(BackendSettings::kOutput, engine_->DefaultOutput());
s.setValue(BackendSettings::kDevice, QVariant());
s.endGroup();
}
@@ -156,7 +165,7 @@ void Player::Init() {
Settings s;
if (!engine_) {
s.beginGroup(BackendSettingsPage::kSettingsGroup);
s.beginGroup(BackendSettings::kSettingsGroup);
EngineBase::Type enginetype = EngineBase::TypeFromName(s.value("engine", EngineBase::Name(EngineBase::Type::GStreamer)).toString().toLower());
s.endGroup();
CreateEngine(enginetype);
@@ -179,10 +188,10 @@ void Player::Init() {
QObject::connect(&*engine_, &EngineBase::VolumeChanged, this, &Player::SetVolumeFromEngine);
// Equalizer
QObject::connect(&*equalizer_, &Equalizer::StereoBalancerEnabledChanged, &*app_->player()->engine(), &EngineBase::SetStereoBalancerEnabled);
QObject::connect(&*equalizer_, &Equalizer::StereoBalanceChanged, &*app_->player()->engine(), &EngineBase::SetStereoBalance);
QObject::connect(&*equalizer_, &Equalizer::EqualizerEnabledChanged, &*app_->player()->engine(), &EngineBase::SetEqualizerEnabled);
QObject::connect(&*equalizer_, &Equalizer::EqualizerParametersChanged, &*app_->player()->engine(), &EngineBase::SetEqualizerParameters);
QObject::connect(&*equalizer_, &Equalizer::StereoBalancerEnabledChanged, &*engine_, &EngineBase::SetStereoBalancerEnabled);
QObject::connect(&*equalizer_, &Equalizer::StereoBalanceChanged, &*engine_, &EngineBase::SetStereoBalance);
QObject::connect(&*equalizer_, &Equalizer::EqualizerEnabledChanged, &*engine_, &EngineBase::SetEqualizerEnabled);
QObject::connect(&*equalizer_, &Equalizer::EqualizerParametersChanged, &*engine_, &EngineBase::SetEqualizerParameters);
engine_->SetStereoBalancerEnabled(equalizer_->is_stereo_balancer_enabled());
engine_->SetStereoBalance(equalizer_->stereo_balance());
@@ -199,26 +208,32 @@ void Player::ReloadSettings() {
Settings s;
s.beginGroup(PlaylistSettingsPage::kSettingsGroup);
s.beginGroup(PlaylistSettings::kSettingsGroup);
continue_on_error_ = s.value("continue_on_error", false).toBool();
greyout_ = s.value("greyout_songs_play", true).toBool();
s.endGroup();
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
menu_previousmode_ = static_cast<BehaviourSettingsPage::PreviousBehaviour>(s.value("menu_previousmode", static_cast<int>(BehaviourSettingsPage::PreviousBehaviour::DontRestart)).toInt());
seek_step_sec_ = s.value("seek_step_sec", 10).toInt();
volume_increment_ = s.value("volume_increment", 5).toUInt();
s.beginGroup(BehaviourSettings::kSettingsGroup);
menu_previousmode_ = static_cast<BehaviourSettings::PreviousBehaviour>(s.value(BehaviourSettings::kMenuPreviousMode, static_cast<int>(BehaviourSettings::PreviousBehaviour::DontRestart)).toInt());
seek_step_sec_ = s.value(BehaviourSettings::kSeekStepSec, 10).toInt();
volume_increment_ = s.value(BehaviourSettings::kVolumeIncrement, 5).toUInt();
s.endGroup();
engine_->ReloadSettings();
}
void Player::UrlHandlerRegistered(UrlHandler *url_handler) const {
QObject::connect(url_handler, &UrlHandler::AsyncLoadComplete, this, &Player::HandleLoadResult);
}
void Player::LoadVolume() {
Settings s;
s.beginGroup(kSettingsGroup);
const uint volume = s.value("volume", 100).toInt();
const uint volume = s.value(kVolume, 100).toInt();
s.endGroup();
SetVolume(volume);
@@ -229,7 +244,7 @@ void Player::SaveVolume() {
Settings s;
s.beginGroup(kSettingsGroup);
s.setValue("volume", volume_);
s.setValue(kVolume, volume_);
s.endGroup();
}
@@ -239,14 +254,14 @@ void Player::SavePlaybackStatus() {
Settings s;
s.beginGroup(kSettingsGroup);
s.setValue("playback_state", static_cast<int>(app_->player()->GetState()));
if (app_->player()->GetState() == EngineBase::State::Playing || app_->player()->GetState() == EngineBase::State::Paused) {
s.setValue("playback_playlist", app_->playlist_manager()->active()->id());
s.setValue("playback_position", app_->player()->engine()->position_nanosec() / kNsecPerSec);
s.setValue(kPlaybackState, static_cast<int>(GetState()));
if (GetState() == EngineBase::State::Playing || GetState() == EngineBase::State::Paused) {
s.setValue(kPlaybackPlaylist, playlist_manager_->active()->id());
s.setValue(kPlaybackPosition, engine_->position_nanosec() / kNsecPerSec);
}
else {
s.setValue("playback_playlist", -1);
s.setValue("playback_position", 0);
s.setValue(kPlaybackPlaylist, -1);
s.setValue(kPlaybackPosition, 0);
}
s.endGroup();
@@ -258,12 +273,12 @@ void Player::PlaylistsLoaded() {
Settings s;
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
s.beginGroup(BehaviourSettings::kSettingsGroup);
const bool resume_playback = s.value("resumeplayback", false).toBool();
s.endGroup();
s.beginGroup(Player::kSettingsGroup);
const EngineBase::State playback_state = static_cast<EngineBase::State>(s.value("playback_state", static_cast<int>(EngineBase::State::Empty)).toInt());
s.beginGroup(kSettingsGroup);
const EngineBase::State playback_state = static_cast<EngineBase::State>(s.value(kPlaybackState, static_cast<int>(EngineBase::State::Empty)).toInt());
s.endGroup();
if (resume_playback && (playback_state == EngineBase::State::Playing || playback_state == EngineBase::State::Paused)) {
@@ -283,14 +298,14 @@ void Player::ResumePlayback() {
Settings s;
s.beginGroup(kSettingsGroup);
const EngineBase::State playback_state = static_cast<EngineBase::State>(s.value("playback_state", static_cast<int>(EngineBase::State::Empty)).toInt());
const int playback_playlist = s.value("playback_playlist", -1).toInt();
const int playback_position = s.value("playback_position", 0).toInt();
const EngineBase::State playback_state = static_cast<EngineBase::State>(s.value(kPlaybackState, static_cast<int>(EngineBase::State::Empty)).toInt());
const int playback_playlist = s.value(kPlaybackPlaylist, -1).toInt();
const int playback_position = s.value(kPlaybackPosition, 0).toInt();
s.endGroup();
if (playback_playlist == app_->playlist_manager()->current()->id()) {
if (playback_playlist == playlist_manager_->current()->id()) {
// Set active to current to resume playback on correct playlist.
app_->playlist_manager()->SetActiveToCurrent();
playlist_manager_->SetActiveToCurrent();
if (playback_state == EngineBase::State::Playing) {
Play(playback_position * kNsecPerSec);
}
@@ -301,9 +316,9 @@ void Player::ResumePlayback() {
// Reset saved playback status so we don't resume again from the same position.
s.beginGroup(kSettingsGroup);
s.setValue("playback_state", static_cast<int>(EngineBase::State::Empty));
s.setValue("playback_playlist", -1);
s.setValue("playback_position", 0);
s.setValue(kPlaybackState, static_cast<int>(EngineBase::State::Empty));
s.setValue(kPlaybackPlaylist, -1);
s.setValue(kPlaybackPosition, 0);
s.endGroup();
}
@@ -315,19 +330,19 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
}
// Might've been an async load, so check we're still on the same item
const int current_row = app_->playlist_manager()->active()->current_row();
const int current_row = playlist_manager_->active()->current_row();
if (current_row == -1) {
return;
}
PlaylistItemPtr current_item = app_->playlist_manager()->active()->current_item();
PlaylistItemPtr current_item = playlist_manager_->active()->current_item();
if (!current_item) {
return;
}
int next_row = app_->playlist_manager()->active()->next_row();
int next_row = playlist_manager_->active()->next_row();
const bool has_next_row = next_row != -1;
PlaylistItemPtr next_item;
if (has_next_row) {
next_item = app_->playlist_manager()->active()->item_at(next_row);
next_item = playlist_manager_->active()->item_at(next_row);
}
bool is_current = false;
@@ -408,10 +423,10 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
if (update) {
if (is_current) {
app_->playlist_manager()->active()->UpdateItemMetadata(current_row, current_item, song, true);
playlist_manager_->active()->UpdateItemMetadata(current_row, current_item, song, true);
}
else if (is_next) {
app_->playlist_manager()->active()->UpdateItemMetadata(next_row, next_item, song, true);
playlist_manager_->active()->UpdateItemMetadata(next_row, next_item, song, true);
}
}
@@ -457,13 +472,13 @@ void Player::NextItem(const EngineBase::TrackChangeFlags change, const Playlist:
pause_time_ = QDateTime();
play_offset_nanosec_ = 0;
Playlist *active_playlist = app_->playlist_manager()->active();
Playlist *active_playlist = playlist_manager_->active();
// If we received too many errors in auto change, with repeat enabled, we stop
if (change & EngineBase::TrackChangeType::Auto) {
const PlaylistSequence::RepeatMode repeat_mode = active_playlist->RepeatMode();
if (repeat_mode != PlaylistSequence::RepeatMode::Off) {
if ((repeat_mode == PlaylistSequence::RepeatMode::Track && nb_errors_received_ >= 3) || (nb_errors_received_ >= app_->playlist_manager()->active()->filter()->rowCount())) {
if ((repeat_mode == PlaylistSequence::RepeatMode::Track && nb_errors_received_ >= 3) || (nb_errors_received_ >= playlist_manager_->active()->filter()->rowCount())) {
// We received too many "Error" state changes: probably looping over a playlist which contains only unavailable elements: stop now.
nb_errors_received_ = 0;
Stop();
@@ -482,8 +497,8 @@ void Player::NextItem(const EngineBase::TrackChangeFlags change, const Playlist:
int i = active_playlist->next_row(ignore_repeat_track);
if (i == -1) {
app_->playlist_manager()->active()->set_current_row(i);
app_->playlist_manager()->active()->reset_last_played();
playlist_manager_->active()->set_current_row(i);
playlist_manager_->active()->reset_last_played();
Q_EMIT PlaylistFinished();
Stop();
return;
@@ -503,9 +518,9 @@ void Player::PlayPlaylistInternal(const EngineBase::TrackChangeFlags change, con
play_offset_nanosec_ = 0;
Playlist *playlist = nullptr;
const QList<Playlist*> playlists = app_->playlist_manager()->GetAllPlaylists();
const QList<Playlist*> playlists = playlist_manager_->GetAllPlaylists();
for (Playlist *p : playlists) {
if (playlist_name == app_->playlist_manager()->GetPlaylistName(p->id())) {
if (playlist_name == playlist_manager_->GetPlaylistName(p->id())) {
playlist = p;
break;
}
@@ -516,12 +531,12 @@ void Player::PlayPlaylistInternal(const EngineBase::TrackChangeFlags change, con
return;
}
app_->playlist_manager()->SetActivePlaylist(playlist->id());
app_->playlist_manager()->SetCurrentPlaylist(playlist->id());
playlist_manager_->SetActivePlaylist(playlist->id());
playlist_manager_->SetCurrentPlaylist(playlist->id());
if (playlist->rowCount() == 0) return;
int i = app_->playlist_manager()->active()->current_row();
if (i == -1) i = app_->playlist_manager()->active()->last_played_row();
int i = playlist_manager_->active()->current_row();
if (i == -1) i = playlist_manager_->active()->last_played_row();
if (i == -1) i = 0;
PlayAt(i, false, 0, change, autoscroll, true);
@@ -530,14 +545,14 @@ void Player::PlayPlaylistInternal(const EngineBase::TrackChangeFlags change, con
bool Player::HandleStopAfter(const Playlist::AutoScroll autoscroll) {
if (app_->playlist_manager()->active()->stop_after_current()) {
if (playlist_manager_->active()->stop_after_current()) {
// Find what the next track would've been, and mark that one as current, so it plays next time the user presses Play.
const int next_row = app_->playlist_manager()->active()->next_row();
const int next_row = playlist_manager_->active()->next_row();
if (next_row != -1) {
app_->playlist_manager()->active()->set_current_row(next_row, autoscroll, true);
playlist_manager_->active()->set_current_row(next_row, autoscroll, true);
}
app_->playlist_manager()->active()->StopAfter(-1);
playlist_manager_->active()->StopAfter(-1);
Stop(true);
return true;
@@ -550,7 +565,7 @@ bool Player::HandleStopAfter(const Playlist::AutoScroll autoscroll) {
void Player::TrackEnded() {
if (current_item_ && current_item_->IsLocalCollectionItem() && current_item_->Metadata().id() != -1) {
app_->playlist_manager()->collection_backend()->IncrementPlayCountAsync(current_item_->Metadata().id());
playlist_manager_->collection_backend()->IncrementPlayCountAsync(current_item_->Metadata().id());
}
if (HandleStopAfter(Playlist::AutoScroll::Maybe)) return;
@@ -584,10 +599,10 @@ void Player::PlayPause(const quint64 offset_nanosec, const Playlist::AutoScroll
case EngineBase::State::Idle:{
pause_time_ = QDateTime();
play_offset_nanosec_ = offset_nanosec;
app_->playlist_manager()->SetActivePlaylist(app_->playlist_manager()->current_id());
if (app_->playlist_manager()->active()->rowCount() == 0) break;
int i = app_->playlist_manager()->active()->current_row();
if (i == -1) i = app_->playlist_manager()->active()->last_played_row();
playlist_manager_->SetActivePlaylist(playlist_manager_->current_id());
if (playlist_manager_->active()->rowCount() == 0) break;
int i = playlist_manager_->active()->current_row();
if (i == -1) i = playlist_manager_->active()->last_played_row();
if (i == -1) i = 0;
PlayAt(i, false, offset_nanosec, EngineBase::TrackChangeType::First, autoscroll, true);
break;
@@ -600,12 +615,12 @@ void Player::UnPause() {
if (current_item_ && pause_time_.isValid()) {
const Song &song = current_item_->Metadata();
if (url_handlers_.contains(song.url().scheme()) && song.stream_url_can_expire()) {
if (url_handlers_->CanHandle(song.url()) && song.stream_url_can_expire()) {
const quint64 time = QDateTime::currentSecsSinceEpoch() - pause_time_.toSecsSinceEpoch();
if (time >= 30) { // Stream URL might be expired.
qLog(Debug) << "Re-requesting stream URL for" << song.url();
play_offset_nanosec_ = engine_->position_nanosec();
UrlHandler *url_handler = url_handlers_.value(song.url().scheme());
UrlHandler *url_handler = url_handlers_->GetUrlHandler(song.url());
HandleLoadResult(url_handler->StartLoading(song.url()));
return;
}
@@ -636,8 +651,8 @@ void Player::RestartOrPrevious() {
void Player::Stop(const bool stop_after) {
engine_->Stop(stop_after);
app_->playlist_manager()->active()->set_current_row(-1);
app_->playlist_manager()->active()->reset_played_indexes();
playlist_manager_->active()->set_current_row(-1);
playlist_manager_->active()->reset_played_indexes();
current_item_.reset();
pause_time_ = QDateTime();
play_offset_nanosec_ = 0;
@@ -645,13 +660,13 @@ void Player::Stop(const bool stop_after) {
}
void Player::StopAfterCurrent() {
app_->playlist_manager()->active()->StopAfter(app_->playlist_manager()->active()->current_row());
playlist_manager_->active()->StopAfter(playlist_manager_->active()->current_row());
}
bool Player::PreviousWouldRestartTrack() const {
// Check if it has been over two seconds since previous button was pressed
return menu_previousmode_ == BehaviourSettingsPage::PreviousBehaviour::Restart && last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(QDateTime::currentDateTime()) >= 2;
return menu_previousmode_ == BehaviourSettings::PreviousBehaviour::Restart && last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(QDateTime::currentDateTime()) >= 2;
}
@@ -664,19 +679,19 @@ void Player::PreviousItem(const EngineBase::TrackChangeFlags change) {
const bool ignore_repeat_track = change & EngineBase::TrackChangeType::Manual;
if (menu_previousmode_ == BehaviourSettingsPage::PreviousBehaviour::Restart) {
if (menu_previousmode_ == BehaviourSettings::PreviousBehaviour::Restart) {
// Check if it has been over two seconds since previous button was pressed
QDateTime now = QDateTime::currentDateTime();
if (last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(now) >= 2) {
last_pressed_previous_ = now;
PlayAt(app_->playlist_manager()->active()->current_row(), false, 0, change, Playlist::AutoScroll::Always, false, true);
PlayAt(playlist_manager_->active()->current_row(), false, 0, change, Playlist::AutoScroll::Always, false, true);
return;
}
last_pressed_previous_ = now;
}
int i = app_->playlist_manager()->active()->previous_row(ignore_repeat_track);
app_->playlist_manager()->active()->set_current_row(i, Playlist::AutoScroll::Always, false);
int i = playlist_manager_->active()->previous_row(ignore_repeat_track);
playlist_manager_->active()->set_current_row(i, Playlist::AutoScroll::Always, false);
if (i == -1) {
Stop();
PlayAt(i, false, 0, change, Playlist::AutoScroll::Always, true);
@@ -790,22 +805,22 @@ void Player::PlayAt(const int index, const bool pause, const quint64 offset_nano
Q_EMIT TrackSkipped(current_item_);
}
if (current_item_ && app_->playlist_manager()->active()->has_item_at(index) && current_item_->Metadata().IsOnSameAlbum(app_->playlist_manager()->active()->item_at(index)->Metadata())) {
if (current_item_ && playlist_manager_->active()->has_item_at(index) && current_item_->Metadata().IsOnSameAlbum(playlist_manager_->active()->item_at(index)->Metadata())) {
change |= EngineBase::TrackChangeType::SameAlbum;
}
if (reshuffle) app_->playlist_manager()->active()->ReshuffleIndices();
if (reshuffle) playlist_manager_->active()->ReshuffleIndices();
app_->playlist_manager()->active()->set_current_row(index, autoscroll, false, force_inform);
if (app_->playlist_manager()->active()->current_row() == -1) {
playlist_manager_->active()->set_current_row(index, autoscroll, false, force_inform);
if (playlist_manager_->active()->current_row() == -1) {
// Maybe index didn't exist in the playlist.
return;
}
current_item_ = app_->playlist_manager()->active()->current_item();
current_item_ = playlist_manager_->active()->current_item();
const QUrl url = current_item_->StreamUrl();
if (url_handlers_.contains(url.scheme())) {
if (url_handlers_->CanHandle(url)) {
// It's already loading
if (loading_async_.contains(url)) {
return;
@@ -814,7 +829,7 @@ void Player::PlayAt(const int index, const bool pause, const quint64 offset_nano
pause_ = pause;
stream_change_type_ = change;
autoscroll_ = autoscroll;
UrlHandler *url_handler = url_handlers_.value(url.scheme());
UrlHandler *url_handler = url_handlers_->GetUrlHandler(url);
HandleLoadResult(url_handler->StartLoading(url));
}
else {
@@ -844,12 +859,12 @@ void Player::SeekTo(const quint64 seconds) {
engine_->Seek(nanosec);
qLog(Debug) << "Track seeked to" << nanosec << "ns - updating scrobble point";
app_->playlist_manager()->active()->UpdateScrobblePoint(nanosec);
playlist_manager_->active()->UpdateScrobblePoint(nanosec);
Q_EMIT Seeked(nanosec / 1000);
if (seconds == 0) {
app_->playlist_manager()->active()->InformOfCurrentSongChange(false);
playlist_manager_->active()->InformOfCurrentSongChange(false);
}
}
@@ -865,26 +880,26 @@ void Player::SeekBackward() {
void Player::EngineMetadataReceived(const EngineMetadata &engine_metadata) {
if (engine_metadata.type == EngineMetadata::Type::Any || engine_metadata.type == EngineMetadata::Type::Current) {
const int current_row = app_->playlist_manager()->active()->current_row();
const int current_row = playlist_manager_->active()->current_row();
if (current_row != -1) {
PlaylistItemPtr item = app_->playlist_manager()->active()->current_item();
PlaylistItemPtr item = playlist_manager_->active()->current_item();
if (item && engine_metadata.media_url == item->Url()) {
Song song = item->Metadata();
song.MergeFromEngineMetadata(engine_metadata);
app_->playlist_manager()->active()->UpdateItemMetadata(current_row, item, song, true);
playlist_manager_->active()->UpdateItemMetadata(current_row, item, song, true);
return;
}
}
}
if (engine_metadata.type == EngineMetadata::Type::Any || engine_metadata.type == EngineMetadata::Type::Next) {
const int next_row = app_->playlist_manager()->active()->next_row();
const int next_row = playlist_manager_->active()->next_row();
if (next_row != -1) {
PlaylistItemPtr next_item = app_->playlist_manager()->active()->item_at(next_row);
PlaylistItemPtr next_item = playlist_manager_->active()->item_at(next_row);
if (engine_metadata.media_url == next_item->Url()) {
Song song = next_item->Metadata();
song.MergeFromEngineMetadata(engine_metadata);
app_->playlist_manager()->active()->UpdateItemMetadata(next_row, next_item, song, true);
playlist_manager_->active()->UpdateItemMetadata(next_row, next_item, song, true);
}
}
}
@@ -893,9 +908,9 @@ void Player::EngineMetadataReceived(const EngineMetadata &engine_metadata) {
PlaylistItemPtr Player::GetItemAt(const int pos) const {
if (pos < 0 || pos >= app_->playlist_manager()->active()->rowCount())
if (pos < 0 || pos >= playlist_manager_->active()->rowCount())
return PlaylistItemPtr();
return app_->playlist_manager()->active()->item_at(pos);
return playlist_manager_->active()->item_at(pos);
}
@@ -940,10 +955,10 @@ void Player::PlayWithPause(const quint64 offset_nanosec) {
pause_time_ = QDateTime();
play_offset_nanosec_ = offset_nanosec;
app_->playlist_manager()->SetActivePlaylist(app_->playlist_manager()->current_id());
if (app_->playlist_manager()->active()->rowCount() == 0) return;
int i = app_->playlist_manager()->active()->current_row();
if (i == -1) i = app_->playlist_manager()->active()->last_played_row();
playlist_manager_->SetActivePlaylist(playlist_manager_->current_id());
if (playlist_manager_->active()->rowCount() == 0) return;
int i = playlist_manager_->active()->current_row();
if (i == -1) i = playlist_manager_->active()->last_played_row();
if (i == -1) i = 0;
PlayAt(i, true, offset_nanosec, EngineBase::TrackChangeType::First, Playlist::AutoScroll::Always, true);
@@ -959,11 +974,11 @@ void Player::TogglePrettyOSD() {
void Player::TrackAboutToEnd() {
const bool has_next_row = app_->playlist_manager()->active()->next_row() != -1;
const bool has_next_row = playlist_manager_->active()->next_row() != -1;
PlaylistItemPtr next_item;
if (has_next_row) {
next_item = app_->playlist_manager()->active()->item_at(app_->playlist_manager()->active()->next_row());
next_item = playlist_manager_->active()->item_at(playlist_manager_->active()->next_row());
}
if (engine_->is_autocrossfade_enabled()) {
@@ -989,10 +1004,10 @@ void Player::TrackAboutToEnd() {
QUrl url = next_item->StreamUrl();
// Get the actual track URL rather than the stream URL.
if (url_handlers_.contains(url.scheme())) {
if (url_handlers_->CanHandle(url)) {
if (loading_async_.contains(url)) return;
autoscroll_ = Playlist::AutoScroll::Maybe;
UrlHandler *url_handler = url_handlers_.value(url.scheme());
UrlHandler *url_handler = url_handlers_->GetUrlHandler(url);
const UrlHandler::LoadResult result = url_handler->StartLoading(url);
switch (result.type_) {
case UrlHandler::LoadResult::Type::Error:
@@ -1045,57 +1060,6 @@ void Player::InvalidSongRequested(const QUrl &url) {
}
void Player::RegisterUrlHandler(UrlHandler *handler) {
const QString scheme = handler->scheme();
if (url_handlers_.contains(scheme)) {
qLog(Warning) << "Tried to register a URL handler for" << scheme << "but one was already registered";
return;
}
qLog(Info) << "Registered URL handler for" << scheme;
url_handlers_.insert(scheme, handler);
QObject::connect(handler, &UrlHandler::destroyed, this, &Player::UrlHandlerDestroyed);
QObject::connect(handler, &UrlHandler::AsyncLoadComplete, this, &Player::HandleLoadResult);
}
void Player::UnregisterUrlHandler(UrlHandler *handler) {
const QString scheme = url_handlers_.key(handler);
if (scheme.isEmpty()) {
qLog(Warning) << "Tried to unregister a URL handler for" << handler->scheme() << "that wasn't registered";
return;
}
qLog(Info) << "Unregistered URL handler for" << scheme;
url_handlers_.remove(scheme);
QObject::disconnect(handler, &UrlHandler::destroyed, this, &Player::UrlHandlerDestroyed);
QObject::disconnect(handler, &UrlHandler::AsyncLoadComplete, this, &Player::HandleLoadResult);
}
const UrlHandler *Player::HandlerForUrl(const QUrl &url) const {
QMap<QString, UrlHandler*>::const_iterator it = url_handlers_.constFind(url.scheme());
if (it == url_handlers_.constEnd()) {
return nullptr;
}
return *it;
}
void Player::UrlHandlerDestroyed(QObject *object) {
UrlHandler *handler = static_cast<UrlHandler*>(object);
const QString scheme = url_handlers_.key(handler);
if (!scheme.isEmpty()) {
url_handlers_.remove(scheme);
}
}
void Player::HandleAuthentication() {
Q_EMIT Authenticated();
}

View File

@@ -31,110 +31,29 @@
#include <QString>
#include <QUrl>
#include "shared_ptr.h"
#include "urlhandler.h"
#include "includes/shared_ptr.h"
#include "core/urlhandler.h"
#include "core/enginemetadata.h"
#include "engine/enginebase.h"
#include "engine/enginemetadata.h"
#include "playlist/playlist.h"
#include "playlist/playlistitem.h"
#include "settings/behavioursettingspage.h"
#include "constants/behavioursettings.h"
#include "playerinterface.h"
class QTimer;
class Application;
class Song;
class TaskManager;
class UrlHandlers;
class PlaylistManager;
class AnalyzerContainer;
class Equalizer;
class GstStartup;
class PlayerInterface : public QObject {
Q_OBJECT
public:
explicit PlayerInterface(QObject *parent = nullptr) : QObject(parent) {}
virtual SharedPtr<EngineBase> engine() const = 0;
virtual EngineBase::State GetState() const = 0;
virtual uint GetVolume() const = 0;
virtual PlaylistItemPtr GetCurrentItem() const = 0;
virtual PlaylistItemPtr GetItemAt(const int pos) const = 0;
virtual void RegisterUrlHandler(UrlHandler *handler) = 0;
virtual void UnregisterUrlHandler(UrlHandler *handler) = 0;
public Q_SLOTS:
virtual void ReloadSettings() = 0;
virtual void LoadVolume() = 0;
virtual void SaveVolume() = 0;
virtual void SavePlaybackStatus() = 0;
virtual void PlaylistsLoaded() = 0;
// Manual track change to the specified track
virtual void PlayAt(const int index, const bool pause, const quint64 offset_nanosec, EngineBase::TrackChangeFlags change, const Playlist::AutoScroll autoscroll, const bool reshuffle, const bool force_inform = false) = 0;
// If there's currently a song playing, pause it, otherwise play the track that was playing last, or the first one on the playlist
virtual void PlayPause(const quint64 offset_nanosec = 0, const Playlist::AutoScroll autoscroll = Playlist::AutoScroll::Always) = 0;
virtual void PlayPauseHelper() = 0;
virtual void RestartOrPrevious() = 0;
// Skips this track. Might load more of the current radio station.
virtual void Next() = 0;
virtual void Previous() = 0;
virtual void PlayPlaylist(const QString &playlist_name) = 0;
virtual void SetVolumeFromEngine(const uint volume) = 0;
virtual void SetVolumeFromSlider(const int value) = 0;
virtual void SetVolume(const uint volume) = 0;
virtual void VolumeUp() = 0;
virtual void VolumeDown() = 0;
virtual void SeekTo(const quint64 seconds) = 0;
// Moves the position of the currently playing song five seconds forward.
virtual void SeekForward() = 0;
// Moves the position of the currently playing song five seconds backwards.
virtual void SeekBackward() = 0;
virtual void CurrentMetadataChanged(const Song &metadata) = 0;
virtual void Mute() = 0;
virtual void Pause() = 0;
virtual void Stop(const bool stop_after = false) = 0;
virtual void Play(const quint64 offset_nanosec = 0) = 0;
virtual void PlayWithPause(const quint64 offset_nanosec) = 0;
virtual void PlayHelper() = 0;
virtual void ShowOSD() = 0;
Q_SIGNALS:
void Playing();
void Paused();
// Emitted only when playback is manually resumed
void Resumed();
void Stopped();
void Error(const QString &message = QString());
void PlaylistFinished();
void VolumeEnabled(const bool volume_enabled);
void VolumeChanged(const uint volume);
void TrackSkipped(PlaylistItemPtr old_track);
// Emitted when there's a manual change to the current's track position.
void Seeked(const qint64 microseconds);
// Emitted when Player has processed a request to play another song.
// This contains the URL of the song and a flag saying whether it was able to play the song.
void SongChangeRequestProcessed(const QUrl &url, const bool valid);
// The toggle parameter is true when user requests to toggle visibility for Pretty OSD
void ForceShowOSD(const Song &song, const bool toggle);
void Authenticated();
};
class Player : public PlayerInterface {
Q_OBJECT
public:
explicit Player(Application *app, QObject *parent = nullptr);
static const char *kSettingsGroup;
explicit Player(const SharedPtr<TaskManager> task_manager, const SharedPtr<UrlHandlers> url_handlers, const SharedPtr<PlaylistManager> playlist_manager, QObject *parent = nullptr);
EngineBase::Type CreateEngine(EngineBase::Type Type);
void Init();
@@ -146,11 +65,6 @@ class Player : public PlayerInterface {
PlaylistItemPtr GetCurrentItem() const override { return current_item_; }
PlaylistItemPtr GetItemAt(const int pos) const override;
void RegisterUrlHandler(UrlHandler *handler) override;
void UnregisterUrlHandler(UrlHandler *handler) override;
const UrlHandler *HandlerForUrl(const QUrl &url) const;
bool PreviousWouldRestartTrack() const;
void SetAnalyzer(AnalyzerContainer *analyzer) { analyzer_ = analyzer; }
@@ -158,6 +72,7 @@ class Player : public PlayerInterface {
public Q_SLOTS:
void ReloadSettings() override;
void LoadVolume() override;
void SaveVolume() override;
void SavePlaybackStatus() override;
@@ -197,6 +112,8 @@ class Player : public PlayerInterface {
void EngineChanged(const EngineBase::Type Type);
private Q_SLOTS:
void UrlHandlerRegistered(UrlHandler *url_handler) const;
void EngineStateChanged(const EngineBase::State);
void EngineMetadataReceived(const EngineMetadata &engine_metadata);
void TrackAboutToEnd();
@@ -212,7 +129,6 @@ class Player : public PlayerInterface {
void ValidSongRequested(const QUrl&);
void InvalidSongRequested(const QUrl&);
void UrlHandlerDestroyed(QObject *object);
void HandleLoadResult(const UrlHandler::LoadResult &result);
private:
@@ -224,7 +140,9 @@ class Player : public PlayerInterface {
void UnPause();
private:
Application *app_;
const SharedPtr<TaskManager> task_manager_;
const SharedPtr<UrlHandlers> url_handlers_;
const SharedPtr<PlaylistManager> playlist_manager_;
SharedPtr<EngineBase> engine_;
GstStartup *gst_startup_;
AnalyzerContainer *analyzer_;
@@ -242,8 +160,6 @@ class Player : public PlayerInterface {
EngineBase::State last_state_;
int nb_errors_received_;
QMap<QString, UrlHandler*> url_handlers_;
QList<QUrl> loading_async_;
uint volume_;
uint volume_before_mute_;
@@ -251,13 +167,12 @@ class Player : public PlayerInterface {
bool continue_on_error_;
bool greyout_;
BehaviourSettingsPage::PreviousBehaviour menu_previousmode_;
BehaviourSettings::PreviousBehaviour menu_previousmode_;
int seek_step_sec_;
uint volume_increment_;
QDateTime pause_time_;
quint64 play_offset_nanosec_;
};
#endif // PLAYER_H

View File

@@ -1,5 +1,6 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
*
@@ -18,12 +19,6 @@
*
*/
#include <QString>
#include "playerinterface.h"
#include "scopedwchararray.h"
ScopedWCharArray::ScopedWCharArray(const QString &str)
: chars_(str.length()), data_(new wchar_t[chars_ + 1]) {
str.toWCharArray(data_.get());
data_[chars_] = '\0';
}
PlayerInterface::PlayerInterface(QObject *parent) : QObject(parent) {}

117
src/core/playerinterface.h Normal file
View File

@@ -0,0 +1,117 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PLAYERINTERFACE_H
#define PLAYERINTERFACE_H
#include "config.h"
#include <QObject>
#include <QString>
#include <QUrl>
#include "includes/shared_ptr.h"
#include "engine/enginebase.h"
#include "playlist/playlist.h"
#include "playlist/playlistitem.h"
class QTimer;
class Song;
class PlayerInterface : public QObject {
Q_OBJECT
public:
explicit PlayerInterface(QObject *parent = nullptr);
virtual SharedPtr<EngineBase> engine() const = 0;
virtual EngineBase::State GetState() const = 0;
virtual uint GetVolume() const = 0;
virtual PlaylistItemPtr GetCurrentItem() const = 0;
virtual PlaylistItemPtr GetItemAt(const int pos) const = 0;
public Q_SLOTS:
virtual void ReloadSettings() = 0;
virtual void LoadVolume() = 0;
virtual void SaveVolume() = 0;
virtual void SavePlaybackStatus() = 0;
virtual void PlaylistsLoaded() = 0;
// Manual track change to the specified track
virtual void PlayAt(const int index, const bool pause, const quint64 offset_nanosec, EngineBase::TrackChangeFlags change, const Playlist::AutoScroll autoscroll, const bool reshuffle, const bool force_inform = false) = 0;
// If there's currently a song playing, pause it, otherwise play the track that was playing last, or the first one on the playlist
virtual void PlayPause(const quint64 offset_nanosec = 0, const Playlist::AutoScroll autoscroll = Playlist::AutoScroll::Always) = 0;
virtual void PlayPauseHelper() = 0;
virtual void RestartOrPrevious() = 0;
// Skips this track. Might load more of the current radio station.
virtual void Next() = 0;
virtual void Previous() = 0;
virtual void PlayPlaylist(const QString &playlist_name) = 0;
virtual void SetVolumeFromEngine(const uint volume) = 0;
virtual void SetVolumeFromSlider(const int value) = 0;
virtual void SetVolume(const uint volume) = 0;
virtual void VolumeUp() = 0;
virtual void VolumeDown() = 0;
virtual void SeekTo(const quint64 seconds) = 0;
// Moves the position of the currently playing song five seconds forward.
virtual void SeekForward() = 0;
// Moves the position of the currently playing song five seconds backwards.
virtual void SeekBackward() = 0;
virtual void CurrentMetadataChanged(const Song &metadata) = 0;
virtual void Mute() = 0;
virtual void Pause() = 0;
virtual void Stop(const bool stop_after = false) = 0;
virtual void Play(const quint64 offset_nanosec = 0) = 0;
virtual void PlayWithPause(const quint64 offset_nanosec) = 0;
virtual void PlayHelper() = 0;
virtual void ShowOSD() = 0;
Q_SIGNALS:
void Playing();
void Paused();
// Emitted only when playback is manually resumed
void Resumed();
void Stopped();
void Error(const QString &message = QString());
void PlaylistFinished();
void VolumeEnabled(const bool volume_enabled);
void VolumeChanged(const uint volume);
void TrackSkipped(PlaylistItemPtr old_track);
// Emitted when there's a manual change to the current's track position.
void Seeked(const qint64 microseconds);
// Emitted when Player has processed a request to play another song.
// This contains the URL of the song and a flag saying whether it was able to play the song.
void SongChangeRequestProcessed(const QUrl &url, const bool valid);
// The toggle parameter is true when user requests to toggle visibility for Pretty OSD
void ForceShowOSD(const Song &song, const bool toggle);
void Authenticated();
};
#endif // PLAYERINTERFACE_H

View File

@@ -1,210 +0,0 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <QObject>
#include <QCoreApplication>
#include <QSystemTrayIcon>
#include <QAction>
#include <QMenu>
#include <QIcon>
#include <QString>
#include <QUrl>
#include "song.h"
#include "iconloader.h"
#include "qtsystemtrayicon.h"
using namespace Qt::Literals::StringLiterals;
SystemTrayIcon::SystemTrayIcon(QObject *parent)
: QSystemTrayIcon(parent),
menu_(new QMenu),
app_name_(QCoreApplication::applicationName()),
pixmap_playing_(u":/pictures/tiny-play.png"_s),
pixmap_paused_(u":/pictures/tiny-pause.png"_s),
action_play_pause_(nullptr),
action_stop_(nullptr),
action_stop_after_this_track_(nullptr),
action_mute_(nullptr),
action_love_(nullptr),
available_(false),
trayicon_progress_(false),
song_progress_(0) {
app_name_[0] = app_name_[0].toUpper();
const QIcon icon = IconLoader::Load(u"strawberry"_s);
const QIcon icon_grey = IconLoader::Load(u"strawberry-grey"_s);
pixmap_normal_ = icon.pixmap(48, QIcon::Normal);
if (icon_grey.isNull()) {
pixmap_grey_ = icon.pixmap(48, QIcon::Disabled);
}
else {
pixmap_grey_ = icon_grey.pixmap(48, QIcon::Disabled);
}
if (isSystemTrayAvailable()) {
available_ = true;
setIcon(icon);
setToolTip(app_name_);
}
QObject::connect(this, &QSystemTrayIcon::activated, this, &SystemTrayIcon::Clicked);
}
SystemTrayIcon::~SystemTrayIcon() {
delete menu_;
}
void SystemTrayIcon::SetTrayiconProgress(const bool enabled) {
trayicon_progress_ = enabled;
UpdateIcon();
}
void SystemTrayIcon::SetupMenu(QAction *previous, QAction *play, QAction *stop, QAction *stop_after, QAction *next, QAction *mute, QAction *love, QAction *quit) {
// Creating new actions and connecting them to old ones.
// This allows us to use old actions without displaying shortcuts that can not be used when Strawberry's window is hidden
menu_->addAction(previous->icon(), previous->text(), previous, &QAction::trigger);
action_play_pause_ = menu_->addAction(play->icon(), play->text(), play, &QAction::trigger);
action_stop_ = menu_->addAction(stop->icon(), stop->text(), stop, &QAction::trigger);
action_stop_after_this_track_ = menu_->addAction(stop_after->icon(), stop_after->text(), stop_after, &QAction::trigger);
menu_->addAction(next->icon(), next->text(), next, &QAction::trigger);
menu_->addSeparator();
action_mute_ = menu_->addAction(mute->icon(), mute->text(), mute, &QAction::trigger);
action_mute_->setCheckable(true);
action_mute_->setChecked(mute->isChecked());
menu_->addSeparator();
action_love_ = menu_->addAction(love->icon(), love->text(), love, &QAction::trigger);
action_love_->setVisible(love->isVisible());
action_love_->setEnabled(love->isEnabled());
menu_->addSeparator();
menu_->addAction(quit->icon(), quit->text(), quit, &QAction::trigger);
if (available_) setContextMenu(menu_);
}
void SystemTrayIcon::Clicked(const QSystemTrayIcon::ActivationReason reason) {
switch (reason) {
case QSystemTrayIcon::DoubleClick:
case QSystemTrayIcon::Trigger:
Q_EMIT ShowHide();
break;
case QSystemTrayIcon::MiddleClick:
Q_EMIT PlayPause();
break;
default:
break;
}
}
void SystemTrayIcon::ShowPopup(const QString &summary, const QString &message, const int timeout) {
if (available_) showMessage(summary, message, QSystemTrayIcon::NoIcon, timeout);
}
void SystemTrayIcon::UpdateIcon() {
if (available_) setIcon(CreateIcon(pixmap_normal_, pixmap_grey_));
}
void SystemTrayIcon::SetPlaying(bool enable_play_pause) {
current_state_icon_ = pixmap_playing_;
UpdateIcon();
action_stop_->setEnabled(true);
action_stop_after_this_track_->setEnabled(true);
action_play_pause_->setIcon(IconLoader::Load(u"media-playback-pause"_s));
action_play_pause_->setText(tr("Pause"));
action_play_pause_->setEnabled(enable_play_pause);
}
void SystemTrayIcon::SetPaused() {
current_state_icon_ = pixmap_paused_;
UpdateIcon();
action_stop_->setEnabled(true);
action_stop_after_this_track_->setEnabled(true);
action_play_pause_->setIcon(IconLoader::Load(u"media-playback-start"_s));
action_play_pause_->setText(tr("Play"));
action_play_pause_->setEnabled(true);
}
void SystemTrayIcon::SetStopped() {
current_state_icon_ = QPixmap();
UpdateIcon();
action_stop_->setEnabled(false);
action_stop_after_this_track_->setEnabled(false);
action_play_pause_->setIcon(IconLoader::Load(u"media-playback-start"_s));
action_play_pause_->setText(tr("Play"));
action_play_pause_->setEnabled(true);
action_love_->setEnabled(false);
}
void SystemTrayIcon::SetProgress(const int percentage) {
song_progress_ = percentage;
UpdateIcon();
}
void SystemTrayIcon::MuteButtonStateChanged(const bool value) {
if (action_mute_) action_mute_->setChecked(value);
}
void SystemTrayIcon::SetNowPlaying(const Song &song, const QUrl &url) {
Q_UNUSED(url)
if (available_) setToolTip(song.PrettyTitleWithArtist());
}
void SystemTrayIcon::ClearNowPlaying() {
if (available_) setToolTip(app_name_);
}
void SystemTrayIcon::LoveVisibilityChanged(const bool value) {
if (action_love_) action_love_->setVisible(value);
}
void SystemTrayIcon::LoveStateChanged(const bool value) {
if (action_love_) action_love_->setEnabled(value);
}

View File

@@ -1,105 +0,0 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef QTSYSTEMTRAYICON_H
#define QTSYSTEMTRAYICON_H
#include "config.h"
#include <QObject>
#include <QSystemTrayIcon>
#include <QString>
#include <QUrl>
#include <QIcon>
#include <QPixmap>
#include <QAction>
#include <QtEvents>
#include "song.h"
class QMenu;
class SystemTrayIcon : public QSystemTrayIcon {
Q_OBJECT
public:
explicit SystemTrayIcon(QObject *parent = nullptr);
~SystemTrayIcon() override;
bool IsSystemTrayAvailable() const { return available_; }
void SetTrayiconProgress(const bool enabled);
void SetupMenu(QAction *previous, QAction *play, QAction *stop, QAction *stop_after, QAction *next, QAction *mute, QAction *love, QAction *quit);
void ShowPopup(const QString &summary, const QString &message, const int timeout);
void SetPlaying(const bool enable_play_pause = false);
void SetPaused();
void SetStopped();
void SetProgress(const int percentage);
void MuteButtonStateChanged(const bool value);
void SetNowPlaying(const Song &song, const QUrl &url);
void ClearNowPlaying();
void LoveVisibilityChanged(const bool value);
void LoveStateChanged(const bool value);
bool MuteEnabled() const { return action_mute_->isVisible(); }
void SetMuteEnabled(const bool enabled) { action_mute_->setVisible(enabled); }
private:
QPixmap CreateIcon(const QPixmap &icon, const QPixmap &grey_icon);
void UpdateIcon();
Q_SIGNALS:
void ChangeVolume(const int delta);
void SeekForward();
void SeekBackward();
void NextTrack();
void PreviousTrack();
void ShowHide();
void PlayPause();
private Q_SLOTS:
void Clicked(const QSystemTrayIcon::ActivationReason);
private:
QMenu *menu_;
QString app_name_;
QPixmap pixmap_normal_;
QPixmap pixmap_grey_;
QPixmap pixmap_playing_;
QPixmap pixmap_paused_;
QAction *action_play_pause_;
QAction *action_stop_;
QAction *action_stop_after_this_track_;
QAction *action_mute_;
QAction *action_love_;
bool available_;
bool trayicon_progress_;
int song_progress_;
QPixmap current_state_icon_;
};
#endif // QTSYSTEMTRAYICON_H

View File

@@ -1,65 +0,0 @@
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_MAC_SCOPED_CFTYPEREF_H_
#define BASE_MAC_SCOPED_CFTYPEREF_H_
#include <CoreFoundation/CoreFoundation.h>
// ScopedCFTypeRef<> is patterned after scoped_ptr<>, but maintains ownership
// of a CoreFoundation object: any object that can be represented as a
// CFTypeRef. Style deviations here are solely for compatibility with
// scoped_ptr<>'s interface, with which everyone is already familiar.
//
// When ScopedCFTypeRef<> takes ownership of an object (in the constructor or
// in reset()), it takes over the caller's existing ownership claim. The
// caller must own the object it gives to ScopedCFTypeRef<>, and relinquishes
// an ownership claim to that object. ScopedCFTypeRef<> does not call
// CFRetain().
template<typename CFT>
class ScopedCFTypeRef {
public:
using element_type = CFT;
explicit ScopedCFTypeRef(CFT object = nullptr) : object_(object) {}
~ScopedCFTypeRef() {
if (object_) CFRelease(object_);
}
void reset(CFT object = nullptr) {
if (object_) CFRelease(object_);
object_ = object;
}
bool operator==(CFT that) const { return object_ == that; }
bool operator!=(CFT that) const { return object_ != that; }
operator CFT() const { return object_; }
CFT get() const { return object_; }
void swap(ScopedCFTypeRef &that) {
CFT temp = that.object_;
that.object_ = object_;
object_ = temp;
}
// ScopedCFTypeRef<>::release() is like scoped_ptr<>::release. It is NOT
// a wrapper for CFRelease(). To force a ScopedCFTypeRef<> object to call
// CFRelease(), use ScopedCFTypeRef<>::reset().
CFT release() __attribute__((warn_unused_result)) {
CFT temp = object_;
object_ = nullptr;
return temp;
}
private:
CFT object_;
Q_DISABLE_COPY(ScopedCFTypeRef);
};
#endif // BASE_MAC_SCOPED_CFTYPEREF_H_

View File

@@ -1,143 +0,0 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_MEMORY_SCOPED_NSOBJECT_H_
#define BASE_MEMORY_SCOPED_NSOBJECT_H_
#import <Foundation/Foundation.h>
// scoped_nsobject<> is patterned after scoped_ptr<>, but maintains ownership
// of an NSObject subclass object. Style deviations here are solely for
// compatibility with scoped_ptr<>'s interface, with which everyone is already
// familiar.
//
// When scoped_nsobject<> takes ownership of an object (in the constructor or
// in reset()), it takes over the caller's existing ownership claim. The
// caller must own the object it gives to scoped_nsobject<>, and relinquishes
// an ownership claim to that object. scoped_nsobject<> does not call
// -retain.
//
// scoped_nsobject<> is not to be used for NSAutoreleasePools. For
// NSAutoreleasePools use ScopedNSAutoreleasePool from
// scoped_nsautorelease_pool.h instead.
// We check for bad uses of scoped_nsobject and NSAutoreleasePool at compile
// time with a template specialization (see below).
template <typename NST>
class scoped_nsobject {
public:
explicit scoped_nsobject(NST* object = nil) : object_(object) {}
~scoped_nsobject() { [object_ release]; }
void reset(NST* object = nil) {
// We intentionally do not check that object != object_ as the caller must
// already have an ownership claim over whatever it gives to
// scoped_nsobject and ScopedCFTypeRef, whether it's in the constructor or
// in a call to reset(). In either case, it relinquishes that claim and
// the scoper assumes it.
[object_ release];
object_ = object;
}
bool operator==(NST* that) const { return object_ == that; }
bool operator!=(NST* that) const { return object_ != that; }
operator NST*() const { return object_; }
NST* get() const { return object_; }
void swap(scoped_nsobject &that) {
NST* temp = that.object_;
that.object_ = object_;
object_ = temp;
}
// scoped_nsobject<>::release() is like scoped_ptr<>::release. It is NOT
// a wrapper for [object_ release]. To force a scoped_nsobject<> object to
// call [object_ release], use scoped_nsobject<>::reset().
NST* release() __attribute__((warn_unused_result)) {
NST* temp = object_;
object_ = nil;
return temp;
}
private:
NST* object_;
Q_DISABLE_COPY(scoped_nsobject);
};
// Free functions
template <class C>
void swap(scoped_nsobject<C> &p1, scoped_nsobject<C> &p2) {
p1.swap(p2);
}
template <class C>
bool operator==(C* p1, const scoped_nsobject<C> &p2) {
return p1 == p2.get();
}
template <class C>
bool operator!=(C* p1, const scoped_nsobject<C> &p2) {
return p1 != p2.get();
}
// Specialization to make scoped_nsobject<id> work.
template <>
class scoped_nsobject<id> {
public:
explicit scoped_nsobject(id object = nil) : object_(object) {}
~scoped_nsobject() { [object_ release]; }
void reset(id object = nil) {
// We intentionally do not check that object != object_ as the caller must
// already have an ownership claim over whatever it gives to
// scoped_nsobject and ScopedCFTypeRef, whether it's in the constructor or
// in a call to reset(). In either case, it relinquishes that claim and
// the scoper assumes it.
[object_ release];
object_ = object;
}
bool operator==(id that) const { return object_ == that; }
bool operator!=(id that) const { return object_ != that; }
operator id() const { return object_; }
id get() const { return object_; }
void swap(scoped_nsobject &that) {
id temp = that.object_;
that.object_ = object_;
object_ = temp;
}
// scoped_nsobject<>::release() is like scoped_ptr<>::release. It is NOT
// a wrapper for [object_ release]. To force a scoped_nsobject<> object to
// call [object_ release], use scoped_nsobject<>::reset().
id release() __attribute__((warn_unused_result)) {
id temp = object_;
object_ = nil;
return temp;
}
private:
id object_;
Q_DISABLE_COPY(scoped_nsobject);
};
// Do not use scoped_nsobject for NSAutoreleasePools, use
// ScopedNSAutoreleasePool instead. This is a compile time check. See details
// at top of header.
template <>
class scoped_nsobject<NSAutoreleasePool> {
private:
explicit scoped_nsobject(NSAutoreleasePool* object = nil);
Q_DISABLE_COPY(scoped_nsobject);
};
#endif // BASE_MEMORY_SCOPED_NSOBJECT_H_

View File

@@ -1,68 +0,0 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCOPEDGOBJECT_H
#define SCOPEDGOBJECT_H
#include "config.h"
#include <glib-object.h>
template<typename T>
class ScopedGObject {
public:
ScopedGObject() : object_(nullptr) {}
ScopedGObject(const ScopedGObject &other) : object_(nullptr) {
reset(other.object_);
}
~ScopedGObject() { reset(); }
ScopedGObject &operator=(const ScopedGObject &other) {
reset(other.object_);
return *this;
}
void reset(T *new_object = nullptr) {
if (new_object) g_object_ref(new_object);
reset_without_add(new_object);
}
void reset_without_add(T *new_object = nullptr) {
if (object_) g_object_unref(object_);
object_ = new_object;
}
T *get() const { return object_; }
operator T*() const { return get(); }
T *operator*() const { return get(); }
operator bool() const { return get(); }
bool operator==(const ScopedGObject &other) const {
return object_ == other.object_;
}
private:
T *object_;
};
#endif // SCOPEDGOBJECT_H

View File

@@ -1,48 +0,0 @@
/*
* Strawberry Music Player
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCOPEDWCHARARRAY_H
#define SCOPEDWCHARARRAY_H
#include <QObject>
#include <QString>
#include "scoped_ptr.h"
class ScopedWCharArray {
public:
explicit ScopedWCharArray(const QString &str);
QString ToString() const { return QString::fromWCharArray(data_.get()); }
wchar_t *get() const { return data_.get(); }
explicit operator wchar_t *() const { return get(); }
qint64 characters() const { return chars_; }
qint64 bytes() const { return (chars_ + 1) * sizeof(wchar_t); }
private:
Q_DISABLE_COPY(ScopedWCharArray)
qint64 chars_;
ScopedPtr<wchar_t[]> data_;
};
#endif // SCOPEDWCHARARRAY_H

View File

@@ -51,11 +51,11 @@
#include <taglib/tstring.h>
#include "core/iconloader.h"
#include "engine/enginemetadata.h"
#include "core/enginemetadata.h"
#include "utilities/strutils.h"
#include "utilities/timeutils.h"
#include "utilities/coverutils.h"
#include "utilities/timeconstants.h"
#include "constants/timeconstants.h"
#include "utilities/sqlhelper.h"
#include "song.h"
@@ -1960,3 +1960,43 @@ QString Song::TitleRemoveMisc(const QString &title) {
return StripRegexList(title, kTitleMisc);
}
QString Song::GetNameForNewPlaylist(const SongList &songs) {
if (songs.isEmpty()) {
return QObject::tr("Playlist");
}
QSet<QString> artists;
QSet<QString> albums;
artists.reserve(songs.count());
albums.reserve(songs.count());
for (const Song &song : songs) {
artists << (song.effective_albumartist().isEmpty() ? QObject::tr("Unknown") : song.effective_albumartist());
albums << (song.album().isEmpty() ? QObject::tr("Unknown") : song.album());
if (artists.size() > 1) {
break;
}
}
bool various_artists = artists.size() > 1;
QString result;
if (various_artists) {
result = QObject::tr("Various artists");
}
else {
QStringList artist_names = artists.values();
result = artist_names.first();
}
if (!various_artists && albums.size() == 1) {
QStringList album_names = albums.values();
result += " - "_L1 + album_names.first();
}
return result;
}

View File

@@ -509,6 +509,8 @@ class Song {
static QString AlbumRemoveDiscMisc(const QString &album);
static QString TitleRemoveMisc(const QString &title);
static QString GetNameForNewPlaylist(const QList<Song> &songs);
static inline QString TagLibStringToQString(const TagLib::String &s) {
return QString::fromUtf8((s).toCString(true));
}

View File

@@ -39,15 +39,13 @@
#include <QUrl>
#include <QEventLoop>
#include "includes/shared_ptr.h"
#include "core/logging.h"
#include "shared_ptr.h"
#include "signalchecker.h"
#include "player.h"
#include "song.h"
#include "core/signalchecker.h"
#include "core/song.h"
#include "core/database.h"
#include "core/urlhandlers.h"
#include "songloader.h"
#include "database.h"
#include "engine/enginebase.h"
#include "tagreader/tagreaderclient.h"
#include "collection/collectionbackend.h"
#include "playlistparsers/cueparser.h"
@@ -66,13 +64,17 @@ constexpr int kDefaultTimeout = 5000;
QSet<QString> SongLoader::sRawUriSchemes;
SongLoader::SongLoader(SharedPtr<CollectionBackendInterface> collection_backend, const SharedPtr<Player> player, QObject *parent)
SongLoader::SongLoader(const SharedPtr<UrlHandlers> url_handlers,
const SharedPtr<CollectionBackendInterface> collection_backend,
const SharedPtr<TagReaderClient> tagreader_client,
QObject *parent)
: QObject(parent),
player_(player),
url_handlers_(url_handlers),
collection_backend_(collection_backend),
tagreader_client_(tagreader_client),
timeout_timer_(new QTimer(this)),
playlist_parser_(new PlaylistParser(collection_backend, this)),
cue_parser_(new CueParser(collection_backend, this)),
playlist_parser_(new PlaylistParser(tagreader_client, collection_backend, this)),
cue_parser_(new CueParser(tagreader_client, collection_backend, this)),
parser_(nullptr),
state_(State::WaitingForType),
timeout_(kDefaultTimeout),
@@ -114,23 +116,16 @@ SongLoader::Result SongLoader::Load(const QUrl &url) {
return LoadLocal(url_.toLocalFile());
}
if (sRawUriSchemes.contains(url_.scheme()) || player_->HandlerForUrl(url)) {
if (sRawUriSchemes.contains(url_.scheme()) || url_handlers_->CanHandle(url)) {
// The URI scheme indicates that it can't possibly be a playlist,
// or we have a custom handler for the URL, so add it as a raw stream.
AddAsRawStream();
return Result::Success;
}
if (player_->engine()->type() == EngineBase::Type::GStreamer) {
preload_func_ = std::bind(&SongLoader::LoadRemote, this);
return Result::BlockingLoadRequired;
}
else {
errors_ << tr("You need GStreamer for this URL.");
return Result::Error;
}
preload_func_ = std::bind(&SongLoader::LoadRemote, this);
return Result::Success;
return Result::BlockingLoadRequired;
}
@@ -165,7 +160,7 @@ SongLoader::Result SongLoader::LoadLocalPartial(const QString &filename) {
// Assume it's just a normal file
if (!Song::kRejectedExtensions.contains(fileinfo.suffix(), Qt::CaseInsensitive) &&
(TagReaderClient::Instance()->IsMediaFileBlocking(filename) ||
(tagreader_client_->IsMediaFileBlocking(filename) ||
Song::kAcceptedExtensions.contains(fileinfo.suffix(), Qt::CaseInsensitive))) {
Song song(Song::Source::LocalFile);
song.InitFromFilePartial(filename, fileinfo);
@@ -183,19 +178,14 @@ SongLoader::Result SongLoader::LoadLocalPartial(const QString &filename) {
SongLoader::Result SongLoader::LoadAudioCD() {
#ifdef HAVE_AUDIOCD
if (player_->engine()->type() == EngineBase::Type::GStreamer) {
CddaSongLoader *cdda_song_loader = new CddaSongLoader(QUrl(), this);
QObject::connect(cdda_song_loader, &CddaSongLoader::SongsDurationLoaded, this, &SongLoader::AudioCDTracksLoadFinishedSlot);
QObject::connect(cdda_song_loader, &CddaSongLoader::SongsMetadataLoaded, this, &SongLoader::AudioCDTracksTagsLoaded);
cdda_song_loader->LoadSongs();
return Result::Success;
}
else {
#endif
errors_ << tr("CD playback is only available with the GStreamer engine.");
return Result::Error;
#ifdef HAVE_AUDIOCD
}
CddaSongLoader *cdda_song_loader = new CddaSongLoader(QUrl(), this);
QObject::connect(cdda_song_loader, &CddaSongLoader::SongsDurationLoaded, this, &SongLoader::AudioCDTracksLoadFinishedSlot);
QObject::connect(cdda_song_loader, &CddaSongLoader::SongsMetadataLoaded, this, &SongLoader::AudioCDTracksTagsLoaded);
cdda_song_loader->LoadSongs();
return Result::Success;
#else
errors_ << tr("Missing CDDA playback.");
return Result::Error;
#endif
}
@@ -306,7 +296,7 @@ SongLoader::Result SongLoader::LoadLocalAsync(const QString &filename) {
// Assume it's just a normal file
if (!Song::kRejectedExtensions.contains(fileinfo.suffix(), Qt::CaseInsensitive) &&
(TagReaderClient::Instance()->IsMediaFileBlocking(filename) ||
(tagreader_client_->IsMediaFileBlocking(filename) ||
Song::kAcceptedExtensions.contains(fileinfo.suffix(), Qt::CaseInsensitive))) {
Song song(Song::Source::LocalFile);
song.InitFromFilePartial(filename, fileinfo);
@@ -346,7 +336,7 @@ void SongLoader::EffectiveSongLoad(Song *song) {
else {
// It's a normal media file
const QString filename = song->url().toLocalFile();
const TagReaderResult result = TagReaderClient::Instance()->ReadFileBlocking(filename, song);
const TagReaderResult result = tagreader_client_->ReadFileBlocking(filename, song);
if (!result.success()) {
qLog(Error) << "Could not read file" << song->url() << result.error_string();
}

View File

@@ -38,11 +38,12 @@
#include <QStringList>
#include <QUrl>
#include "shared_ptr.h"
#include "song.h"
#include "includes/shared_ptr.h"
#include "core/song.h"
class QTimer;
class Player;
class UrlHandlers;
class TagReaderClient;
class CollectionBackendInterface;
class PlaylistParser;
class ParserBase;
@@ -56,7 +57,11 @@ class SongLoader : public QObject {
Q_OBJECT
public:
explicit SongLoader(SharedPtr<CollectionBackendInterface> collection_backend, const SharedPtr<Player> player, QObject *parent = nullptr);
explicit SongLoader(const SharedPtr<UrlHandlers> url_handlers,
const SharedPtr<CollectionBackendInterface> collection_backend,
const SharedPtr<TagReaderClient> tagreader_client,
QObject *parent = nullptr);
~SongLoader() override;
enum class Result {
@@ -137,8 +142,9 @@ class SongLoader : public QObject {
QUrl url_;
SongList songs_;
const SharedPtr<Player> player_;
SharedPtr<CollectionBackendInterface> collection_backend_;
const SharedPtr<UrlHandlers> url_handlers_;
const SharedPtr<CollectionBackendInterface> collection_backend_;
const SharedPtr<TagReaderClient> tagreader_client_;
QTimer *timeout_timer_;
PlaylistParser *playlist_parser_;
CueParser *cue_parser_;

View File

@@ -1,6 +1,8 @@
/*
* Strawberry Music Player
* Copyright 2023, Jonas Kvinge <jonas@jkvinge.net>
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,12 +19,8 @@
*
*/
#ifndef SCOPED_PTR_H
#define SCOPED_PTR_H
#include "songmimedata.h"
#include <memory>
template <typename T, typename D = std::default_delete<T>>
using ScopedPtr = std::unique_ptr<T, D>;
#endif // SCOPED_PTR_H
SongMimeData::SongMimeData(QObject *parent) : backend(nullptr) {
Q_UNUSED(parent);
}

44
src/core/songmimedata.h Normal file
View File

@@ -0,0 +1,44 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SONGMIMEDATA_H
#define SONGMIMEDATA_H
#include "config.h"
#include "includes/shared_ptr.h"
#include "mimedata.h"
#include "core/song.h"
class CollectionBackendInterface;
class SongMimeData : public MimeData {
Q_OBJECT
public:
explicit SongMimeData(QObject *parent = nullptr);
SharedPtr<CollectionBackendInterface> backend;
SongList songs;
};
#endif // SONGMIMEDATA_H

View File

@@ -35,8 +35,8 @@
#include <QPalette>
#include <QEvent>
#include "shared_ptr.h"
#include "core/logging.h"
#include "includes/shared_ptr.h"
#include "logging.h"
#include "stylesheetloader.h"
using namespace Qt::Literals::StringLiterals;

View File

@@ -30,7 +30,7 @@
#include <QPalette>
#include <QString>
#include "shared_ptr.h"
#include "includes/shared_ptr.h"
class QWidget;
class QEvent;

View File

@@ -1,79 +0,0 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2019-2021, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <cmath>
#include <QPixmap>
#include <QPainter>
#include <QPoint>
#include <QPolygon>
#include <QRect>
#ifdef Q_OS_MACOS
# include "macsystemtrayicon.h"
#else
# include "qtsystemtrayicon.h"
#endif
QPixmap SystemTrayIcon::CreateIcon(const QPixmap &icon, const QPixmap &grey_icon) {
QRect rect(icon.rect());
QPixmap ret(icon);
QPainter p(&ret);
if (trayicon_progress_) {
// The angle of the line that's used to cover the icon.
// Centered on rect.topLeft()
double angle = static_cast<double>(100 - song_progress_) / 100.0 * M_PI_2;
double length = sqrt(pow(rect.width(), 2.0) + pow(rect.height(), 2.0));
QPolygon mask;
mask << rect.topLeft();
mask << rect.topLeft() + QPoint(static_cast<int>(length * sin(angle)), static_cast<int>(length * cos(angle)));
if (song_progress_ > 50) mask << rect.bottomRight();
mask << rect.topRight();
mask << rect.topLeft();
// Draw the grey bit
p.setClipRegion(mask);
p.drawPixmap(0, 0, grey_icon);
p.setClipping(false);
}
// Draw the playing or paused icon in the top-right
if (!current_state_icon_.isNull()) {
int height = rect.height() / 2;
QPixmap scaled(current_state_icon_.scaledToHeight(height, Qt::SmoothTransformation));
QRect state_rect(rect.width() - scaled.width(), 0, scaled.width(), scaled.height());
p.drawPixmap(state_rect, scaled);
}
p.end();
return ret;
}

View File

@@ -30,7 +30,7 @@
using namespace Qt::Literals::StringLiterals;
Translations::Translations() {}
Translations::Translations() = default;
Translations::~Translations() {

View File

@@ -1,8 +1,6 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -1,8 +1,6 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

95
src/core/urlhandlers.cpp Normal file
View File

@@ -0,0 +1,95 @@
/*
* Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <QString>
#include "core/logging.h"
#include "urlhandlers.h"
#include "urlhandler.h"
UrlHandlers::UrlHandlers(QObject *parent) : QObject(parent) {}
void UrlHandlers::Register(UrlHandler *url_handler) {
const QString scheme = url_handler->scheme();
if (url_handlers_.contains(scheme)) {
qLog(Warning) << "Tried to register a URL handler for" << scheme << "but one was already registered";
return;
}
qLog(Info) << "Registered URL handler for" << scheme;
url_handlers_.insert(scheme, url_handler);
QObject::connect(url_handler, &UrlHandler::destroyed, this, &UrlHandlers::Destroyed);
Q_EMIT Registered(url_handler);
}
void UrlHandlers::Unregister(UrlHandler *url_handler) {
const QString scheme = url_handlers_.key(url_handler);
if (scheme.isEmpty()) {
qLog(Warning) << "Tried to unregister a URL handler for" << url_handler->scheme() << "that wasn't registered";
return;
}
qLog(Info) << "Unregistered URL handler for" << scheme;
url_handlers_.remove(scheme);
QObject::disconnect(url_handler, &UrlHandler::destroyed, this, &UrlHandlers::Destroyed);
QObject::disconnect(url_handler, &UrlHandler::AsyncLoadComplete, nullptr, nullptr);
}
void UrlHandlers::Destroyed(QObject *object) {
UrlHandler *handler = static_cast<UrlHandler*>(object);
const QString scheme = url_handlers_.key(handler);
if (!scheme.isEmpty()) {
url_handlers_.remove(scheme);
}
}
bool UrlHandlers::CanHandle(const QString &scheme) const {
return url_handlers_.contains(scheme);
}
bool UrlHandlers::CanHandle(const QUrl &url) const {
return url_handlers_.contains(url.scheme());
}
UrlHandler *UrlHandlers::GetUrlHandler(const QString &scheme) const {
if (!CanHandle(scheme)) return nullptr;
return url_handlers_.value(scheme);
}
UrlHandler *UrlHandlers::GetUrlHandler(const QUrl &url) const {
return GetUrlHandler(url.scheme());
}

56
src/core/urlhandlers.h Normal file
View File

@@ -0,0 +1,56 @@
/*
* Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef URLHANDLERS_H
#define URLHANDLERS_H
#include "config.h"
#include <QObject>
#include <QMap>
#include <QString>
#include <QUrl>
class UrlHandler;
class UrlHandlers : public QObject {
Q_OBJECT
public:
explicit UrlHandlers(QObject *parent = nullptr);
void Register(UrlHandler *url_handler);
void Unregister(UrlHandler *url_handler);
void Destroyed(QObject *object);
bool CanHandle(const QString &scheme) const;
bool CanHandle(const QUrl &url) const;
UrlHandler *GetUrlHandler(const QString &scheme) const;
UrlHandler *GetUrlHandler(const QUrl &url) const;
Q_SIGNALS:
void Registered(UrlHandler *url_handler);
void UnRegistered(UrlHandler *url_handler);
private:
QMap<QString, UrlHandler*> url_handlers_;
};
#endif // URLHANDLERS_H