diff --git a/dgl/Window.hpp b/dgl/Window.hpp index f93e29f1..37ae278e 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -25,10 +25,48 @@ START_NAMESPACE_DGL class App; class Widget; +class StandaloneWindow; class Window { public: + /** + File browser options. + */ + struct FileBrowserOptions { + const char* startDir; + const char* title; + uint width; + uint height; + + /** + File browser buttons. + + 0 means hidden. + 1 means visible and unchecked. + 2 means visible and checked. + */ + struct Buttons { + uint listAllFiles; + uint showHidden; + uint showPlaces; + + /** Constuctor for default values */ + Buttons() + : listAllFiles(2), + showHidden(1), + showPlaces(1) {} + } buttons; + + /** Constuctor for default values */ + FileBrowserOptions() + : startDir(nullptr), + title(nullptr), + width(0), + height(0), + buttons() {} + }; + explicit Window(App& app); explicit Window(App& app, Window& parent); explicit Window(App& app, intptr_t parentId); @@ -42,6 +80,8 @@ public: void focus(); void repaint() noexcept; + bool openFileBrowser(const FileBrowserOptions& options); + bool isVisible() const noexcept; void setVisible(bool yesNo); @@ -54,6 +94,7 @@ public: void setSize(uint width, uint height); void setSize(Size size); + const char* getTitle() const noexcept; void setTitle(const char* title); void setTransientWinId(uintptr_t winId); @@ -70,6 +111,8 @@ protected: virtual void onReshape(uint width, uint height); virtual void onClose(); + virtual void fileBrowserSelected(const char* filename); + private: struct PrivateData; PrivateData* const pData; diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index d837c6e5..c901ff2b 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -20,6 +20,7 @@ #include "AppPrivateData.hpp" #include "../Widget.hpp" #include "../Window.hpp" +#include "../../distrho/extra/d_string.hpp" #include "pugl/pugl.h" @@ -69,6 +70,7 @@ struct Window::PrivateData { fUsingEmbed(false), fWidth(1), fHeight(1), + fTitle(nullptr), fWidgets(), fModal(), #if defined(DISTRHO_OS_WINDOWS) @@ -97,6 +99,7 @@ struct Window::PrivateData { fUsingEmbed(false), fWidth(1), fHeight(1), + fTitle(nullptr), fWidgets(), fModal(parent.pData), #if defined(DISTRHO_OS_WINDOWS) @@ -135,6 +138,7 @@ struct Window::PrivateData { fUsingEmbed(parentId != 0), fWidth(1), fHeight(1), + fTitle(nullptr), fWidgets(), fModal(), #if defined(DISTRHO_OS_WINDOWS) @@ -190,6 +194,7 @@ struct Window::PrivateData { puglSetSpecialFunc(fView, onSpecialCallback); puglSetReshapeFunc(fView, onReshapeCallback); puglSetCloseFunc(fView, onCloseCallback); + puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback); puglCreateWindow(fView, nullptr); @@ -535,10 +540,22 @@ struct Window::PrivateData { // ------------------------------------------------------------------- + const char* getTitle() const noexcept + { + static const char* const kFallback = ""; + + return fTitle != nullptr ? fTitle : kFallback; + } + void setTitle(const char* const title) { DBGp("Window setTitle \"%s\"\n", title); + if (fTitle != nullptr) + std::free(fTitle); + + fTitle = strdup(title); + #if defined(DISTRHO_OS_WINDOWS) SetWindowTextA(hwnd, title); #elif defined(DISTRHO_OS_MAC) @@ -840,6 +857,7 @@ struct Window::PrivateData { bool fUsingEmbed; uint fWidth; uint fHeight; + char* fTitle; std::list fWidgets; struct Modal { @@ -922,6 +940,11 @@ struct Window::PrivateData { handlePtr->onPuglClose(); } + static void fileBrowserSelectedCallback(PuglView* view, const char* filename) + { + handlePtr->fSelf->fileBrowserSelected(filename); + } + #undef handlePtr DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) @@ -977,6 +1000,73 @@ void Window::repaint() noexcept puglPostRedisplay(pData->fView); } +// static int fib_filter_filename_filter(const char* const name) +// { +// return 1; +// (void)name; +// } + +bool Window::openFileBrowser(const FileBrowserOptions& options) +{ + using DISTRHO_NAMESPACE::d_string; + + // -------------------------------------------------------------------------- + // configure start dir + + // TODO: get abspath if needed + // TODO: cross-platform + + d_string startDir(options.startDir); + + if (startDir.isEmpty()) + { + if (char* const dir_name = get_current_dir_name()) + { + startDir = dir_name; + std::free(dir_name); + } + } + + DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), false); + + if (! startDir.endsWith('/')) + startDir += "/"; + + DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false); + + // -------------------------------------------------------------------------- + // configure title + + d_string title(options.title); + + if (title.isEmpty()) + { + title = pData->getTitle(); + + if (title.isEmpty()) + title = "FileBrowser"; + } + + DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false); + + // -------------------------------------------------------------------------- + // configure filters + + x_fib_cfg_filter_callback(nullptr); //fib_filter_filename_filter); + + // -------------------------------------------------------------------------- + // configure buttons + + x_fib_cfg_buttons(3, options.buttons.listAllFiles-1); + x_fib_cfg_buttons(1, options.buttons.showHidden-1); + x_fib_cfg_buttons(2, options.buttons.showPlaces-1); + + // -------------------------------------------------------------------------- + // show + + return (x_fib_show(pData->xDisplay, pData->xWindow, /*options.width*/0, /*options.height*/0) == 0); +} + bool Window::isVisible() const noexcept { return pData->fVisible; @@ -1022,6 +1112,11 @@ void Window::setSize(Size size) pData->setSize(size.getWidth(), size.getHeight()); } +const char* Window::getTitle() const noexcept +{ + return pData->getTitle(); +} + void Window::setTitle(const char* title) { pData->setTitle(title); @@ -1101,6 +1196,10 @@ void Window::onClose() { } +void Window::fileBrowserSelected(const char*) +{ +} + // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/pugl/pugl.h b/dgl/src/pugl/pugl.h index c45c213e..7cd8b842 100644 --- a/dgl/src/pugl/pugl.h +++ b/dgl/src/pugl/pugl.h @@ -219,6 +219,14 @@ typedef void (*PuglScrollFunc)(PuglView* view, int x, int y, float dx, float dy) */ typedef void (*PuglSpecialFunc)(PuglView* view, bool press, PuglKey key); +/** + A function called when a filename is selected via file-browser. + + @param view The view the event occured in. + @param filename The selected file name or NULL if the dialog was canceled. +*/ +typedef void (*PuglFileSelectedFunc)(PuglView* view, const char* filename); + /** Create a Pugl context. @@ -352,6 +360,12 @@ puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc); PUGL_API void puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc); +/** + Set the function to call on file-browser selections. +*/ +PUGL_API void +puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc); + /** Return the native window handle. */ diff --git a/dgl/src/pugl/pugl_internal.h b/dgl/src/pugl/pugl_internal.h index 76ce41b3..7301091a 100644 --- a/dgl/src/pugl/pugl_internal.h +++ b/dgl/src/pugl/pugl_internal.h @@ -51,6 +51,7 @@ struct PuglViewImpl { PuglReshapeFunc reshapeFunc; PuglScrollFunc scrollFunc; PuglSpecialFunc specialFunc; + PuglFileSelectedFunc fileSelectedFunc; PuglInternals* impl; PuglNativeWindow parent; @@ -200,3 +201,9 @@ puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc) { view->specialFunc = specialFunc; } + +void +puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc) +{ + view->fileSelectedFunc = fileSelectedFunc; +} diff --git a/dgl/src/pugl/pugl_x11.c b/dgl/src/pugl/pugl_x11.c index 8548387d..eac570ce 100644 --- a/dgl/src/pugl/pugl_x11.c +++ b/dgl/src/pugl/pugl_x11.c @@ -32,6 +32,10 @@ #include "pugl_internal.h" +#define SOFD_HAVE_X11 +#include "../sofd/libsofd.h" +#include "../sofd/libsofd.c" + struct PuglInternalsImpl { Display* display; int screen; @@ -96,7 +100,7 @@ puglCreateWindow(PuglView* view, const char* title) { PuglInternals* impl = view->impl; - impl->display = XOpenDisplay(0); + impl->display = XOpenDisplay(NULL); impl->screen = DefaultScreen(impl->display); impl->doubleBuffered = True; @@ -322,6 +326,26 @@ puglProcessEvents(PuglView* view) XEvent event; while (XPending(view->impl->display) > 0) { XNextEvent(view->impl->display, &event); + + if (x_fib_handle_events(view->impl->display, &event)) { + const int status = x_fib_status(); + + if (status > 0) { + char* const filename = x_fib_filename(); + x_fib_close(view->impl->display); + if (view->fileSelectedFunc) { + view->fileSelectedFunc(view, filename); + } + free(filename); + } else if (status < 0) { + x_fib_close(view->impl->display); + if (view->fileSelectedFunc) { + view->fileSelectedFunc(view, NULL); + } + } + break; + } + switch (event.type) { case MapNotify: puglReshape(view, view->width, view->height);