- 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:
casey langen 2017-02-14 20:55:37 -08:00
parent e8985aadd1
commit 9c6e136c79
17 changed files with 393 additions and 104 deletions

View File

@ -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) {

View File

@ -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);

View File

@ -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;
};
} }

View File

@ -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";

View File

@ -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 {

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View 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);
}

View 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();
};
}
}

View File

@ -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();
});

View File

@ -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();
}

View File

@ -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;

View File

@ -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" />

View File

@ -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">