From 64391e08b2c88e74306e07170de1bbf3af1628b6 Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Fri, 12 Dec 2014 03:07:33 +0000 Subject: [PATCH 01/13] Add Haiku support --- src/nfd_haiku.cpp | 361 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 src/nfd_haiku.cpp diff --git a/src/nfd_haiku.cpp b/src/nfd_haiku.cpp new file mode 100644 index 0000000..9cc5563 --- /dev/null +++ b/src/nfd_haiku.cpp @@ -0,0 +1,361 @@ +/* + Native File Dialog + + http://www.frogtoss.com/labs + */ + + +#include "nfd.h" +#include "nfd_common.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +const int32 kOpenResponse = 'oREF'; +const int32 kSaveResponse = 'sREF'; +const int32 kCancelResponse = 'cREF'; + +class ExtensionRefFilter : public BRefFilter { +public: + ExtensionRefFilter(BString stuff); + bool Filter(const entry_ref* ref, BNode* node, + struct stat_beos* stat, const char* mimeType); +private: + BStringList mExtensions; +}; + + +ExtensionRefFilter::ExtensionRefFilter(BString stuff) { + int32 start = 0; + while(start < stuff.Length()) { + int32 comma = stuff.FindFirst(",", start); + int32 semicolon = stuff.FindFirst(";", start); + if (comma >= 0 && (comma <= semicolon || semicolon < 0)) { + mExtensions.Add(BString(stuff.String() + start, comma - start)); + start = comma + 1; + } else if (semicolon >= 0 && (semicolon < comma || comma < 0)) { + mExtensions.Add(BString(stuff.String() + start, semicolon - start)); + start = semicolon + 1; + } else { + if (stuff.Length() - start < 1) + break; + mExtensions.Add(BString(stuff.String() + start)); + break; + } + } + + for (int32 i = 0; i < mExtensions.CountStrings(); i++) { + mExtensions.StringAt(i).Prepend("."); + } +} + + +bool +ExtensionRefFilter::Filter(const entry_ref* ref, BNode* node, + struct stat_beos* stat, const char* mimeType) +{ + if (S_ISDIR(stat->st_mode)) + return true; + + BString name(ref->name); + for(int32 i = 0; i < mExtensions.CountStrings(); i++) { + if (name.EndsWith(mExtensions.StringAt(i))) + return true; + } + + return false; +} + +class DialogHandler : public BLooper { +public: + DialogHandler(port_id port); + void MessageReceived(BMessage *msg); +private: + port_id mPort; +}; + + +DialogHandler::DialogHandler(port_id port) + : BLooper(), + mPort(port) +{ +} + + +struct response_data { + struct { + int32 count; + entry_ref *refs; + } open; + + struct { + entry_ref directory; + BString filename; + } save; +}; + +void +DialogHandler::MessageReceived(BMessage *msg) +{ + switch(msg->what) { + case B_REFS_RECEIVED: { + response_data data; + msg->GetInfo("refs", NULL, &data.open.count); + data.open.refs = new entry_ref[data.open.count]; + for (int32 i = 0; i < data.open.count; i++) { + entry_ref ref; + msg->FindRef("refs", i, data.open.refs + i); + } + write_port(mPort, kOpenResponse, &data, sizeof(response_data)); + PostMessage(B_QUIT_REQUESTED); + break; + } + + case B_SAVE_REQUESTED: { + response_data data; + msg->FindRef("directory", &data.save.directory); + msg->FindString("name", &data.save.filename); + write_port(mPort, kSaveResponse, &data, sizeof(response_data)); + PostMessage(B_QUIT_REQUESTED); + break; + } + + case B_CANCEL: { + response_data data; + write_port(mPort, kCancelResponse, &data, sizeof(response_data)); + PostMessage(B_QUIT_REQUESTED); + break; + } + + default: + BLooper::MessageReceived(msg); + } +} + + +/* single file open dialog */ +nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ) +{ + if (be_app == NULL) { + NFDi_SetError("You need a valid BApplication before you can open a file open dialog!"); + return NFD_ERROR; + } + + port_id port = create_port(1, "NFD_OpenDialog helper"); + if (port == B_NO_MORE_PORTS) { + NFDi_SetError("Couldn't create port for communicating with BFilePanel"); + return NFD_ERROR; + } + + DialogHandler *handler = new DialogHandler(port); + BMessenger messenger(handler); + BFilePanel *panel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_FILE_NODE, false); + ExtensionRefFilter *filter = NULL; + + if (filterList != NULL && *filterList != 0) { + filter = new ExtensionRefFilter(filterList); + panel->SetRefFilter(filter); + } + + handler->Run(); + panel->SetTarget(messenger); + + if (defaultPath != NULL) { + BEntry directory(defaultPath, true); + panel->SetPanelDirectory(&directory); + } + + panel->Show(); + + response_data data; + int32 response; + + read_port(port, &response, &data, sizeof(response_data)); + + delete_port(port); + delete panel; + + switch (response) { + case kCancelResponse: + if (filter) + delete filter; + return NFD_CANCEL; + case kOpenResponse: { + if (data.open.count != 1) { + delete[] data.open.refs; + if (filter) + delete filter; + + NFDi_SetError("Got invalid count of refs back"); + return NFD_ERROR; + } + + size_t length = NFDi_UTF8_Strlen((nfdchar_t *)data.open.refs[0].name) + 1; + + *outPath = (nfdchar_t *)NFDi_Malloc(length); + NFDi_SafeStrncpy(*outPath, data.open.refs[0].name, length); + + if (filter) + delete filter; + delete[] data.open.refs; + return NFD_OKAY; + } + } + + if (filter) + delete filter; + + NFDi_SetError("Got invalid response from port"); + return NFD_ERROR; +} + + +/* multiple file open dialog */ +nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdpathset_t *outPaths ) +{ + if (be_app == NULL) { + NFDi_SetError("You need a valid BApplication before you can open a file open dialog!"); + return NFD_ERROR; + } + + port_id port = create_port(1, "NFD_OpenDialogMultiple helper"); + if (port == B_NO_MORE_PORTS) { + NFDi_SetError("Couldn't create port for communicating with BFilePanel"); + return NFD_ERROR; + } + + DialogHandler *handler = new DialogHandler(port); + BMessenger messenger(handler); + BFilePanel *panel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_FILE_NODE, false); + ExtensionRefFilter *filter = NULL; + + if (filterList != NULL && *filterList != 0) { + filter = new ExtensionRefFilter(filterList); + panel->SetRefFilter(filter); + } + + handler->Run(); + panel->SetTarget(messenger); + + if (defaultPath != NULL) { + BEntry directory(defaultPath, true); + panel->SetPanelDirectory(&directory); + } + + panel->Show(); + + response_data data; + int32 response; + + read_port(port, &response, &data, sizeof(response_data)); + + delete_port(port); + delete panel; + handler->PostMessage(B_QUIT_REQUESTED); + + switch (response) { + case kCancelResponse: + if (filter) + delete filter; + return NFD_CANCEL; + case kOpenResponse: { + size_t total_length; + for (int i = 0; i < data.open.count; i++) { + total_length += NFDi_UTF8_Strlen((nfdchar_t *)data.open.refs[i].name) + 1; + } + + outPaths->indices = (size_t *)NFDi_Malloc(sizeof(size_t) * data.open.count); + outPaths->count = data.open.count; + outPaths->buf = (nfdchar_t *)NFDi_Malloc(total_length); + + nfdchar_t *buflocation = outPaths->buf; + for (int i = 0; i < data.open.count; i++) { + size_t length = NFDi_UTF8_Strlen((nfdchar_t *)data.open.refs[i].name) + 1; + NFDi_SafeStrncpy(buflocation, (nfdchar_t *)data.open.refs[i].name, length); + outPaths->indices[i] = buflocation - outPaths->buf; + buflocation += length; + } + + delete[] data.open.refs; + if (filter) + delete filter; + return NFD_OKAY; + } + } + + if (filter) + delete filter; + + NFDi_SetError("Got invalid response from port"); + return NFD_ERROR; +} + + +/* save dialog */ +nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ) +{ if (be_app == NULL) { + NFDi_SetError("You need a valid BApplication before you can open a file open dialog!"); + return NFD_ERROR; + } + + port_id port = create_port(1, "NFD_OpenDialog helper"); + if (port == B_NO_MORE_PORTS) { + NFDi_SetError("Haiku couldn't create port for communicating with BFilePanel"); + return NFD_ERROR; + } + + DialogHandler *handler = new DialogHandler(port); + handler->Run(); + BMessenger messenger(handler); + BFilePanel *panel = new BFilePanel(B_SAVE_PANEL, NULL, NULL, B_FILE_NODE, false); + panel->SetTarget(messenger); + if (defaultPath != NULL) { + BEntry directory(defaultPath, true); + panel->SetPanelDirectory(&directory); + } + + panel->Show(); + response_data data; + int32 response; + + read_port(port, &response, &data, sizeof(response_data)); + + delete_port(port); + delete panel; + handler->PostMessage(B_QUIT_REQUESTED); + + switch (response) { + case kCancelResponse: + return NFD_CANCEL; + case kSaveResponse: { + BEntry entry(&data.save.directory); + BPath path(&entry); + size_t dirlength = strlen(path.Path()); + size_t namelength = strlen(data.save.filename); + *outPath = (nfdchar_t *)NFDi_Malloc(dirlength + namelength + 2); + NFDi_SafeStrncpy(*outPath, path.Path(), dirlength + 1); + (*outPath)[dirlength] = '/'; + NFDi_SafeStrncpy((*outPath) + dirlength + 1, data.save.filename, namelength + 1); + return NFD_OKAY; + } + } + + NFDi_SetError("Got invalid response from port"); + return NFD_ERROR; +} From 1894e5bc4acec5ef128ea6168d8c3db9f1fbbdf5 Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Fri, 12 Dec 2014 03:42:30 +0000 Subject: [PATCH 02/13] Add screenshot, update readme --- README.md | 6 ++++++ screens/open_haiku.png | Bin 0 -> 27432 bytes 2 files changed, 6 insertions(+) create mode 100644 screens/open_haiku.png diff --git a/README.md b/README.md index 9ad6039..9f2ed0a 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Features: - Support for non-deprecated Cocoa APIs on OS X. - GTK3 dialog on Linux. - Optional Zenity support on Linux to avoid linking GTK. + - Haiku support - Tested, works alongside [http://www.libsdl.org](SDL2) on all platforms, for the game developers out there. # Example Usage # @@ -54,6 +55,7 @@ See self-documenting API [NFD.h](src/include/nfd.h) for more options. ![Windows rendering a dialog](screens/open_win.png?raw=true) ![GTK3 on Linux rendering a dialog](screens/open_gtk3.png?raw=true) ![Cocoa on MacOS rendering a dialog](screens/open_cocoa.png?raw=true) +![Haiku](screens/open_haiku.png?raw=true) ## Changelog ## @@ -155,6 +157,9 @@ On Mac OS, add `AppKit` to the list of frameworks. On Windows, ensure you are linking against `comctl32.lib`. +#### Haiku #### +On Haiku, you need to link to `libtracker` and `libbe`, and make sure a `BApplication` exists before trying to call the API + ## Usage ## See `NFD.h` for API calls. See `tests/*.c` for example code. @@ -195,6 +200,7 @@ I accept quality code patches, or will resolve these and other matters through s - It errors out if Zenity is not installed on the user's system - This backend's process exec error handling does not gracefully handle numerous error cases, choosing to abort rather than cleanup and return. - Unlike the other backends, the Zenity backend does not return implied extensions from filterlists. [#95](https://github.com/mlabbe/nativefiledialog/issues/95 "Issue 95") + - On Haiku, a `BApplication` is required to spawn a window. In the examples this is done with some #if's. # Copyright and Credit # diff --git a/screens/open_haiku.png b/screens/open_haiku.png new file mode 100644 index 0000000000000000000000000000000000000000..da880a41aebb3194ff13962f11d9b9a42237b099 GIT binary patch literal 27432 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�fFWSkRL|$7&8Iihz(7{@ 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 literal 0 HcmV?d00001 From 425c2cfb1944c60b02264c60dc50117894b09e4a Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Fri, 12 Dec 2014 03:42:45 +0000 Subject: [PATCH 03/13] Add switch to enable aggressive filtering --- src/nfd_haiku.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/nfd_haiku.cpp b/src/nfd_haiku.cpp index 9cc5563..270df9a 100644 --- a/src/nfd_haiku.cpp +++ b/src/nfd_haiku.cpp @@ -35,6 +35,8 @@ class ExtensionRefFilter : public BRefFilter { }; +#define FILTER 1 + ExtensionRefFilter::ExtensionRefFilter(BString stuff) { int32 start = 0; while(start < stuff.Length()) { @@ -64,6 +66,9 @@ bool ExtensionRefFilter::Filter(const entry_ref* ref, BNode* node, struct stat_beos* stat, const char* mimeType) { + #if !FILTER + return true; + #endif if (S_ISDIR(stat->st_mode)) return true; From a723a2f9ba2b624a3e7b5872533c32886b50c584 Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Sat, 13 Dec 2014 16:54:00 +0000 Subject: [PATCH 04/13] Use semaphores instead of ports --- src/nfd_haiku.cpp | 116 +++++++++++++++++++++------------------------- 1 file changed, 53 insertions(+), 63 deletions(-) diff --git a/src/nfd_haiku.cpp b/src/nfd_haiku.cpp index 270df9a..77d2846 100644 --- a/src/nfd_haiku.cpp +++ b/src/nfd_haiku.cpp @@ -81,21 +81,6 @@ ExtensionRefFilter::Filter(const entry_ref* ref, BNode* node, return false; } -class DialogHandler : public BLooper { -public: - DialogHandler(port_id port); - void MessageReceived(BMessage *msg); -private: - port_id mPort; -}; - - -DialogHandler::DialogHandler(port_id port) - : BLooper(), - mPort(port) -{ -} - struct response_data { struct { @@ -109,35 +94,60 @@ struct response_data { } save; }; + +class DialogHandler : public BLooper { +public: + DialogHandler(); + ~DialogHandler() { delete_sem(mSemaphore); } + void MessageReceived(BMessage *msg); + int32 ResponseId() { return mResponseId; } + response_data &ResponseData() { return mResponseData; } + void Wait() { acquire_sem(mSemaphore); } +private: + sem_id mSemaphore; + int32 mResponseId; + response_data mResponseData; +}; + + +DialogHandler::DialogHandler() + : BLooper(), + mResponseId(0) +{ + mSemaphore = create_sem(0, "NativeFileDialog helper"); +} + void DialogHandler::MessageReceived(BMessage *msg) { + if (mResponseId != 0) { + BLooper::MessageReceived(msg); + return; + } + switch(msg->what) { case B_REFS_RECEIVED: { - response_data data; - msg->GetInfo("refs", NULL, &data.open.count); - data.open.refs = new entry_ref[data.open.count]; - for (int32 i = 0; i < data.open.count; i++) { + mResponseId = kOpenResponse; + msg->GetInfo("refs", NULL, &mResponseData.open.count); + mResponseData.open.refs = new entry_ref[mResponseData.open.count]; + for (int32 i = 0; i < mResponseData.open.count; i++) { entry_ref ref; - msg->FindRef("refs", i, data.open.refs + i); + msg->FindRef("refs", i, mResponseData.open.refs + i); } - write_port(mPort, kOpenResponse, &data, sizeof(response_data)); PostMessage(B_QUIT_REQUESTED); break; } case B_SAVE_REQUESTED: { - response_data data; - msg->FindRef("directory", &data.save.directory); - msg->FindString("name", &data.save.filename); - write_port(mPort, kSaveResponse, &data, sizeof(response_data)); + mResponseId = kSaveResponse; + msg->FindRef("directory", &mResponseData.save.directory); + msg->FindString("name", &mResponseData.save.filename); PostMessage(B_QUIT_REQUESTED); break; } case B_CANCEL: { - response_data data; - write_port(mPort, kCancelResponse, &data, sizeof(response_data)); + mResponseId = kCancelResponse; PostMessage(B_QUIT_REQUESTED); break; } @@ -145,6 +155,8 @@ DialogHandler::MessageReceived(BMessage *msg) default: BLooper::MessageReceived(msg); } + + release_sem(mSemaphore); } @@ -157,14 +169,8 @@ nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, NFDi_SetError("You need a valid BApplication before you can open a file open dialog!"); return NFD_ERROR; } - - port_id port = create_port(1, "NFD_OpenDialog helper"); - if (port == B_NO_MORE_PORTS) { - NFDi_SetError("Couldn't create port for communicating with BFilePanel"); - return NFD_ERROR; - } - DialogHandler *handler = new DialogHandler(port); + DialogHandler *handler = new DialogHandler(); BMessenger messenger(handler); BFilePanel *panel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_FILE_NODE, false); ExtensionRefFilter *filter = NULL; @@ -184,12 +190,12 @@ nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, panel->Show(); - response_data data; - int32 response; + handler->Wait(); - read_port(port, &response, &data, sizeof(response_data)); + response_data &data = handler->ResponseData(); + int32 response = handler->ResponseId(); + handler->PostMessage(B_QUIT_REQUESTED); - delete_port(port); delete panel; switch (response) { @@ -236,14 +242,8 @@ nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, NFDi_SetError("You need a valid BApplication before you can open a file open dialog!"); return NFD_ERROR; } - - port_id port = create_port(1, "NFD_OpenDialogMultiple helper"); - if (port == B_NO_MORE_PORTS) { - NFDi_SetError("Couldn't create port for communicating with BFilePanel"); - return NFD_ERROR; - } - DialogHandler *handler = new DialogHandler(port); + DialogHandler *handler = new DialogHandler(); BMessenger messenger(handler); BFilePanel *panel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_FILE_NODE, false); ExtensionRefFilter *filter = NULL; @@ -263,13 +263,11 @@ nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, panel->Show(); - response_data data; - int32 response; + handler->Wait(); - read_port(port, &response, &data, sizeof(response_data)); + response_data &data = handler->ResponseData(); + int32 response = handler->ResponseId(); - delete_port(port); - delete panel; handler->PostMessage(B_QUIT_REQUESTED); switch (response) { @@ -318,14 +316,8 @@ nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, NFDi_SetError("You need a valid BApplication before you can open a file open dialog!"); return NFD_ERROR; } - - port_id port = create_port(1, "NFD_OpenDialog helper"); - if (port == B_NO_MORE_PORTS) { - NFDi_SetError("Haiku couldn't create port for communicating with BFilePanel"); - return NFD_ERROR; - } - DialogHandler *handler = new DialogHandler(port); + DialogHandler *handler = new DialogHandler(); handler->Run(); BMessenger messenger(handler); BFilePanel *panel = new BFilePanel(B_SAVE_PANEL, NULL, NULL, B_FILE_NODE, false); @@ -336,13 +328,11 @@ nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, } panel->Show(); - response_data data; - int32 response; - - read_port(port, &response, &data, sizeof(response_data)); + + handler->Wait(); - delete_port(port); - delete panel; + response_data &data = handler->ResponseData(); + int32 response = handler->ResponseId(); handler->PostMessage(B_QUIT_REQUESTED); switch (response) { From 87da7d75786b4834a6d6d73e83d7683bb612f1d9 Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Sun, 14 Dec 2014 08:46:48 +0000 Subject: [PATCH 05/13] Fix small typo, make BFilePanel modal --- src/nfd_haiku.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nfd_haiku.cpp b/src/nfd_haiku.cpp index 77d2846..f78ad1b 100644 --- a/src/nfd_haiku.cpp +++ b/src/nfd_haiku.cpp @@ -172,7 +172,7 @@ nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, DialogHandler *handler = new DialogHandler(); BMessenger messenger(handler); - BFilePanel *panel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_FILE_NODE, false); + BFilePanel *panel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_FILE_NODE, false, NULL, NULL, true); ExtensionRefFilter *filter = NULL; if (filterList != NULL && *filterList != 0) { @@ -245,7 +245,7 @@ nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, DialogHandler *handler = new DialogHandler(); BMessenger messenger(handler); - BFilePanel *panel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_FILE_NODE, false); + BFilePanel *panel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_FILE_NODE, false, NULL, NULL, true); ExtensionRefFilter *filter = NULL; if (filterList != NULL && *filterList != 0) { @@ -313,14 +313,14 @@ nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, const nfdchar_t *defaultPath, nfdchar_t **outPath ) { if (be_app == NULL) { - NFDi_SetError("You need a valid BApplication before you can open a file open dialog!"); + NFDi_SetError("You need a valid BApplication before you can open a file save dialog!"); return NFD_ERROR; } DialogHandler *handler = new DialogHandler(); handler->Run(); BMessenger messenger(handler); - BFilePanel *panel = new BFilePanel(B_SAVE_PANEL, NULL, NULL, B_FILE_NODE, false); + BFilePanel *panel = new BFilePanel(B_SAVE_PANEL, NULL, NULL, B_FILE_NODE, false, NULL, NULL, true); panel->SetTarget(messenger); if (defaultPath != NULL) { BEntry directory(defaultPath, true); From d04d7a88db0bc7aa91546e2dcab98be0e7c23e46 Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Sun, 14 Dec 2014 09:03:07 +0000 Subject: [PATCH 06/13] Have nfd_haiku create its own temporary be_app when there's none already created --- src/nfd_haiku.cpp | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/nfd_haiku.cpp b/src/nfd_haiku.cpp index f78ad1b..98f993c 100644 --- a/src/nfd_haiku.cpp +++ b/src/nfd_haiku.cpp @@ -34,7 +34,6 @@ class ExtensionRefFilter : public BRefFilter { BStringList mExtensions; }; - #define FILTER 1 ExtensionRefFilter::ExtensionRefFilter(BString stuff) { @@ -165,9 +164,9 @@ nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, const nfdchar_t *defaultPath, nfdchar_t **outPath ) { + BApplication *temporaryApp = NULL; if (be_app == NULL) { - NFDi_SetError("You need a valid BApplication before you can open a file open dialog!"); - return NFD_ERROR; + temporaryApp = new BApplication("application/x-vnd.nfd-dialog"); } DialogHandler *handler = new DialogHandler(); @@ -196,6 +195,9 @@ nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, int32 response = handler->ResponseId(); handler->PostMessage(B_QUIT_REQUESTED); + if (temporaryApp) + delete temporaryApp; + delete panel; switch (response) { @@ -238,9 +240,9 @@ nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, const nfdchar_t *defaultPath, nfdpathset_t *outPaths ) { + BApplication *temporaryApp = NULL; if (be_app == NULL) { - NFDi_SetError("You need a valid BApplication before you can open a file open dialog!"); - return NFD_ERROR; + temporaryApp = new BApplication("application/x-vnd.nfd-dialog"); } DialogHandler *handler = new DialogHandler(); @@ -270,13 +272,16 @@ nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, handler->PostMessage(B_QUIT_REQUESTED); + if (temporaryApp) + delete temporaryApp; + switch (response) { case kCancelResponse: if (filter) delete filter; return NFD_CANCEL; case kOpenResponse: { - size_t total_length; + size_t total_length = 0; for (int i = 0; i < data.open.count; i++) { total_length += NFDi_UTF8_Strlen((nfdchar_t *)data.open.refs[i].name) + 1; } @@ -312,9 +317,10 @@ nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, const nfdchar_t *defaultPath, nfdchar_t **outPath ) -{ if (be_app == NULL) { - NFDi_SetError("You need a valid BApplication before you can open a file save dialog!"); - return NFD_ERROR; +{ + BApplication *temporaryApp = NULL; + if (be_app == NULL) { + temporaryApp = new BApplication("application/x-vnd.nfd-dialog"); } DialogHandler *handler = new DialogHandler(); @@ -335,6 +341,9 @@ nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, int32 response = handler->ResponseId(); handler->PostMessage(B_QUIT_REQUESTED); + if (temporaryApp) + delete temporaryApp; + switch (response) { case kCancelResponse: return NFD_CANCEL; From c357fd23dbfae6a970b60ef8c3e0df8ea4ff9882 Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Sun, 14 Dec 2014 10:05:41 +0000 Subject: [PATCH 07/13] Update readme, add source files --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9f2ed0a..88cd90d 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,8 @@ Tomasz Konojacki for [microutf8](http://puszcza.gnu.org.ua/software/microutf8/) [Tom Mason](https://github.com/wheybags) for Zenity support. +[Puck Meerburg](https://github.com/puckipedia/nativefiledialog) for Haiku support. + Various pull requests and bugfixes -- thanks to the original authors. ## Support ## From 5a8b4c38afed2d37589007f20b03800144e55d79 Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Sun, 14 Dec 2014 11:08:13 +0100 Subject: [PATCH 08/13] Update readme some more --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 88cd90d..9b068f7 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ On Mac OS, add `AppKit` to the list of frameworks. On Windows, ensure you are linking against `comctl32.lib`. #### Haiku #### -On Haiku, you need to link to `libtracker` and `libbe`, and make sure a `BApplication` exists before trying to call the API +On Haiku, you need to link to `libtracker` and `libbe`. ## Usage ## @@ -200,7 +200,7 @@ I accept quality code patches, or will resolve these and other matters through s - It errors out if Zenity is not installed on the user's system - This backend's process exec error handling does not gracefully handle numerous error cases, choosing to abort rather than cleanup and return. - Unlike the other backends, the Zenity backend does not return implied extensions from filterlists. [#95](https://github.com/mlabbe/nativefiledialog/issues/95 "Issue 95") - - On Haiku, a `BApplication` is required to spawn a window. In the examples this is done with some #if's. + - On Haiku, the file open dialog either shows only allowed files or all files, which is chosen at build time. # Copyright and Credit # From 907fdda295bbe810412b5e99ceb80c2626ad6530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Mon, 13 Aug 2018 00:18:26 +0200 Subject: [PATCH 09/13] Haiku: Fix use-after-delete The members of the strucs where used after the looper deleted itself (via PostMessage(B_QUIT_REQUESTED)), resuting in bad data. So we copy the struct instead of getting a reference, before deleting the looper. --- src/nfd_haiku.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/nfd_haiku.cpp b/src/nfd_haiku.cpp index 98f993c..26ebdd9 100644 --- a/src/nfd_haiku.cpp +++ b/src/nfd_haiku.cpp @@ -133,7 +133,6 @@ DialogHandler::MessageReceived(BMessage *msg) entry_ref ref; msg->FindRef("refs", i, mResponseData.open.refs + i); } - PostMessage(B_QUIT_REQUESTED); break; } @@ -141,13 +140,11 @@ DialogHandler::MessageReceived(BMessage *msg) mResponseId = kSaveResponse; msg->FindRef("directory", &mResponseData.save.directory); msg->FindString("name", &mResponseData.save.filename); - PostMessage(B_QUIT_REQUESTED); break; } case B_CANCEL: { mResponseId = kCancelResponse; - PostMessage(B_QUIT_REQUESTED); break; } @@ -191,7 +188,7 @@ nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, handler->Wait(); - response_data &data = handler->ResponseData(); + response_data data = handler->ResponseData(); int32 response = handler->ResponseId(); handler->PostMessage(B_QUIT_REQUESTED); @@ -267,7 +264,7 @@ nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, handler->Wait(); - response_data &data = handler->ResponseData(); + response_data data = handler->ResponseData(); int32 response = handler->ResponseId(); handler->PostMessage(B_QUIT_REQUESTED); @@ -337,7 +334,7 @@ nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, handler->Wait(); - response_data &data = handler->ResponseData(); + response_data data = handler->ResponseData(); int32 response = handler->ResponseId(); handler->PostMessage(B_QUIT_REQUESTED); From 7a14fd310e61d222730e4d1c790c8a1d2a77ba8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Mon, 13 Aug 2018 00:37:46 +0200 Subject: [PATCH 10/13] Haiku: Fix open path(s) reporting We previously only returned the file names, not the full paths. --- src/nfd_haiku.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/nfd_haiku.cpp b/src/nfd_haiku.cpp index 26ebdd9..992f7ca 100644 --- a/src/nfd_haiku.cpp +++ b/src/nfd_haiku.cpp @@ -130,8 +130,7 @@ DialogHandler::MessageReceived(BMessage *msg) msg->GetInfo("refs", NULL, &mResponseData.open.count); mResponseData.open.refs = new entry_ref[mResponseData.open.count]; for (int32 i = 0; i < mResponseData.open.count; i++) { - entry_ref ref; - msg->FindRef("refs", i, mResponseData.open.refs + i); + msg->FindRef("refs", i, &mResponseData.open.refs[i]); } break; } @@ -211,11 +210,12 @@ nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, NFDi_SetError("Got invalid count of refs back"); return NFD_ERROR; } - - size_t length = NFDi_UTF8_Strlen((nfdchar_t *)data.open.refs[0].name) + 1; + + BPath path(&data.open.refs[0]); + size_t length = NFDi_UTF8_Strlen((nfdchar_t *)path.Path()) + 1; *outPath = (nfdchar_t *)NFDi_Malloc(length); - NFDi_SafeStrncpy(*outPath, data.open.refs[0].name, length); + NFDi_SafeStrncpy(*outPath, path.Path(), length); if (filter) delete filter; @@ -279,8 +279,10 @@ nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, return NFD_CANCEL; case kOpenResponse: { size_t total_length = 0; + BPath paths[data.open.count]; for (int i = 0; i < data.open.count; i++) { - total_length += NFDi_UTF8_Strlen((nfdchar_t *)data.open.refs[i].name) + 1; + paths[i] = BPath(&data.open.refs[i]); + total_length += NFDi_UTF8_Strlen((nfdchar_t *)paths[i].Path()) + 1; } outPaths->indices = (size_t *)NFDi_Malloc(sizeof(size_t) * data.open.count); @@ -289,8 +291,8 @@ nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, nfdchar_t *buflocation = outPaths->buf; for (int i = 0; i < data.open.count; i++) { - size_t length = NFDi_UTF8_Strlen((nfdchar_t *)data.open.refs[i].name) + 1; - NFDi_SafeStrncpy(buflocation, (nfdchar_t *)data.open.refs[i].name, length); + size_t length = NFDi_UTF8_Strlen((nfdchar_t *)paths[i].Path()) + 1; + NFDi_SafeStrncpy(buflocation, (nfdchar_t *)paths[i].Path(), length); outPaths->indices[i] = buflocation - outPaths->buf; buflocation += length; } From 0a49babb7f24c5e45c090ece92546c8ab487a05d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Wed, 16 Oct 2019 03:59:25 +0200 Subject: [PATCH 11/13] Haiku: implement NFD_PickFolder --- src/nfd_haiku.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/nfd_haiku.cpp b/src/nfd_haiku.cpp index 992f7ca..e8fa7be 100644 --- a/src/nfd_haiku.cpp +++ b/src/nfd_haiku.cpp @@ -362,3 +362,64 @@ nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, NFDi_SetError("Got invalid response from port"); return NFD_ERROR; } + + +/* single folder open dialog */ +nfdresult_t NFD_PickFolder( const nfdchar_t *defaultPath, + nfdchar_t **outPath ) +{ + BApplication *temporaryApp = NULL; + if (be_app == NULL) { + temporaryApp = new BApplication("application/x-vnd.nfd-dialog"); + } + + DialogHandler *handler = new DialogHandler(); + BMessenger messenger(handler); + BFilePanel *panel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_DIRECTORY_NODE, false, NULL, NULL, true); + + handler->Run(); + panel->SetTarget(messenger); + + if (defaultPath != NULL) { + BEntry directory(defaultPath, true); + panel->SetPanelDirectory(&directory); + } + + panel->Show(); + + handler->Wait(); + + response_data data = handler->ResponseData(); + int32 response = handler->ResponseId(); + handler->PostMessage(B_QUIT_REQUESTED); + + if (temporaryApp) + delete temporaryApp; + + delete panel; + + switch (response) { + case kCancelResponse: + return NFD_CANCEL; + case kOpenResponse: { + if (data.open.count != 1) { + delete[] data.open.refs; + + NFDi_SetError("Got invalid count of refs back"); + return NFD_ERROR; + } + + BPath path(&data.open.refs[0]); + size_t length = NFDi_UTF8_Strlen((nfdchar_t *)path.Path()) + 1; + + *outPath = (nfdchar_t *)NFDi_Malloc(length); + NFDi_SafeStrncpy(*outPath, path.Path(), length); + + delete[] data.open.refs; + return NFD_OKAY; + } + } + + NFDi_SetError("Got invalid response from port"); + return NFD_ERROR; +} From da1f994e2a0e7bd6c4621384dab9f8014f299b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Wed, 16 Oct 2019 04:01:12 +0200 Subject: [PATCH 12/13] Add Haiku support to premake5.lua Note we want x86 build by default. Only gcc>2 build works for some reason, I suppose premake doesn't care about gcc2 and uses incompatible args, but we do. --- build/premake5.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/build/premake5.lua b/build/premake5.lua index fb525ab..6c75842 100644 --- a/build/premake5.lua +++ b/build/premake5.lua @@ -53,6 +53,8 @@ workspace "NativeFileDialog" platforms {"x64", "x86"} filter "system:linux or system:macosx" platforms {"arm64"} + filter "system:haiku" + platforms {"x86", "x64"} objdir(path.join(build_dir, "obj/")) @@ -105,6 +107,9 @@ workspace "NativeFileDialog" language "C" files {root_dir.."src/nfd_cocoa.m"} + filter "system:haiku" + language "C++" + files {root_dir.."src/nfd_haiku.cpp"} filter {"system:linux", "options:linux_backend=gtk3"} @@ -174,6 +179,10 @@ local make_test = function(name) filter {"system:macosx"} links {"Foundation.framework", "AppKit.framework"} + filter {"system:haiku"} + -- should link to stdc++.r4 for gcc2 + links {"be", "tracker", "stdc++"} + filter {"configurations:Debug", "system:linux", "options:linux_backend=gtk3"} linkoptions {"-lnfd_d `pkg-config --libs gtk+-3.0`"} filter {"configurations:Debug", "system:linux", "options:linux_backend=zenity"} @@ -231,6 +240,7 @@ newaction premake_do_action("gmake", "linux", true,{}) premake_do_action("gmake", "linux", true,{linux_backend='zenity'}) premake_do_action("gmake", "macosx", true,{}) + premake_do_action("gmake", "haiku", true,{}) premake_do_action("gmake", "windows", true,{}) end } @@ -276,6 +286,7 @@ newaction "xcode4", "gmake_linux", "gmake_macosx", + "gmake_haiku", "gmake_windows" } From 09ad4669c4ebcc2652b13ef007f1e4cf270eec9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Sat, 18 Nov 2023 17:53:31 +0100 Subject: [PATCH 13/13] Haiku: add missing definitions --- src/ftg_core.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ftg_core.h b/src/ftg_core.h index 857f03a..53d8709 100644 --- a/src/ftg_core.h +++ b/src/ftg_core.h @@ -114,7 +114,8 @@ /* broad test for OSes that are vaguely POSIX compliant */ #if defined(__linux__) || defined(__APPLE__) || defined(ANDROID) || \ defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \ - defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) + defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) || \ + defined(__HAIKU__) # define FTG_POSIX_LIKE 1 #endif @@ -399,7 +400,7 @@ typedef struct ftg_dirhandle_s ftg_dirhandle_t; #elif defined(_WIN32) typedef int64_t ftg_off_t; typedef wchar_t ftg_wchar_t; -#elif defined(__FreeBSD__) || defined(__OpenBSD__) +#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__) typedef off_t ftg_off_t; #elif defined(FTG_WASM) typedef off_t ftg_off_t;