Refactoring
This commit is contained in:
@@ -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
|
||||
@@ -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(); }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)))
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
#include "shared_ptr.h"
|
||||
#include "includes/shared_ptr.h"
|
||||
#include "song.h"
|
||||
|
||||
class QThread;
|
||||
|
||||
31
src/core/enginemetadata.cpp
Normal file
31
src/core/enginemetadata.cpp
Normal 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
56
src/core/enginemetadata.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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
37
src/core/memorydatabase.h
Normal 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
|
||||
@@ -35,7 +35,7 @@
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include "core/scoped_ptr.h"
|
||||
#include "includes/scoped_ptr.h"
|
||||
|
||||
class QMimeData;
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include <QList>
|
||||
#include <QImage>
|
||||
|
||||
#include "shared_ptr.h"
|
||||
#include "includes/shared_ptr.h"
|
||||
#include "song.h"
|
||||
|
||||
class MusicStorage {
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
117
src/core/playerinterface.h
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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
44
src/core/songmimedata.h
Normal 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include <QPalette>
|
||||
#include <QString>
|
||||
|
||||
#include "shared_ptr.h"
|
||||
#include "includes/shared_ptr.h"
|
||||
|
||||
class QWidget;
|
||||
class QEvent;
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
Translations::Translations() {}
|
||||
Translations::Translations() = default;
|
||||
|
||||
Translations::~Translations() {
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
95
src/core/urlhandlers.cpp
Normal 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
56
src/core/urlhandlers.h
Normal 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
|
||||
Reference in New Issue
Block a user