mirror of
https://github.com/clangen/musikcube.git
synced 2025-03-14 13:21:13 +00:00
- Cleaned up plugin loading in PluginFactory
- Added the ability to selectively enable/disable plugins in prefs - Tweaked win32globalhotkeys to not register a hook if disabled - Added more functionality to ListOverlay, including auto-dismiss and percentage-based width options - Fixed "remove missing files" setting
This commit is contained in:
parent
e8985aadd1
commit
9c6e136c79
@ -40,7 +40,8 @@
|
||||
|
||||
musik::core::sdk::IPlaybackService* playback;
|
||||
|
||||
static HHOOK hook = NULL;
|
||||
static HHOOK hook = nullptr;
|
||||
static HMODULE module = nullptr;
|
||||
|
||||
LRESULT CALLBACK ShellProc(int code, WPARAM wParam, LPARAM lParam) {
|
||||
if (code == HC_ACTION && playback) {
|
||||
@ -116,15 +117,28 @@ LRESULT CALLBACK ShellProc(int code, WPARAM wParam, LPARAM lParam) {
|
||||
return CallNextHookEx(nullptr, code, wParam, lParam);
|
||||
}
|
||||
|
||||
void installHook() {
|
||||
if (!::hook) {
|
||||
hook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)ShellProc, module, 0L);
|
||||
}
|
||||
}
|
||||
|
||||
void removeHook() {
|
||||
if (::hook) {
|
||||
UnhookWindowsHookEx(::hook);
|
||||
::hook = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) {
|
||||
switch (reason) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
hook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC) ShellProc, module, 0L);
|
||||
::module = module;
|
||||
break;
|
||||
|
||||
case DLL_PROCESS_DETACH:
|
||||
UnhookWindowsHookEx(hook);
|
||||
hook = nullptr;
|
||||
::module = nullptr;
|
||||
removeHook();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -156,6 +170,12 @@ class MMShellHook:
|
||||
|
||||
virtual void SetPlaybackService(musik::core::sdk::IPlaybackService* playback) {
|
||||
::playback = playback;
|
||||
if (playback) {
|
||||
installHook();
|
||||
}
|
||||
else {
|
||||
removeHook();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnTrackChanged(musik::core::sdk::ITrack* track) {
|
||||
|
@ -36,8 +36,10 @@
|
||||
|
||||
#include <core/sdk/constants.h>
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
#include <core/config.h>
|
||||
#include <core/support/Preferences.h>
|
||||
#include <core/support/PreferenceKeys.h>
|
||||
#include <core/support/Common.h>
|
||||
#include <core/config.h>
|
||||
#include <core/debug.h>
|
||||
#include <iostream>
|
||||
|
||||
@ -46,7 +48,15 @@ static std::mutex instanceMutex;
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
PluginFactory& PluginFactory:: Instance() {
|
||||
#ifdef WIN32
|
||||
typedef musik::core::sdk::IPlugin* STDCALL(CallGetPlugin);
|
||||
static void closeNativeHandle(void* dll) { FreeLibrary((HMODULE)dll); }
|
||||
#else
|
||||
typedef musik::core::sdk::IPlugin* (*CallGetPlugin)();
|
||||
static void closeNativeHandle(void* dll) { dclose(dll); }
|
||||
#endif
|
||||
|
||||
PluginFactory& PluginFactory::Instance() {
|
||||
std::unique_lock<std::mutex> lock(instanceMutex);
|
||||
|
||||
static PluginFactory* instance = NULL;
|
||||
@ -59,25 +69,17 @@ PluginFactory& PluginFactory:: Instance() {
|
||||
}
|
||||
|
||||
PluginFactory::PluginFactory() {
|
||||
this->prefs = Preferences::ForComponent(prefs::components::Plugins);
|
||||
musik::debug::info(TAG, "loading plugins");
|
||||
this->LoadPlugins();
|
||||
}
|
||||
|
||||
PluginFactory::~PluginFactory() {
|
||||
for (size_t i = 0; i < this->loadedPlugins.size(); i++) {
|
||||
this->loadedPlugins[i]->Destroy();
|
||||
for (std::shared_ptr<Descriptor> plugin : this->plugins) {
|
||||
plugin->plugin->Destroy();
|
||||
closeNativeHandle(plugin->nativeHandle);
|
||||
}
|
||||
|
||||
std::vector<void*>::iterator dll = this->loadedDlls.begin();
|
||||
for( ; dll != this->loadedDlls.end(); dll++) {
|
||||
#ifdef WIN32
|
||||
FreeLibrary((HMODULE) (*dll));
|
||||
#else
|
||||
dlclose(*dll);
|
||||
#endif
|
||||
}
|
||||
|
||||
loadedDlls.clear();
|
||||
plugins.clear();
|
||||
}
|
||||
|
||||
void PluginFactory::LoadPlugins() {
|
||||
@ -96,6 +98,11 @@ void PluginFactory::LoadPlugins() {
|
||||
for (boost::filesystem::directory_iterator file(dir); file != end; file++) {
|
||||
if (boost::filesystem::is_regular(file->status())){
|
||||
std::string filename(file->path().string());
|
||||
|
||||
std::shared_ptr<Descriptor> descriptor(new Descriptor());
|
||||
descriptor->filename = filename;
|
||||
descriptor->key = boost::filesystem::path(filename).filename().string();
|
||||
|
||||
#ifdef WIN32
|
||||
/* if the file ends with ".dll", we'll try to load it*/
|
||||
if (filename.substr(filename.size() - 4) == ".dll") {
|
||||
@ -108,8 +115,9 @@ void PluginFactory::LoadPlugins() {
|
||||
/* exists? check the version, and add it! */
|
||||
auto plugin = getPluginCall();
|
||||
if (plugin->SdkVersion() == musik::core::sdk::SdkVersion) {
|
||||
this->loadedPlugins.push_back(plugin);
|
||||
this->loadedDlls.push_back(dll);
|
||||
descriptor->plugin = plugin;
|
||||
descriptor->nativeHandle = dll;
|
||||
this->plugins.push_back(descriptor);
|
||||
}
|
||||
else {
|
||||
FreeLibrary(dll);
|
||||
@ -159,8 +167,9 @@ void PluginFactory::LoadPlugins() {
|
||||
auto plugin = getPluginCall();
|
||||
if (plugin->SdkVersion() == musik::core::sdk::SdkVersion) {
|
||||
musik::debug::info(TAG, "loaded: " + filename);
|
||||
this->loadedPlugins.push_back(getPluginCall());
|
||||
this->loadedDlls.push_back(dll);
|
||||
descriptor->plugin = getPluginCall();
|
||||
descriptor->nativeHandle = dll;
|
||||
this->plugins.push_back(descriptor);
|
||||
}
|
||||
else {
|
||||
dlclose(dll);
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
#include <core/config.h>
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include <core/sdk/IPreferences.h>
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
@ -57,45 +58,53 @@ namespace musik { namespace core {
|
||||
static PluginFactory& Instance();
|
||||
|
||||
template <typename T>
|
||||
class DestroyDeleter {
|
||||
public: void operator()(T* t) {
|
||||
struct DestroyDeleter {
|
||||
void operator()(T* t) {
|
||||
t->Destroy();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class NullDeleter {
|
||||
public: void operator()(T* t) {
|
||||
struct NullDeleter {
|
||||
void operator()(T* t) {
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class D> std::vector<std::shared_ptr<T> > QueryInterface(const char* functionName) {
|
||||
template <class T, class D> void QueryInterface(
|
||||
const char* functionName,
|
||||
std::function<void(std::shared_ptr<T>, const std::string&)> handler)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
|
||||
typedef T* STDCALL(PluginInterfaceCall);
|
||||
|
||||
std::vector<std::shared_ptr<T> > plugins;
|
||||
HandleList& allDlls = PluginFactory::Instance().loadedDlls;
|
||||
for (std::shared_ptr<Descriptor> descriptor : this->plugins) {
|
||||
if (functionName == "GetPlugin" || prefs->GetBool(descriptor->key.c_str(), true)) { /* enabled */
|
||||
PluginInterfaceCall funcPtr =
|
||||
#ifdef WIN32
|
||||
(PluginInterfaceCall) GetProcAddress((HMODULE)(descriptor->nativeHandle), functionName);
|
||||
#else
|
||||
(PluginInterfaceCall)dlsym(descriptor->nativeHandle, functionName);
|
||||
#endif
|
||||
if (funcPtr) {
|
||||
T* result = funcPtr();
|
||||
|
||||
typedef HandleList::iterator Iterator;
|
||||
Iterator currentDll = allDlls.begin();
|
||||
while (currentDll != allDlls.end()) {
|
||||
PluginInterfaceCall funcPtr =
|
||||
#ifdef WIN32
|
||||
(PluginInterfaceCall) GetProcAddress((HMODULE)(*currentDll), functionName);
|
||||
#else
|
||||
(PluginInterfaceCall) dlsym(*currentDll, functionName);
|
||||
#endif
|
||||
if (funcPtr) {
|
||||
T* result = funcPtr();
|
||||
|
||||
if (result) {
|
||||
plugins.push_back(std::shared_ptr<T>(result, D()));
|
||||
if (result) {
|
||||
handler(std::shared_ptr<T>(result, D()), descriptor->filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentDll++;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class D> std::vector<std::shared_ptr<T> > QueryInterface(const char* functionName) {
|
||||
std::vector<std::shared_ptr<T> > plugins;
|
||||
|
||||
QueryInterface<T, D>(
|
||||
functionName,
|
||||
[&plugins](std::shared_ptr<T> plugin, const std::string& fn) {
|
||||
plugins.push_back(plugin);
|
||||
});
|
||||
|
||||
return plugins;
|
||||
}
|
||||
@ -106,39 +115,35 @@ namespace musik { namespace core {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
|
||||
for (size_t i = 0; i < loadedDlls.size(); i++) {
|
||||
void* currentDll = loadedDlls[i];
|
||||
|
||||
T funcPtr =
|
||||
for (std::shared_ptr<Descriptor> descriptor : this->plugins) {
|
||||
if (prefs->GetBool(descriptor->key.c_str(), true)) { /* if enabled by prefs */
|
||||
T funcPtr =
|
||||
#ifdef WIN32
|
||||
(T) GetProcAddress((HMODULE)(currentDll), functionName);
|
||||
(T) GetProcAddress((HMODULE)(descriptor->nativeHandle), functionName);
|
||||
#else
|
||||
(T) dlsym(currentDll, functionName);
|
||||
(T)dlsym(descriptor->nativeHandle, functionName);
|
||||
#endif
|
||||
if (funcPtr) {
|
||||
handler(loadedPlugins[i], funcPtr);
|
||||
if (funcPtr) {
|
||||
handler(descriptor->plugin, funcPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct Descriptor {
|
||||
musik::core::sdk::IPlugin* plugin;
|
||||
void* nativeHandle;
|
||||
std::string filename;
|
||||
std::string key;
|
||||
};
|
||||
|
||||
PluginFactory();
|
||||
~PluginFactory();
|
||||
|
||||
void LoadPlugins();
|
||||
|
||||
#ifdef WIN32
|
||||
typedef musik::core::sdk::IPlugin* STDCALL(CallGetPlugin);
|
||||
#else
|
||||
typedef musik::core::sdk::IPlugin* (*CallGetPlugin)();
|
||||
#endif
|
||||
|
||||
typedef std::vector<musik::core::sdk::IPlugin*> PluginList;
|
||||
typedef std::vector<void*> HandleList;
|
||||
|
||||
PluginList loadedPlugins;
|
||||
HandleList loadedDlls;
|
||||
std::vector<std::shared_ptr<Descriptor> > plugins;
|
||||
std::mutex mutex;
|
||||
std::shared_ptr<musik::core::sdk::IPreferences> prefs;
|
||||
};
|
||||
} }
|
||||
|
@ -40,6 +40,7 @@ namespace musik { namespace core { namespace prefs {
|
||||
const std::string components::Settings = "settings";
|
||||
const std::string components::Libraries = "libraries";
|
||||
const std::string components::Playback = "playback";
|
||||
const std::string components::Plugins = "plugins";
|
||||
|
||||
const std::string keys::AutoSyncIntervalMillis = "AutoSyncIntervalMillis";
|
||||
const std::string keys::MaxTagReadThreads = "MaxTagReadThreads";
|
||||
|
@ -42,6 +42,7 @@ namespace musik { namespace core { namespace prefs {
|
||||
extern const std::string Settings;
|
||||
extern const std::string Libraries;
|
||||
extern const std::string Playback;
|
||||
extern const std::string Plugins;
|
||||
}
|
||||
|
||||
namespace keys {
|
||||
|
@ -12,6 +12,7 @@ set (BOX_SRCS
|
||||
./app/model/DirectoryAdapter.cpp
|
||||
./app/overlay/PlaybackOverlays.cpp
|
||||
./app/overlay/PlayQueueOverlays.cpp
|
||||
./app/overlay/PluginOverlay.cpp
|
||||
./app/overlay/VisualizerOverlay.cpp
|
||||
./app/util/Hotkeys.cpp
|
||||
./app/util/GlobalHotkeys.cpp
|
||||
@ -26,7 +27,7 @@ set (BOX_SRCS
|
||||
./cursespp/Checkbox.cpp
|
||||
./cursespp/Colors.cpp
|
||||
./cursespp/DialogOverlay.cpp
|
||||
./cursespp/InputOverlay.cpp
|
||||
./cursespp/InputOverlay.cpp
|
||||
./cursespp/LayoutBase.cpp
|
||||
./cursespp/ListOverlay.cpp
|
||||
./cursespp/ListWindow.cpp
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include <app/util/Hotkeys.h>
|
||||
#include <app/util/PreferenceKeys.h>
|
||||
#include <app/overlay/PlaybackOverlays.h>
|
||||
#include <app/overlay/PluginOverlay.h>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
@ -147,6 +148,10 @@ void SettingsLayout::OnTransportDropdownActivate(cursespp::TextLabel* label) {
|
||||
});
|
||||
}
|
||||
|
||||
void SettingsLayout::OnPluginsDropdownActivate(cursespp::TextLabel* label) {
|
||||
PluginOverlay::Show();
|
||||
}
|
||||
|
||||
void SettingsLayout::OnLayout() {
|
||||
int x = this->GetX(), y = this->GetY();
|
||||
int cx = this->GetWidth(), cy = this->GetHeight();
|
||||
@ -166,23 +171,16 @@ void SettingsLayout::OnLayout() {
|
||||
this->browseList->MoveAndResize(leftX, pathListsY, leftWidth, pathsHeight);
|
||||
this->addedPathsList->MoveAndResize(rightX, pathListsY, rightWidth, pathsHeight);
|
||||
|
||||
this->outputDropdown->MoveAndResize(1, BOTTOM(this->browseList), cx - 1, LABEL_HEIGHT);
|
||||
this->transportDropdown->MoveAndResize(1, BOTTOM(this->outputDropdown), cx - 1, LABEL_HEIGHT);
|
||||
this->dotfileCheckbox->MoveAndResize(1, BOTTOM(this->transportDropdown), cx - 1, LABEL_HEIGHT);
|
||||
this->removeCheckbox->MoveAndResize(1, BOTTOM(this->dotfileCheckbox), cx - 1, LABEL_HEIGHT);
|
||||
this->customColorsCheckbox->MoveAndResize(1, BOTTOM(this->dotfileCheckbox), cx - 1, LABEL_HEIGHT);
|
||||
y = BOTTOM(this->browseList);
|
||||
this->outputDropdown->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT);
|
||||
this->transportDropdown->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT);
|
||||
this->pluginsDropdown->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT);
|
||||
this->dotfileCheckbox->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT);
|
||||
this->removeCheckbox->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT);
|
||||
this->customColorsCheckbox->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT);
|
||||
|
||||
this->hotkeyLabel->MoveAndResize(
|
||||
1,
|
||||
BOTTOM(this->customColorsCheckbox) + 2,
|
||||
this->hotkeyLabel->Length(),
|
||||
LABEL_HEIGHT);
|
||||
|
||||
this->hotkeyInput->MoveAndResize(
|
||||
RIGHT(this->hotkeyLabel),
|
||||
BOTTOM(this->customColorsCheckbox) + 1,
|
||||
HOTKEY_INPUT_WIDTH,
|
||||
INPUT_HEIGHT);
|
||||
this->hotkeyLabel->MoveAndResize(1, y + 1, this->hotkeyLabel->Length(), LABEL_HEIGHT);
|
||||
this->hotkeyInput->MoveAndResize(RIGHT(this->hotkeyLabel), y, HOTKEY_INPUT_WIDTH, INPUT_HEIGHT);
|
||||
}
|
||||
|
||||
void SettingsLayout::RefreshAddedPaths() {
|
||||
@ -247,6 +245,10 @@ void SettingsLayout::InitializeWindows() {
|
||||
this->transportDropdown.reset(new TextLabel());
|
||||
this->transportDropdown->Activated.connect(this, &SettingsLayout::OnTransportDropdownActivate);
|
||||
|
||||
this->pluginsDropdown.reset(new TextLabel());
|
||||
this->pluginsDropdown->SetText(arrow + " enable/disable plugins");
|
||||
this->pluginsDropdown->Activated.connect(this, &SettingsLayout::OnPluginsDropdownActivate);
|
||||
|
||||
CREATE_CHECKBOX(this->dotfileCheckbox, "show dotfiles in directory browser");
|
||||
CREATE_CHECKBOX(this->removeCheckbox, "remove missing files from library");
|
||||
CREATE_CHECKBOX(this->customColorsCheckbox, "disable custom colors (requires restart)");
|
||||
@ -258,11 +260,12 @@ void SettingsLayout::InitializeWindows() {
|
||||
this->browseList->SetFocusOrder(0);
|
||||
this->addedPathsList->SetFocusOrder(1);
|
||||
this->outputDropdown->SetFocusOrder(2);
|
||||
this->transportDropdown->SetFocusOrder(4);
|
||||
this->transportDropdown->SetFocusOrder(3);
|
||||
this->pluginsDropdown->SetFocusOrder(4);
|
||||
this->dotfileCheckbox->SetFocusOrder(5);
|
||||
this->removeCheckbox->SetFocusOrder(6);
|
||||
this->customColorsCheckbox->SetFocusOrder(8);
|
||||
this->hotkeyInput->SetFocusOrder(9);
|
||||
this->customColorsCheckbox->SetFocusOrder(7);
|
||||
this->hotkeyInput->SetFocusOrder(8);
|
||||
|
||||
this->AddWindow(this->browseLabel);
|
||||
this->AddWindow(this->addedPathsLabel);
|
||||
@ -270,6 +273,7 @@ void SettingsLayout::InitializeWindows() {
|
||||
this->AddWindow(this->addedPathsList);
|
||||
this->AddWindow(this->outputDropdown);
|
||||
this->AddWindow(this->transportDropdown);
|
||||
this->AddWindow(this->pluginsDropdown);
|
||||
this->AddWindow(this->dotfileCheckbox);
|
||||
this->AddWindow(this->removeCheckbox);
|
||||
this->AddWindow(this->customColorsCheckbox);
|
||||
|
@ -96,6 +96,7 @@ namespace musik {
|
||||
|
||||
void OnOutputDropdownActivated(cursespp::TextLabel* label);
|
||||
void OnTransportDropdownActivate(cursespp::TextLabel* label);
|
||||
void OnPluginsDropdownActivate(cursespp::TextLabel* label);
|
||||
|
||||
int64 ListItemDecorator(
|
||||
cursespp::ScrollableWindow* w,
|
||||
@ -112,6 +113,7 @@ namespace musik {
|
||||
|
||||
std::shared_ptr<cursespp::TextLabel> outputDropdown;
|
||||
std::shared_ptr<cursespp::TextLabel> transportDropdown;
|
||||
std::shared_ptr<cursespp::TextLabel> pluginsDropdown;
|
||||
|
||||
std::shared_ptr<cursespp::Checkbox> removeCheckbox;
|
||||
std::shared_ptr<cursespp::Checkbox> dotfileCheckbox;
|
||||
|
@ -231,7 +231,7 @@ void PlayQueueOverlays::ShowAddTrackOverlay(
|
||||
.SetSelectedIndex(0)
|
||||
.SetWidth(DEFAULT_OVERLAY_WIDTH)
|
||||
.SetItemSelectedCallback(
|
||||
[trackId, &playback](cursespp::IScrollAdapterPtr adapter, size_t index) {
|
||||
[trackId, &playback](ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) {
|
||||
auto editor = playback.Edit();
|
||||
if (index == 0) { /* end */
|
||||
editor.Add(trackId);
|
||||
@ -266,7 +266,7 @@ void PlayQueueOverlays::ShowAddCategoryOverlay(
|
||||
.SetWidth(DEFAULT_OVERLAY_WIDTH)
|
||||
.SetItemSelectedCallback(
|
||||
[&playback, library, fieldColumn, fieldId]
|
||||
(cursespp::IScrollAdapterPtr adapter, size_t index) {
|
||||
(ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) {
|
||||
if (index == ListWindow::NO_SELECTION) {
|
||||
return;
|
||||
}
|
||||
@ -321,7 +321,7 @@ void PlayQueueOverlays::ShowLoadPlaylistOverlay(
|
||||
"load playlist",
|
||||
adapter,
|
||||
[library, result, callback]
|
||||
(cursespp::IScrollAdapterPtr adapter, size_t index) {
|
||||
(ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) {
|
||||
if (index != ListWindow::NO_SELECTION && callback) {
|
||||
DBID playlistId = (*result)[index]->id;
|
||||
callback(playlistId);
|
||||
@ -358,7 +358,7 @@ void PlayQueueOverlays::ShowSavePlaylistOverlay(
|
||||
"save playlist",
|
||||
adapter,
|
||||
[&playback, library, result]
|
||||
(cursespp::IScrollAdapterPtr adapter, size_t index) {
|
||||
(ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) {
|
||||
std::shared_ptr<TrackList> tracks(new TrackList(library));
|
||||
playback.CopyTo(*tracks);
|
||||
|
||||
@ -391,7 +391,7 @@ void PlayQueueOverlays::ShowRenamePlaylistOverlay(musik::core::ILibraryPtr libra
|
||||
showPlaylistListOverlay(
|
||||
"rename playlist",
|
||||
adapter,
|
||||
[library, result](cursespp::IScrollAdapterPtr adapter, size_t index) {
|
||||
[library, result](ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) {
|
||||
if (index != ListWindow::NO_SELECTION) {
|
||||
DBID playlistId = (*result)[index]->id;
|
||||
std::string playlistName = (*result)[index]->displayValue;
|
||||
@ -417,7 +417,7 @@ void PlayQueueOverlays::ShowDeletePlaylistOverlay(musik::core::ILibraryPtr libra
|
||||
"delete playlist",
|
||||
adapter,
|
||||
[library, result]
|
||||
(cursespp::IScrollAdapterPtr adapter, size_t index) {
|
||||
(ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) {
|
||||
if (index != ListWindow::NO_SELECTION) {
|
||||
DBID playlistId = (*result)[index]->id;
|
||||
std::string playlistName = (*result)[index]->displayValue;
|
||||
|
@ -120,7 +120,7 @@ void PlaybackOverlays::ShowOutputOverlay(
|
||||
.SetTitle("output plugins")
|
||||
.SetSelectedIndex(selectedIndex)
|
||||
.SetItemSelectedCallback(
|
||||
[callback, transportType](cursespp::IScrollAdapterPtr adapter, size_t index) {
|
||||
[callback, transportType](ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) {
|
||||
|
||||
if (transportType == MasterTransport::Crossfade) {
|
||||
std::string output = outputs::GetAllOutputs().at(index)->Name();
|
||||
@ -160,7 +160,7 @@ void PlaybackOverlays::ShowTransportOverlay(
|
||||
.SetTitle("playback transport")
|
||||
.SetSelectedIndex(selectedIndex)
|
||||
.SetItemSelectedCallback(
|
||||
[callback](cursespp::IScrollAdapterPtr adapter, size_t index) {
|
||||
[callback](ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) {
|
||||
int result = (index == 0)
|
||||
? MasterTransport::Gapless
|
||||
: MasterTransport::Crossfade;
|
||||
|
150
src/musikbox/app/overlay/PluginOverlay.cpp
Normal file
150
src/musikbox/app/overlay/PluginOverlay.cpp
Normal file
@ -0,0 +1,150 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2007-2016 musikcube team
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "PluginOverlay.h"
|
||||
|
||||
#include <core/support/Preferences.h>
|
||||
#include <core/support/PreferenceKeys.h>
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
|
||||
#include <cursespp/App.h>
|
||||
#include <cursespp/Colors.h>
|
||||
#include <cursespp/ListOverlay.h>
|
||||
#include <cursespp/ScrollAdapterBase.h>
|
||||
#include <cursespp/SingleLineEntry.h>
|
||||
#include <cursespp/Text.h>
|
||||
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::sdk;
|
||||
using namespace musik::box;
|
||||
using namespace cursespp;
|
||||
|
||||
static const std::string check = "\xe2\x9c\x93";
|
||||
|
||||
struct PluginInfo {
|
||||
std::string name;
|
||||
std::string fn;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
using PluginInfoPtr = std::shared_ptr<PluginInfo>;
|
||||
using PluginList = std::vector<PluginInfoPtr>;
|
||||
using SinglePtr = std::shared_ptr<SingleLineEntry>;
|
||||
|
||||
class PluginAdapter : public ScrollAdapterBase {
|
||||
public:
|
||||
PluginAdapter() {
|
||||
this->prefs = Preferences::ForComponent(prefs::components::Plugins);
|
||||
this->Refresh();
|
||||
}
|
||||
|
||||
virtual ~PluginAdapter() {
|
||||
this->prefs->Save();
|
||||
}
|
||||
|
||||
void toggleEnabled(size_t index) {
|
||||
PluginInfoPtr plugin = this->plugins.at(index);
|
||||
plugin->enabled = !plugin->enabled;
|
||||
this->prefs->SetBool(plugin->fn, plugin->enabled);
|
||||
}
|
||||
|
||||
virtual size_t GetEntryCount() {
|
||||
return plugins.size();
|
||||
}
|
||||
|
||||
virtual EntryPtr GetEntry(cursespp::ScrollableWindow* window, size_t index) {
|
||||
PluginInfoPtr plugin = plugins.at(index);
|
||||
|
||||
std::string display =
|
||||
" " +
|
||||
(plugin->enabled ? check : " ") + " " +
|
||||
plugin->name + " (" + plugin->fn + ")";
|
||||
|
||||
SinglePtr result = SinglePtr(new SingleLineEntry(text::Ellipsize(display, this->GetWidth())));
|
||||
|
||||
result->SetAttrs(CURSESPP_DEFAULT_COLOR);
|
||||
|
||||
if (index == window->GetScrollPosition().logicalIndex) {
|
||||
result->SetAttrs(COLOR_PAIR(CURSESPP_HIGHLIGHTED_LIST_ITEM));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
void Refresh() {
|
||||
plugins.clear();
|
||||
|
||||
PluginList& plugins = this->plugins;
|
||||
auto prefs = this->prefs;
|
||||
|
||||
typedef PluginFactory::NullDeleter<IPlugin> Deleter;
|
||||
|
||||
PluginFactory::Instance().QueryInterface<IPlugin, Deleter>(
|
||||
"GetPlugin",
|
||||
[&plugins, prefs](std::shared_ptr<IPlugin> plugin, const std::string& fn) {
|
||||
PluginInfoPtr info(new PluginInfo());
|
||||
info->name = plugin->Name();
|
||||
info->fn = boost::filesystem::path(fn).filename().string();
|
||||
info->enabled = prefs->GetBool(info->fn, true);
|
||||
plugins.push_back(info);
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<Preferences> prefs;
|
||||
PluginList plugins;
|
||||
};
|
||||
|
||||
PluginOverlay::PluginOverlay() {
|
||||
|
||||
}
|
||||
|
||||
void PluginOverlay::Show() {
|
||||
std::shared_ptr<PluginAdapter> pluginAdapter(new PluginAdapter());
|
||||
std::shared_ptr<ListOverlay> dialog(new ListOverlay());
|
||||
|
||||
dialog->SetAdapter(pluginAdapter)
|
||||
.SetTitle("plugins")
|
||||
.SetWidthPercent(80)
|
||||
.SetAutoDismiss(false)
|
||||
.SetItemSelectedCallback(
|
||||
[pluginAdapter](ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) {
|
||||
pluginAdapter->toggleEnabled(index);
|
||||
overlay->RefreshAdapter();
|
||||
});
|
||||
|
||||
cursespp::App::Overlays().Push(dialog);
|
||||
}
|
47
src/musikbox/app/overlay/PluginOverlay.h
Normal file
47
src/musikbox/app/overlay/PluginOverlay.h
Normal file
@ -0,0 +1,47 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2007-2016 musikcube team
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace musik {
|
||||
namespace box {
|
||||
class PluginOverlay {
|
||||
public:
|
||||
static void Show();
|
||||
|
||||
private:
|
||||
PluginOverlay();
|
||||
};
|
||||
}
|
||||
}
|
@ -86,7 +86,7 @@ void VisualizerOverlay::Show() {
|
||||
dialog->SetAdapter(adapter)
|
||||
.SetTitle("visualizers")
|
||||
.SetItemSelectedCallback(
|
||||
[](cursespp::IScrollAdapterPtr adapter, size_t index) {
|
||||
[](ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) {
|
||||
vis::SetSelectedVisualizer(vis::GetVisualizer(index));
|
||||
vis::SelectedVisualizer()->Show();
|
||||
});
|
||||
|
@ -42,14 +42,16 @@ using namespace cursespp;
|
||||
|
||||
#define VERTICAL_PADDING 2
|
||||
#define DEFAULT_WIDTH 26
|
||||
#define MAX_HEIGHT 12
|
||||
|
||||
ListOverlay::ListOverlay() {
|
||||
this->SetFrameVisible(true);
|
||||
this->SetFrameColor(CURSESPP_OVERLAY_FRAME);
|
||||
this->SetContentColor(CURSESPP_OVERLAY_BACKGROUND);
|
||||
|
||||
this->width = this->height = this->setWidth = 0;
|
||||
this->autoDismiss = true;
|
||||
|
||||
this->width = this->height = 0;
|
||||
this->setWidth = this->setWidthPercent = 0;
|
||||
|
||||
this->listWindow.reset(new ListWindow());
|
||||
this->listWindow->SetContentColor(CURSESPP_OVERLAY_BACKGROUND);
|
||||
@ -92,6 +94,18 @@ ListOverlay& ListOverlay::SetTitle(const std::string& title) {
|
||||
|
||||
ListOverlay& ListOverlay::SetWidth(int width) {
|
||||
this->setWidth = width;
|
||||
this->setWidthPercent = 0;
|
||||
|
||||
if (this->IsVisible()) {
|
||||
this->Layout();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ListOverlay& ListOverlay::SetWidthPercent(int percent) {
|
||||
this->setWidthPercent = percent;
|
||||
this->setWidth = 0;
|
||||
|
||||
if (this->IsVisible()) {
|
||||
this->Layout();
|
||||
@ -109,6 +123,11 @@ ListOverlay& ListOverlay::SetAdapter(IScrollAdapterPtr adapter) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
ListOverlay& ListOverlay::SetAutoDismiss(bool autoDismiss) {
|
||||
this->autoDismiss = autoDismiss;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ListOverlay& ListOverlay::SetSelectedIndex(size_t index) {
|
||||
this->listWindow->SetSelectedIndex(index);
|
||||
return *this;
|
||||
@ -129,18 +148,22 @@ bool ListOverlay::KeyPress(const std::string& key) {
|
||||
this->Dismiss();
|
||||
return true;
|
||||
}
|
||||
else if (key == "KEY_ENTER") {
|
||||
else if (key == "KEY_ENTER" || key == " ") {
|
||||
if (itemSelectedCallback) {
|
||||
itemSelectedCallback(
|
||||
this,
|
||||
this->adapter,
|
||||
listWindow->GetSelectedIndex());
|
||||
}
|
||||
this->Dismiss();
|
||||
if (this->autoDismiss) {
|
||||
this->Dismiss();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (key == "KEY_BACKSPACE" || key == "KEY_DC") {
|
||||
if (deleteKeyCallback) {
|
||||
deleteKeyCallback(
|
||||
this,
|
||||
this->adapter,
|
||||
listWindow->GetSelectedIndex());
|
||||
|
||||
@ -159,12 +182,20 @@ void ListOverlay::OnVisibilityChanged(bool visible) {
|
||||
}
|
||||
|
||||
void ListOverlay::RecalculateSize() {
|
||||
this->width = this->setWidth > 0 ? this->setWidth : DEFAULT_WIDTH;
|
||||
if (this->setWidthPercent > 0) {
|
||||
int cx = Screen::GetWidth();
|
||||
this->width = (int)((this->setWidthPercent / 100.0f) * cx);
|
||||
}
|
||||
else {
|
||||
this->width = this->setWidth > 0 ? this->setWidth : DEFAULT_WIDTH;
|
||||
}
|
||||
|
||||
this->height = 4; /* top frame + text + space + bottom frame */
|
||||
const int maxHeight = Screen::GetHeight() - 4;
|
||||
|
||||
if (this->adapter) {
|
||||
this->height = std::min(
|
||||
(int) MAX_HEIGHT,
|
||||
maxHeight,
|
||||
(int) (4 + this->adapter->GetEntryCount()));
|
||||
}
|
||||
|
||||
@ -193,4 +224,9 @@ void ListOverlay::Redraw() {
|
||||
wattroff(c, A_BOLD);
|
||||
currentY += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ListOverlay::RefreshAdapter() {
|
||||
this->listWindow->OnAdapterChanged();
|
||||
}
|
@ -48,8 +48,8 @@ namespace cursespp {
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
using ItemSelectedCallback = std::function<void(IScrollAdapterPtr adapter, size_t index)>;
|
||||
using DeleteKeyCallback = std::function<void(IScrollAdapterPtr adapter, size_t index)>;
|
||||
using ItemSelectedCallback = std::function<void(ListOverlay* sender, IScrollAdapterPtr adapter, size_t index)>;
|
||||
using DeleteKeyCallback = std::function<void(ListOverlay* sender, IScrollAdapterPtr adapter, size_t index)>;
|
||||
|
||||
ListOverlay();
|
||||
virtual ~ListOverlay();
|
||||
@ -60,10 +60,14 @@ namespace cursespp {
|
||||
ListOverlay& SetDeleteKeyCallback(DeleteKeyCallback cb);
|
||||
ListOverlay& SetSelectedIndex(size_t index);
|
||||
ListOverlay& SetWidth(int width);
|
||||
ListOverlay& SetWidthPercent(int percent);
|
||||
ListOverlay& SetAutoDismiss(bool autoDismiss);
|
||||
|
||||
virtual void Layout();
|
||||
virtual bool KeyPress(const std::string& key);
|
||||
|
||||
void RefreshAdapter();
|
||||
|
||||
protected:
|
||||
virtual void OnVisibilityChanged(bool visible);
|
||||
|
||||
@ -74,7 +78,8 @@ namespace cursespp {
|
||||
std::string title;
|
||||
int x, y;
|
||||
int width, height;
|
||||
int setWidth;
|
||||
int setWidth, setWidthPercent;
|
||||
bool autoDismiss;
|
||||
IScrollAdapterPtr adapter;
|
||||
std::shared_ptr<ListWindow> listWindow;
|
||||
ItemSelectedCallback itemSelectedCallback;
|
||||
|
@ -134,6 +134,7 @@
|
||||
<ClCompile Include="app\model\DirectoryAdapter.cpp" />
|
||||
<ClCompile Include="app\overlay\PlaybackOverlays.cpp" />
|
||||
<ClCompile Include="app\overlay\PlayQueueOverlays.cpp" />
|
||||
<ClCompile Include="app\overlay\PluginOverlay.cpp" />
|
||||
<ClCompile Include="app\overlay\VisualizerOverlay.cpp" />
|
||||
<ClCompile Include="app\util\GlobalHotkeys.cpp" />
|
||||
<ClCompile Include="app\util\Hotkeys.cpp" />
|
||||
@ -184,6 +185,7 @@
|
||||
<ClInclude Include="app\model\DirectoryAdapter.h" />
|
||||
<ClInclude Include="app\overlay\PlaybackOverlays.h" />
|
||||
<ClInclude Include="app\overlay\PlayQueueOverlays.h" />
|
||||
<ClInclude Include="app\overlay\PluginOverlay.h" />
|
||||
<ClInclude Include="app\overlay\VisualizerOverlay.h" />
|
||||
<ClInclude Include="app\util\GlobalHotkeys.h" />
|
||||
<ClInclude Include="app\util\Hotkeys.h" />
|
||||
|
@ -129,6 +129,9 @@
|
||||
<ClCompile Include="cursespp\InputOverlay.cpp">
|
||||
<Filter>cursespp</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="app\overlay\PluginOverlay.cpp">
|
||||
<Filter>app\overlay</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h" />
|
||||
@ -303,6 +306,9 @@
|
||||
<ClInclude Include="cursespp\InputOverlay.h">
|
||||
<Filter>cursespp</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="app\overlay\PluginOverlay.h">
|
||||
<Filter>app\overlay</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="cursespp">
|
||||
|
Loading…
x
Reference in New Issue
Block a user