From a44de6c0b542258aa3aa512a19330edc8fb0c094 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 23:52:08 +0100 Subject: [PATCH] Get win32 file dialog working again Signed-off-by: falkTX --- dgl/Window.hpp | 4 ++- dgl/src/WindowPrivateData.cpp | 67 +++++++++++++++++++++++++++++++++-- dgl/src/WindowPrivateData.hpp | 2 +- tests/FileBrowserDialog.cpp | 16 ++++++++- 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 46d7c1be..f1f07c85 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -317,8 +317,10 @@ public: Open a file browser dialog with this window as parent. A few options can be specified to setup the dialog. - This function does not block. If a path is selected, onFileSelected() will be called with the user chosen path. + If the user cancels or does not pick a file, onFileSelected() will be called with nullptr as filename. + + This function does not block the event loop. */ bool openFileBrowser(const FileBrowserOptions& options); #endif diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 5c8b8fa8..4a1da9d4 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -23,6 +23,9 @@ #ifdef DISTRHO_OS_WINDOWS # include +# include +# include +# include #else # include #endif @@ -50,6 +53,11 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- +#ifdef DISTRHO_OS_WINDOWS +// static pointer used for direct comparisons +static const char* const kWin32SelectedFileCancelled = "__dpf_cancelled__"; +#endif + static double getDesktopScaleFactor() { if (const char* const scale = getenv("DPF_SCALE_FACTOR")) @@ -195,6 +203,11 @@ Window::PrivateData::~PrivateData() appData->idleCallbacks.remove(this); appData->windows.remove(self); +#ifdef DISTRHO_OS_WINDOWS + if (win32SelectedFile != nullptr && win32SelectedFile != kWin32SelectedFileCancelled) + std::free(const_cast(win32SelectedFile)); +#endif + if (view != nullptr) puglFreeView(view); } @@ -366,11 +379,13 @@ void Window::PrivateData::idleCallback() { #ifndef DGL_FILE_BROWSER_DISABLED # ifdef DISTRHO_OS_WINDOWS - if (char* const path = win32SelectedFile) + if (const char* path = win32SelectedFile) { win32SelectedFile = nullptr; + if (path == kWin32SelectedFileCancelled) + path = nullptr; self->onFileSelected(path); - std::free(path); + std::free(const_cast(path)); } # endif # ifdef HAVE_X11 @@ -465,6 +480,54 @@ bool Window::PrivateData::openFileBrowser(const Window::FileBrowserOptions& opti // -------------------------------------------------------------------------- // show +#ifdef DISTRHO_OS_WINDOWS + // the old and compatible dialog API + OPENFILENAMEW ofn; + memset(&ofn, 0, sizeof(ofn)); + if (win32SelectedFile != nullptr && win32SelectedFile != kWin32SelectedFileCancelled) + std::free(const_cast(win32SelectedFile)); + win32SelectedFile = nullptr; + + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = (HWND)puglGetNativeWindow(view); + + // set start directory in UTF-16 encoding + std::vector startDirW; + startDirW.resize(startDir.length() + 1); + if (MultiByteToWideChar(CP_UTF8, 0, startDir.buffer(), -1, startDirW.data(), startDirW.size())) + ofn.lpstrInitialDir = startDirW.data(); + + // set title in UTF-16 encoding + std::vector titleW; + titleW.resize(title.length() + 1); + if (MultiByteToWideChar(CP_UTF8, 0, title.buffer(), -1, titleW.data(), titleW.size())) + ofn.lpstrTitle = titleW.data(); + + // prepare a buffer to receive the result + std::vector fileNameW(32768); // the Unicode maximum + ofn.lpstrFile = fileNameW.data(); + ofn.nMaxFile = (DWORD)fileNameW.size(); + + // TODO synchronous only, can't do better with WinAPI native dialogs. + // threading might work, if someone is motivated to risk it. + if (GetOpenFileNameW(&ofn)) + { + // back to UTF-8 + std::vector fileNameA(4 * 32768); + if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, + fileNameA.data(), (int)fileNameA.size(), + nullptr, nullptr)) + { + // handle it during the next idle cycle (fake async) + win32SelectedFile = strdup(fileNameA.data()); + } + } + + if (win32SelectedFile == nullptr) + win32SelectedFile = kWin32SelectedFileCancelled; + + return true; +#endif #ifdef HAVE_X11 uint flags = 0x0; // TODO flags diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 11fac31a..83b9d86a 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -70,7 +70,7 @@ struct Window::PrivateData : IdleCallback { #ifdef DISTRHO_OS_WINDOWS /** Selected file for openFileBrowser on windows, stored for fake async operation. */ - char* win32SelectedFile; + const char* win32SelectedFile; #endif /** Modal window setup. */ diff --git a/tests/FileBrowserDialog.cpp b/tests/FileBrowserDialog.cpp index 6191956e..aa70ac51 100644 --- a/tests/FileBrowserDialog.cpp +++ b/tests/FileBrowserDialog.cpp @@ -116,7 +116,11 @@ protected: FileBrowserOptions opts; opts.title = "Look at me"; - openFileBrowser(opts); + if (! openFileBrowser(opts)) + { + selectedFile = "(Failed to start file browser)"; + repaint(); + } } return true; @@ -133,6 +137,16 @@ protected: buttonBounds = Rectangle(width - 120, height/2 - 20, 100, 40); } + void onFocus(const bool focus, CrossingMode) override + { + if (focus) + return; + + buttonClick = false; + buttonHover = false; + repaint(); + } + void onFileSelected(const char* filename) override { if (filename == nullptr)