diff --git a/VST/AudioGroupFilePresenter.cpp b/VST/AudioGroupFilePresenter.cpp new file mode 100644 index 0000000..3318ef9 --- /dev/null +++ b/VST/AudioGroupFilePresenter.cpp @@ -0,0 +1,310 @@ +#include "AudioGroupFilePresenter.hpp" +#include "VSTBackend.hpp" + +namespace amuse +{ + +bool AudioGroupDataCollection::loadProj() +{ + std::wstring path = m_path + L"\\proj"; + athena::io::FileReader r(path, 1024 * 32, false); + if (r.hasError()) + return false; + std::vector& ret = m_projData; + size_t len = r.length(); + ret.resize(len); + r.readUBytesToBuf(ret.data(), len); + return ret.size() != 0; +} + +bool AudioGroupDataCollection::loadPool() +{ + std::wstring path = m_path + L"\\pool"; + athena::io::FileReader r(path, 1024 * 32, false); + if (r.hasError()) + return false; + std::vector& ret = m_poolData; + size_t len = r.length(); + ret.resize(len); + r.readUBytesToBuf(ret.data(), len); + return ret.size() != 0; +} + +bool AudioGroupDataCollection::loadSdir() +{ + std::wstring path = m_path + L"\\sdir"; + athena::io::FileReader r(path, 1024 * 32, false); + if (r.hasError()) + return false; + std::vector& ret = m_sdirData; + size_t len = r.length(); + ret.resize(len); + r.readUBytesToBuf(ret.data(), len); + return ret.size() != 0; +} + +bool AudioGroupDataCollection::loadSamp() +{ + std::wstring path = m_path + L"\\samp"; + athena::io::FileReader r(path, 1024 * 32, false); + if (r.hasError()) + return false; + std::vector& ret = m_sampData; + size_t len = r.length(); + ret.resize(len); + r.readUBytesToBuf(ret.data(), len); + return ret.size() != 0; +} + +bool AudioGroupDataCollection::loadMeta() +{ + std::wstring path = m_path + L"\\meta"; + athena::io::FileReader r(path, 1024 * 32, false); + if (r.hasError()) + return false; + std::experimental::optional& ret = m_metaData; + ret.emplace(r); + return ret.operator bool(); +} + +AudioGroupDataCollection::AudioGroupDataCollection(const std::wstring& path, const std::wstring& name) +: m_path(path), m_name(name) +{ + +} + +bool AudioGroupDataCollection::_attemptLoad(AudioGroupFilePresenter& presenter) +{ + if (m_metaData && m_loadedData && m_loadedGroup) + return true; + if (!loadProj()) + return false; + if (!loadPool()) + return false; + if (!loadSdir()) + return false; + if (!loadSamp()) + return false; + if (!loadMeta()) + return false; + + return _indexData(presenter); +} + +bool AudioGroupDataCollection::_indexData(AudioGroupFilePresenter& presenter) +{ + amuse::Engine& engine = presenter.getBackend().getAmuseEngine(); + + switch (m_metaData->fmt) + { + case amuse::DataFormat::GCN: + default: + m_loadedData.emplace(m_projData.data(), m_projData.size(), + m_poolData.data(), m_poolData.size(), + m_sdirData.data(), m_sdirData.size(), + m_sampData.data(), m_sampData.size(), + amuse::GCNDataTag{}); + break; + case amuse::DataFormat::N64: + m_loadedData.emplace(m_projData.data(), m_projData.size(), + m_poolData.data(), m_poolData.size(), + m_sdirData.data(), m_sdirData.size(), + m_sampData.data(), m_sampData.size(), + m_metaData->absOffs, amuse::N64DataTag{}); + break; + case amuse::DataFormat::PC: + m_loadedData.emplace(m_projData.data(), m_projData.size(), + m_poolData.data(), m_poolData.size(), + m_sdirData.data(), m_sdirData.size(), + m_sampData.data(), m_sampData.size(), + m_metaData->absOffs, amuse::PCDataTag{}); + break; + } + + m_loadedGroup = engine.addAudioGroup(*m_loadedData); + m_groupTokens.clear(); + if (m_loadedGroup) + { + m_groupTokens.reserve(m_loadedGroup->getProj().songGroups().size() + + m_loadedGroup->getProj().sfxGroups().size()); + + { + const auto& songGroups = m_loadedGroup->getProj().songGroups(); + std::map sortGroups; + for (const auto& pair : songGroups) + sortGroups[pair.first] = &pair.second; + for (const auto& pair : sortGroups) + m_groupTokens.emplace_back(pair.first, pair.second); + } + { + const auto& sfxGroups = m_loadedGroup->getProj().sfxGroups(); + std::map sortGroups; + for (const auto& pair : sfxGroups) + sortGroups[pair.first] = &pair.second; + for (const auto& pair : sortGroups) + m_groupTokens.emplace_back(pair.first, pair.second); + } + } + + return m_loadedData && m_loadedGroup; +} + +AudioGroupCollection::AudioGroupCollection(const std::wstring& path, const std::wstring& name) +: m_path(path), m_name(name) +{ + +} + +void AudioGroupCollection::addCollection(AudioGroupFilePresenter& presenter, + std::vector>&& collection) +{ + for (std::pair& pair : collection) + { + std::wstring collectionPath = m_path + L'\\' + pair.first; + + amuse::IntrusiveAudioGroupData& dataIn = pair.second; + auto search = m_groups.find(pair.first); + if (search == m_groups.end()) + { + search = m_groups.emplace(pair.first, + std::make_unique(collectionPath, + pair.first)).first; + } + + AudioGroupDataCollection& dataCollection = *search->second; + dataCollection.m_projData.resize(dataIn.getProjSize()); + memmove(dataCollection.m_projData.data(), dataIn.getProj(), dataIn.getProjSize()); + + dataCollection.m_poolData.resize(dataIn.getPoolSize()); + memmove(dataCollection.m_poolData.data(), dataIn.getPool(), dataIn.getPoolSize()); + + dataCollection.m_sdirData.resize(dataIn.getSdirSize()); + memmove(dataCollection.m_sdirData.data(), dataIn.getSdir(), dataIn.getSdirSize()); + + dataCollection.m_sampData.resize(dataIn.getSampSize()); + memmove(dataCollection.m_sampData.data(), dataIn.getSamp(), dataIn.getSampSize()); + + dataCollection.m_metaData.emplace(dataIn.getDataFormat(), dataIn.getAbsoluteProjOffsets(), true); + dataCollection._indexData(presenter); + } +} + +void AudioGroupCollection::update(AudioGroupFilePresenter& presenter) +{ + std::wstring path = m_path + L"\\*"; + + WIN32_FIND_DATAW d; + HANDLE dir = FindFirstFileW(path.c_str(), &d); + if (dir == INVALID_HANDLE_VALUE) + return; + do + { + if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L"..")) + continue; + + if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + std::wstring nameStr(d.cFileName); + auto search = m_groups.find(nameStr); + if (search == m_groups.end()) + { + search = + m_groups.emplace(nameStr, + std::make_unique(m_path + L'\\' + nameStr, + nameStr)).first; + search->second->_attemptLoad(presenter); + } + } + } while (FindNextFileW(dir, &d)); + + FindClose(dir); +} + +void AudioGroupFilePresenter::update() +{ + std::wstring path = m_backend.getUserDir() + L"\\*"; + std::map>& theMap = m_audioGroupCollections; + + WIN32_FIND_DATAW d; + HANDLE dir = FindFirstFileW(path.c_str(), &d); + if (dir == INVALID_HANDLE_VALUE) + return; + do + { + if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L"..")) + continue; + + if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + std::wstring nameStr(d.cFileName); + auto search = theMap.find(nameStr); + if (search == theMap.end()) + { + search = theMap.emplace(nameStr, + std::make_unique(m_backend.getUserDir() + L'\\' + nameStr, nameStr)).first; + search->second->update(*this); + } + } + } while (FindNextFileW(dir, &d)); + + FindClose(dir); +} + +void AudioGroupFilePresenter::addCollection(const std::wstring& name, + std::vector>&& collection) +{ + std::wstring path = m_backend.getUserDir() + L'\\' + name; + AudioGroupCollection& insert = *m_audioGroupCollections.emplace(name, std::make_unique(path, name)).first->second; + insert.addCollection(*this, std::move(collection)); + + for (std::pair>& pair : insert.m_groups) + { + std::wstring collectionPath = insert.m_path + L'\\' + pair.first; + CreateDirectory(collectionPath.c_str(), nullptr); + + FILE* fp = _wfopen((collectionPath + L"\\proj").c_str(), L"wb"); + if (fp) + { + fwrite(pair.second->m_projData.data(), 1, pair.second->m_projData.size(), fp); + fclose(fp); + } + + fp = _wfopen((collectionPath + L"\\pool").c_str(), L"wb"); + if (fp) + { + fwrite(pair.second->m_poolData.data(), 1, pair.second->m_poolData.size(), fp); + fclose(fp); + } + + fp = _wfopen((collectionPath + L"\\sdir").c_str(), L"wb"); + if (fp) + { + fwrite(pair.second->m_sdirData.data(), 1, pair.second->m_sdirData.size(), fp); + fclose(fp); + } + + fp = _wfopen((collectionPath + L"\\samp").c_str(), L"wb"); + if (fp) + { + fwrite(pair.second->m_sampData.data(), 1, pair.second->m_sampData.size(), fp); + fclose(fp); + } + + fp = _wfopen((collectionPath + L"\\meta").c_str(), L"wb"); + if (fp) + { + fwrite(&*pair.second->m_metaData, 1, sizeof(*pair.second->m_metaData), fp); + fclose(fp); + } + } +} + +void AudioGroupFilePresenter::populateEditor(VSTEditor& editor) +{ + for (const auto& cgollection : m_audioGroupCollections) + { + + } +} + +} diff --git a/VST/AudioGroupFilePresenter.hpp b/VST/AudioGroupFilePresenter.hpp new file mode 100644 index 0000000..36251e7 --- /dev/null +++ b/VST/AudioGroupFilePresenter.hpp @@ -0,0 +1,92 @@ +#ifndef __AMUSE_AUDIOGROUPFILEPRESENTER_HPP__ +#define __AMUSE_AUDIOGROUPFILEPRESENTER_HPP__ + +#include +#include +#include "optional.hpp" +#include +#include + +namespace amuse +{ +class VSTBackend; +class VSTEditor; +class AudioGroupFilePresenter; + +struct AudioGroupDataCollection +{ + std::wstring m_path; + std::wstring m_name; + + std::vector m_projData; + std::vector m_poolData; + std::vector m_sdirData; + std::vector m_sampData; + + struct MetaData + { + amuse::DataFormat fmt; + uint32_t absOffs; + uint32_t active; + MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn) + : fmt(fmtIn), absOffs(absOffsIn), active(activeIn) {} + MetaData(athena::io::FileReader& r) + : fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little()) {} + }; + std::experimental::optional m_metaData; + + std::experimental::optional m_loadedData; + const amuse::AudioGroup* m_loadedGroup; + struct GroupToken + { + int m_groupId; + const amuse::SongGroupIndex* m_song = nullptr; + const amuse::SFXGroupIndex* m_sfx = nullptr; + GroupToken(int id, const amuse::SongGroupIndex* song) : m_groupId(id), m_song(song) {} + GroupToken(int id, const amuse::SFXGroupIndex* sfx) : m_groupId(id), m_sfx(sfx) {} + }; + std::vector m_groupTokens; + + bool loadProj(); + bool loadPool(); + bool loadSdir(); + bool loadSamp(); + bool loadMeta(); + + AudioGroupDataCollection(const std::wstring& path, const std::wstring& name); + bool isDataComplete() const {return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;} + bool _attemptLoad(AudioGroupFilePresenter& presenter); + bool _indexData(AudioGroupFilePresenter& presenter); +}; + +struct AudioGroupCollection +{ + std::wstring m_path; + std::wstring m_name; + + std::map> m_groups; + + AudioGroupCollection(const std::wstring& path, const std::wstring& name); + void addCollection(AudioGroupFilePresenter& presenter, + std::vector>&& collection); + void update(AudioGroupFilePresenter& presenter); + + //void populate +}; + +class AudioGroupFilePresenter +{ + VSTBackend& m_backend; + std::map> m_audioGroupCollections; +public: + AudioGroupFilePresenter(VSTBackend& backend) : m_backend(backend) {} + void update(); + void populateEditor(VSTEditor& editor); + void addCollection(const std::wstring& name, + std::vector>&& collection); + VSTBackend& getBackend() {return m_backend;} +}; + +} + +#endif // __AMUSE_AUDIOGROUPFILEPRESENTER_HPP__ diff --git a/VST/CMakeLists.txt b/VST/CMakeLists.txt index 5bdfce1..621d858 100644 --- a/VST/CMakeLists.txt +++ b/VST/CMakeLists.txt @@ -7,10 +7,12 @@ if (WIN32 AND (EXISTS ${VST3_SDK_ROOT})) add_library(amuse-vst SHARED VSTBackend.hpp VSTBackend.cpp VSTEditor.hpp VSTEditor.cpp + AudioGroupFilePresenter.hpp AudioGroupFilePresenter.cpp ${VST2_DIR}/vstplugmain.cpp ${VST2_DIR}/audioeffect.cpp - ${VST2_DIR}/audioeffectx.cpp) + ${VST2_DIR}/audioeffectx.cpp + FileOpenDialog.hpp FileOpenDialog.cpp) target_link_libraries(amuse-vst amuse boo soxr ${ZLIB_LIBRARIES} ${BOO_SYS_LIBS} - Msimg32 logvisor athena-core) + Msimg32 Shlwapi logvisor athena-core) set_target_properties(amuse-vst PROPERTIES LINK_FLAGS "/EXPORT:VSTPluginMain") endif() diff --git a/VST/FileOpenDialog.cpp b/VST/FileOpenDialog.cpp new file mode 100644 index 0000000..557fff9 --- /dev/null +++ b/VST/FileOpenDialog.cpp @@ -0,0 +1,238 @@ +#include "FileOpenDialog.hpp" + +#define WIN32_LEAN_AND_MEAN +#include // For common windows data types and function headers +#define STRICT_TYPED_ITEMIDS +#include // For COM headers +#include // for IFileDialogEvents and IFileDialogControlEvents +#include +#include // for KnownFolder APIs/datatypes/function headers +#include // for PROPVAR-related functions +#include // for the Property key APIs/datatypes +#include // for the Property System APIs +#include // for StringCchPrintfW +#include // for COMDLG_FILTERSPEC +#include + +// Controls +#define CONTROL_GROUP 2000 +#define CONTROL_RADIOBUTTONLIST 2 +#define CONTROL_RADIOBUTTON1 1 +#define CONTROL_RADIOBUTTON2 2 // It is OK for this to have the same IDas CONTROL_RADIOBUTTONLIST, + // because it is a child control under CONTROL_RADIOBUTTONLIST + +// IDs for the Task Dialog Buttons +#define IDC_BASICFILEOPEN 100 +#define IDC_ADDITEMSTOCUSTOMPLACES 101 +#define IDC_ADDCUSTOMCONTROLS 102 +#define IDC_SETDEFAULTVALUESFORPROPERTIES 103 +#define IDC_WRITEPROPERTIESUSINGHANDLERS 104 +#define IDC_WRITEPROPERTIESWITHOUTUSINGHANDLERS 105 + +HWND ghMainWnd = 0; +HINSTANCE ghAppInst = 0; +RECT winRect; + +class CDialogEventHandler : public IFileDialogEvents, + public IFileDialogControlEvents +{ +public: + // IUnknown methods + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) + { + static const QITAB qit[] = { + QITABENT(CDialogEventHandler, IFileDialogEvents), + QITABENT(CDialogEventHandler, IFileDialogControlEvents), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); + } + + IFACEMETHODIMP_(ULONG) AddRef() + { + return InterlockedIncrement(&_cRef); + } + + IFACEMETHODIMP_(ULONG) Release() + { + long cRef = InterlockedDecrement(&_cRef); + if (!cRef) + delete this; + return cRef; + } + + // IFileDialogEvents methods + IFACEMETHODIMP OnFileOk(IFileDialog *) { return S_OK; }; + IFACEMETHODIMP OnFolderChange(IFileDialog *) { return S_OK; }; + IFACEMETHODIMP OnFolderChanging(IFileDialog *, IShellItem *) { return S_OK; }; + IFACEMETHODIMP OnHelp(IFileDialog *) { return S_OK; }; + IFACEMETHODIMP OnSelectionChange(IFileDialog *) { return S_OK; }; + IFACEMETHODIMP OnShareViolation(IFileDialog *, IShellItem *, FDE_SHAREVIOLATION_RESPONSE *) { return S_OK; }; + IFACEMETHODIMP OnTypeChange(IFileDialog *pfd); + IFACEMETHODIMP OnOverwrite(IFileDialog *, IShellItem *, FDE_OVERWRITE_RESPONSE *) { return S_OK; }; + + // IFileDialogControlEvents methods + IFACEMETHODIMP OnItemSelected(IFileDialogCustomize *pfdc, DWORD dwIDCtl, DWORD dwIDItem); + IFACEMETHODIMP OnButtonClicked(IFileDialogCustomize *, DWORD) { return S_OK; }; + IFACEMETHODIMP OnCheckButtonToggled(IFileDialogCustomize *, DWORD, BOOL) { return S_OK; }; + IFACEMETHODIMP OnControlActivating(IFileDialogCustomize *, DWORD) { return S_OK; }; + + CDialogEventHandler() : _cRef(1) { }; +private: + ~CDialogEventHandler() { }; + long _cRef; +}; + +HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void **ppv); + +std::wstring openDB() +{ + std::wstring ret; + + //Cocreate the file open dialog object + IFileDialog *pfd = NULL; + + HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd)); + + if (SUCCEEDED(hr)) + { + //Stuff needed for later + const COMDLG_FILTERSPEC rgFExt[] = {{L"Audio Group Archive (*.*)", L"*.*"}}; + + //Create event handling + IFileDialogEvents *pfde = NULL; + hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde)); + + if(SUCCEEDED(hr)) + { + //Hook the event handler + DWORD dwCookie; + + hr = pfd->Advise(pfde, &dwCookie); + + if (SUCCEEDED(hr)) + { + //Set options for the dialog + DWORD dwFlags; + + //Get options first so we do not override + hr = pfd->GetOptions(&dwFlags); + + if (SUCCEEDED(hr)) + { + //Get shell items only + hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM); + + if (SUCCEEDED(hr)) + { + //Types of files to display (not default) + hr = pfd->SetFileTypes(ARRAYSIZE(rgFExt), rgFExt); + + if (SUCCEEDED(hr)) + { + //Set default file type to display + //hr = pfd->SetDefaultExtension(L"sqlite"); + + //if (SUCCEEDED(hr)) + //{ + //Show dialog + hr = pfd->Show(NULL); + + if (SUCCEEDED(hr)) + { + //Get the result once the user clicks on open + IShellItem *result; + + hr = pfd->GetResult(&result); + + if (SUCCEEDED(hr)) + { + //Print out the file name + PWSTR fName = NULL; + + hr = result->GetDisplayName(SIGDN_FILESYSPATH, &fName); + + if (SUCCEEDED(hr)) + { + ret.assign(fName); + CoTaskMemFree(fName); + } + + result->Release(); + } + } + //} + } + } + } + } + + pfd->Unadvise(dwCookie); + } + + pfde->Release(); + } + + pfd->Release(); + return ret; +} + + +HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void **ppv) +{ + *ppv = NULL; + CDialogEventHandler *pDialogEventHandler = new (std::nothrow) CDialogEventHandler(); + HRESULT hr = pDialogEventHandler ? S_OK : E_OUTOFMEMORY; + if (SUCCEEDED(hr)) + { + hr = pDialogEventHandler->QueryInterface(riid, ppv); + pDialogEventHandler->Release(); + } + return hr; +} + +HRESULT CDialogEventHandler::OnTypeChange(IFileDialog *pfd) +{ + IFileSaveDialog *pfsd; + HRESULT hr = pfd->QueryInterface(&pfsd); + if (SUCCEEDED(hr)) + { + UINT uIndex; + hr = pfsd->GetFileTypeIndex(&uIndex); // index of current file-type + if (SUCCEEDED(hr)) + { + IPropertyDescriptionList *pdl = NULL; + + + } + pfsd->Release(); + } + return hr; +} + +// IFileDialogControlEvents +// This method gets called when an dialog control item selection happens (radio-button selection. etc). +// For sample sake, let's react to this event by changing the dialog title. +HRESULT CDialogEventHandler::OnItemSelected(IFileDialogCustomize *pfdc, DWORD dwIDCtl, DWORD dwIDItem) +{ + IFileDialog *pfd = NULL; + HRESULT hr = pfdc->QueryInterface(&pfd); + if (SUCCEEDED(hr)) + { + if (dwIDCtl == CONTROL_RADIOBUTTONLIST) + { + switch (dwIDItem) + { + case CONTROL_RADIOBUTTON1: + hr = pfd->SetTitle(L"Longhorn Dialog"); + break; + + case CONTROL_RADIOBUTTON2: + hr = pfd->SetTitle(L"Vista Dialog"); + break; + } + } + pfd->Release(); + } + return hr; +} diff --git a/VST/FileOpenDialog.hpp b/VST/FileOpenDialog.hpp new file mode 100644 index 0000000..e0a8a49 --- /dev/null +++ b/VST/FileOpenDialog.hpp @@ -0,0 +1,8 @@ +#ifndef __AMUSE_FILEOPENDIALOG_HPP__ +#define __AMUSE_FILEOPENDIALOG_HPP__ + +#include + +std::wstring openDB(); + +#endif // __AMUSE_FILEOPENDIALOG_HPP__ diff --git a/VST/VSTBackend.cpp b/VST/VSTBackend.cpp index bb99c15..c8bb089 100644 --- a/VST/VSTBackend.cpp +++ b/VST/VSTBackend.cpp @@ -1,5 +1,6 @@ #include "VSTBackend.hpp" #include "audiodev/AudioVoiceEngine.hpp" +#include #include struct VSTVoiceEngine : boo::BaseAudioVoiceEngine @@ -141,6 +142,13 @@ VSTBackend::VSTBackend(audioMasterCallback cb) m_booBackend = std::make_unique(); m_voxAlloc.emplace(*m_booBackend); m_engine.emplace(*m_voxAlloc); + + WCHAR path[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, path))) + { + m_userDir = std::wstring(path) + L"\\Amuse"; + CreateDirectory(m_userDir.c_str(), nullptr); + } } VSTBackend::~VSTBackend() diff --git a/VST/VSTBackend.hpp b/VST/VSTBackend.hpp index 1210b30..0c6dbde 100644 --- a/VST/VSTBackend.hpp +++ b/VST/VSTBackend.hpp @@ -31,6 +31,7 @@ class VSTBackend : public AudioEffectX std::experimental::optional m_voxAlloc; std::experimental::optional m_engine; size_t m_curFrame = 0; + std::wstring m_userDir; VSTEditor m_editor; public: VSTBackend(audioMasterCallback cb); @@ -48,6 +49,9 @@ public: VstInt32 getNumMidiInputChannels(); void setSampleRate(float sampleRate); void setBlockSize(VstInt32 blockSize); + + amuse::Engine& getAmuseEngine() {return *m_engine;} + const std::wstring& getUserDir() const {return m_userDir;} }; } diff --git a/VST/VSTEditor.cpp b/VST/VSTEditor.cpp index aa2bebe..e12cf46 100644 --- a/VST/VSTEditor.cpp +++ b/VST/VSTEditor.cpp @@ -1,5 +1,8 @@ #include "VSTEditor.hpp" #include "VSTBackend.hpp" +#include "FileOpenDialog.hpp" +#include +#include extern void* hInstance; static WNDPROC OriginalListViewProc = 0; @@ -45,6 +48,31 @@ LRESULT CALLBACK VSTEditor::WindowProc(HWND hwnd, editor.selectPage(itemAct.iItem); return 0; } + case TVN_GETDISPINFO: + { + NMTVDISPINFO& treeDispInfo = *reinterpret_cast(lParam); + if (treeDispInfo.item.mask & TVIF_CHILDREN) + { + } + return 0; + } + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + } + case WM_COMMAND: + { + switch (HIWORD(wParam)) + { + case BN_CLICKED: + { + HWND button = HWND(lParam); + if (button == editor.m_collectionAdd) + editor.addAction(); + else if (button == editor.m_collectionRemove) + editor.removeAction(); + return 0; + } default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } @@ -137,34 +165,56 @@ bool VSTEditor::open(void* ptr) SetWindowLongPtrW(m_rootView, 0, LONG_PTR(this)); ShowWindow(m_rootView, SW_SHOW); + TVINSERTSTRUCT treeItem = {}; + treeItem.hParent = TVI_ROOT; + treeItem.hInsertAfter = TVI_LAST; + + treeItem.item.mask = TVIF_CHILDREN | TVIF_TEXT; + treeItem.item.cChildren = 1; + treeItem.item.pszText = L"Root A"; + LVCOLUMN column = {}; column.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; column.fmt = LVCFMT_LEFT | LVCFMT_FIXED_WIDTH; - column.cx = 200; + column.cx = 199; LVITEM item = {}; - item.mask = LVIF_TEXT | LVIF_GROUPID; + item.mask = LVIF_TEXT; item.pszText = L"Test"; - item.iGroupId = 1; m_collectionTree = CreateWindowW(WC_TREEVIEW, L"", - WS_CHILD | WS_BORDER | TVS_HASLINES, - 0, 24, - 201, - m_windowRect.bottom - m_windowRect.top - 24, + WS_CHILD | WS_CLIPSIBLINGS | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS, + 1, 25, + 199, + m_windowRect.bottom - m_windowRect.top - 26, m_rootView, nullptr, nullptr, nullptr); TreeView_SetBkColor(m_collectionTree, RGB(64,64,64)); + TreeView_SetTextColor(m_collectionTree, RGB(255,255,255)); + HTREEITEM rootItemA = TreeView_InsertItem(m_collectionTree, &treeItem); + treeItem.item.pszText = L"Root B"; + HTREEITEM rootItemB = TreeView_InsertItem(m_collectionTree, &treeItem); + treeItem.hParent = rootItemA; + treeItem.item.cChildren = 0; + treeItem.item.pszText = L"Child A"; + TreeView_InsertItem(m_collectionTree, &treeItem); + treeItem.item.pszText = L"Child B"; + TreeView_InsertItem(m_collectionTree, &treeItem); + treeItem.hParent = rootItemB; + treeItem.item.pszText = L"Child A"; + TreeView_InsertItem(m_collectionTree, &treeItem); + treeItem.item.pszText = L"Child B"; + TreeView_InsertItem(m_collectionTree, &treeItem); ShowWindow(m_collectionTree, SW_SHOW); m_collectionHeader = CreateWindowW(WC_HEADER, L"", WS_CHILD, 1, 1, - 200, + 199, 24, m_rootView, nullptr, @@ -174,12 +224,39 @@ bool VSTEditor::open(void* ptr) OriginalListViewProc = WNDPROC(SetWindowLongPtr(m_collectionHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc))); ShowWindow(m_collectionHeader, SW_SHOW); + m_collectionAdd = CreateWindowW(WC_BUTTON, + L"+", + WS_CHILD | WS_CLIPSIBLINGS | BS_PUSHBUTTON, + 1, m_windowRect.bottom - m_windowRect.top - 26, + 25, 24, + m_rootView, + nullptr, + nullptr, + nullptr); + SetWindowFont(m_collectionAdd, GetStockObject(ANSI_FIXED_FONT), FALSE); + Button_Enable(m_collectionAdd, TRUE); + SetWindowPos(m_collectionAdd, HWND_TOP, 1, m_windowRect.bottom - m_windowRect.top - 26, 25, 24, SWP_SHOWWINDOW); + + m_collectionRemove = CreateWindowW(WC_BUTTON, + L"-", + WS_CHILD | WS_CLIPSIBLINGS | BS_PUSHBUTTON, + 26, m_windowRect.bottom - m_windowRect.top - 26, + 25, 24, + m_rootView, + nullptr, + nullptr, + nullptr); + SetWindowFont(m_collectionRemove, GetStockObject(ANSI_FIXED_FONT), FALSE); + Button_Enable(m_collectionRemove, TRUE); + SetWindowPos(m_collectionRemove, HWND_TOP, 26, m_windowRect.bottom - m_windowRect.top - 26, 25, 24, SWP_SHOWWINDOW); + + m_groupListView = CreateWindowW(WC_LISTVIEW, L"", - WS_CHILD | WS_BORDER | LVS_REPORT | LVS_SINGLESEL | LVS_NOSORTHEADER, - 200, 0, - 201, - m_windowRect.bottom - m_windowRect.top, + WS_CHILD | LVS_REPORT | LVS_SINGLESEL | LVS_NOSORTHEADER, + 201, 1, + 199, + m_windowRect.bottom - m_windowRect.top - 2, m_rootView, nullptr, nullptr, @@ -189,25 +266,32 @@ bool VSTEditor::open(void* ptr) SetWindowLongPtrW(header, GWLP_USERDATA, LONG_PTR(column.pszText)); SetWindowLongPtr(header, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)); ListView_SetBkColor(m_groupListView, RGB(64,64,64)); + ListView_SetTextBkColor(m_groupListView, CLR_NONE); + ListView_SetTextColor(m_groupListView, RGB(255,255,255)); ListView_InsertColumn(m_groupListView, 0, &column); + ListView_InsertItem(m_groupListView, &item); ShowWindow(m_groupListView, SW_SHOW); m_pageListView = CreateWindowW(WC_LISTVIEW, L"", - WS_CHILD | WS_BORDER | LVS_REPORT | LVS_SINGLESEL | LVS_NOSORTHEADER, - 400, 0, - 200, - m_windowRect.bottom - m_windowRect.top, + WS_CHILD | LVS_REPORT | LVS_SINGLESEL | LVS_NOSORTHEADER, + 401, 1, + 198, + m_windowRect.bottom - m_windowRect.top - 2, m_rootView, nullptr, nullptr, nullptr); column.pszText = L"Page"; + column.cx = 198; header = ListView_GetHeader(m_pageListView); SetWindowLongPtrW(header, GWLP_USERDATA, LONG_PTR(column.pszText)); SetWindowLongPtr(header, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)); ListView_SetBkColor(m_pageListView, RGB(64,64,64)); + ListView_SetTextBkColor(m_pageListView, CLR_NONE); + ListView_SetTextColor(m_pageListView, RGB(255,255,255)); ListView_InsertColumn(m_pageListView, 0, &column); + ListView_InsertItem(m_pageListView, &item); ShowWindow(m_pageListView, SW_SHOW); return true; @@ -224,6 +308,31 @@ void VSTEditor::update() } +void VSTEditor::addAction() +{ + VstFileSelect fSelect = {}; + fSelect.command = kVstFileLoad; + fSelect.type = kVstFileType; + strcpy(fSelect.title, "Select Audio Group Archive"); + if (m_backend.openFileSelector(&fSelect)) + { + m_backend.closeFileSelector(&fSelect); + } + else + { + std::wstring path = openDB(); + if (path.size()) + { + + } + } +} + +void VSTEditor::removeAction() +{ + +} + void VSTEditor::selectCollection(int idx) { diff --git a/VST/VSTEditor.hpp b/VST/VSTEditor.hpp index 093f126..8a89762 100644 --- a/VST/VSTEditor.hpp +++ b/VST/VSTEditor.hpp @@ -14,12 +14,16 @@ class VSTBackend; /** Editor UI class */ class VSTEditor : public AEffEditor { + friend class AudioGroupFilePresenter; + VSTBackend& m_backend; ERect m_windowRect = {0, 0, 420, 600}; HWND m_rootView; HWND m_collectionHeader; HWND m_collectionTree; + HWND m_collectionAdd; + HWND m_collectionRemove; HWND m_groupListView; HWND m_pageListView; @@ -43,6 +47,9 @@ public: void close(); void update(); + void addAction(); + void removeAction(); + void selectCollection(int idx); void selectGroup(int idx); void selectPage(int idx);