2022-03-13 23:32:35 +00:00
|
|
|
#include "fileDialog.h"
|
|
|
|
#include "ImGuiFileDialog.h"
|
2022-03-14 04:10:43 +00:00
|
|
|
#include "../ta-log.h"
|
2022-03-14 02:19:52 +00:00
|
|
|
|
2022-06-17 06:28:22 +00:00
|
|
|
#ifdef USE_NFD
|
|
|
|
#include <nfd.h>
|
2022-11-06 00:24:14 +00:00
|
|
|
#elif defined(ANDROID)
|
|
|
|
#include <SDL.h>
|
2022-06-17 06:28:22 +00:00
|
|
|
#else
|
2022-03-14 03:02:50 +00:00
|
|
|
#include "../../extern/pfd-fixed/portable-file-dialogs.h"
|
2022-06-17 06:28:22 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef USE_NFD
|
|
|
|
struct NFDState {
|
2022-07-15 07:23:16 +00:00
|
|
|
bool isSave, allowMultiple;
|
2022-06-17 06:28:22 +00:00
|
|
|
String header;
|
|
|
|
std::vector<String> filter;
|
|
|
|
String path;
|
|
|
|
FileDialogSelectCallback clickCallback;
|
2022-07-15 07:23:16 +00:00
|
|
|
NFDState(bool save, String h, std::vector<String> filt, String pa, FileDialogSelectCallback cc, bool multi):
|
2022-06-17 06:28:22 +00:00
|
|
|
isSave(save),
|
2022-07-15 07:23:16 +00:00
|
|
|
allowMultiple(multi),
|
2022-06-17 06:28:22 +00:00
|
|
|
header(h),
|
|
|
|
filter(filt),
|
|
|
|
path(pa),
|
|
|
|
clickCallback(cc) {
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// TODO: filter
|
2022-07-16 06:52:15 +00:00
|
|
|
void _nfdThread(const NFDState state, std::atomic<bool>* ok, std::vector<String>* result, bool* errorOutput) {
|
2022-06-17 06:28:22 +00:00
|
|
|
nfdchar_t* out=NULL;
|
|
|
|
nfdresult_t ret=NFD_CANCEL;
|
2022-07-14 07:00:51 +00:00
|
|
|
(*errorOutput)=false;
|
2022-07-16 06:52:15 +00:00
|
|
|
nfdpathset_t paths;
|
|
|
|
|
|
|
|
result->clear();
|
2022-06-17 06:28:22 +00:00
|
|
|
|
|
|
|
if (state.isSave) {
|
2022-06-24 08:11:16 +00:00
|
|
|
ret=NFD_SaveDialog(state.filter,state.path.c_str(),&out,state.clickCallback);
|
2022-06-17 06:28:22 +00:00
|
|
|
} else {
|
2022-07-16 06:52:15 +00:00
|
|
|
if (state.allowMultiple) {
|
|
|
|
ret=NFD_OpenDialogMultiple(state.filter,state.path.c_str(),&paths,state.clickCallback);
|
|
|
|
} else {
|
|
|
|
ret=NFD_OpenDialog(state.filter,state.path.c_str(),&out,state.clickCallback);
|
|
|
|
}
|
2022-06-17 06:28:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (ret) {
|
|
|
|
case NFD_OKAY:
|
2022-07-16 06:52:15 +00:00
|
|
|
if (state.allowMultiple) {
|
|
|
|
logD("pushing multi path");
|
|
|
|
for (size_t i=0; i<NFD_PathSet_GetCount(&paths); i++) {
|
|
|
|
result->push_back(String(NFD_PathSet_GetPath(&paths,i)));
|
|
|
|
}
|
|
|
|
NFD_PathSet_Free(&paths);
|
2022-06-17 06:28:22 +00:00
|
|
|
} else {
|
2022-07-16 06:52:15 +00:00
|
|
|
logD("pushing single path");
|
|
|
|
if (out!=NULL) {
|
|
|
|
logD("we have it");
|
|
|
|
result->push_back(String(out));
|
|
|
|
}
|
2022-06-17 06:28:22 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NFD_CANCEL:
|
|
|
|
break;
|
|
|
|
case NFD_ERROR:
|
|
|
|
logE("NFD error! %s\n",NFD_GetError());
|
2022-07-14 06:59:55 +00:00
|
|
|
(*errorOutput)=true;
|
2022-06-17 06:28:22 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
logE("NFD unknown return code %d!\n",ret);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
(*ok)=true;
|
|
|
|
}
|
|
|
|
#endif
|
2022-03-13 23:32:35 +00:00
|
|
|
|
2022-07-15 07:23:16 +00:00
|
|
|
bool FurnaceGUIFileDialog::openLoad(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale, FileDialogSelectCallback clickCallback, bool allowMultiple) {
|
2022-03-13 23:32:35 +00:00
|
|
|
if (opened) return false;
|
|
|
|
saving=false;
|
|
|
|
curPath=path;
|
2022-11-25 19:33:56 +00:00
|
|
|
|
|
|
|
// strip excess directory separators
|
|
|
|
while (!curPath.empty()) {
|
|
|
|
if (curPath[curPath.size()-1]!=DIR_SEPARATOR) break;
|
|
|
|
curPath.erase(curPath.size()-1);
|
|
|
|
}
|
|
|
|
curPath+=DIR_SEPARATOR;
|
|
|
|
|
2022-04-11 09:07:01 +00:00
|
|
|
logD("opening load file dialog with curPath %s",curPath.c_str());
|
2022-03-13 23:32:35 +00:00
|
|
|
if (sysDialog) {
|
2022-06-17 06:28:22 +00:00
|
|
|
#ifdef USE_NFD
|
|
|
|
dialogOK=false;
|
2022-06-19 23:51:51 +00:00
|
|
|
#ifdef NFD_NON_THREADED
|
2022-07-15 07:23:16 +00:00
|
|
|
_nfdThread(NFDState(false,header,filter,path,clickCallback,allowMultiple),&dialogOK,&nfdResult,&hasError);
|
2022-06-19 23:51:51 +00:00
|
|
|
#else
|
2022-07-15 07:23:16 +00:00
|
|
|
dialogO=new std::thread(_nfdThread,NFDState(false,header,filter,path,clickCallback,allowMultiple),&dialogOK,&nfdResult,&hasError);
|
2022-06-19 23:51:51 +00:00
|
|
|
#endif
|
2022-11-06 00:24:14 +00:00
|
|
|
#elif defined(ANDROID)
|
|
|
|
hasError=false;
|
|
|
|
if (jniEnv==NULL) {
|
|
|
|
jniEnv=(JNIEnv*)SDL_AndroidGetJNIEnv();
|
|
|
|
if (jniEnv==NULL) {
|
|
|
|
hasError=true;
|
|
|
|
logE("could not acquire JNI env!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
jobject activity=(jobject)SDL_AndroidGetActivity();
|
|
|
|
if (activity==NULL) {
|
|
|
|
hasError=true;
|
|
|
|
logE("the Activity is NULL!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
jclass class_=jniEnv->GetObjectClass(activity);
|
|
|
|
jmethodID showFileDialog=jniEnv->GetMethodID(class_,"showFileDialog","()B");
|
|
|
|
|
|
|
|
if (showFileDialog==NULL) {
|
|
|
|
logE("method showFileDialog not found!");
|
|
|
|
hasError=true;
|
|
|
|
jniEnv->DeleteLocalRef(class_);
|
|
|
|
jniEnv->DeleteLocalRef(activity);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
jboolean mret=jniEnv->CallBooleanMethod(activity,showFileDialog);
|
|
|
|
|
|
|
|
if (!(bool)mret) {
|
|
|
|
hasError=true;
|
|
|
|
logW("could not open Android file picker...");
|
|
|
|
}
|
|
|
|
|
|
|
|
jniEnv->DeleteLocalRef(class_);
|
|
|
|
jniEnv->DeleteLocalRef(activity);
|
|
|
|
return true;
|
2022-06-17 06:28:22 +00:00
|
|
|
#else
|
2022-07-15 07:23:16 +00:00
|
|
|
dialogO=new pfd::open_file(header,path,filter,allowMultiple?(pfd::opt::multiselect):(pfd::opt::none));
|
2022-07-14 06:59:55 +00:00
|
|
|
hasError=!pfd::settings::available();
|
2022-06-17 06:28:22 +00:00
|
|
|
#endif
|
2022-03-13 23:32:35 +00:00
|
|
|
} else {
|
2022-07-14 06:59:55 +00:00
|
|
|
hasError=false;
|
2022-03-13 23:32:35 +00:00
|
|
|
ImGuiFileDialog::Instance()->DpiScale=dpiScale;
|
2022-07-15 07:23:16 +00:00
|
|
|
ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,allowMultiple?999:1,nullptr,0,clickCallback);
|
2022-03-13 23:32:35 +00:00
|
|
|
}
|
2022-03-14 04:10:43 +00:00
|
|
|
opened=true;
|
2022-03-13 23:32:35 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-03-14 04:10:43 +00:00
|
|
|
bool FurnaceGUIFileDialog::openSave(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale) {
|
|
|
|
if (opened) return false;
|
|
|
|
saving=true;
|
2022-03-13 23:32:35 +00:00
|
|
|
curPath=path;
|
2022-11-25 19:33:56 +00:00
|
|
|
|
|
|
|
// strip excess directory separators
|
|
|
|
while (!curPath.empty()) {
|
|
|
|
if (curPath[curPath.size()-1]!=DIR_SEPARATOR) break;
|
|
|
|
curPath.erase(curPath.size()-1);
|
|
|
|
}
|
|
|
|
curPath+=DIR_SEPARATOR;
|
|
|
|
|
2022-04-11 09:07:01 +00:00
|
|
|
logD("opening save file dialog with curPath %s",curPath.c_str());
|
2022-03-13 23:32:35 +00:00
|
|
|
if (sysDialog) {
|
2022-06-17 06:28:22 +00:00
|
|
|
#ifdef USE_NFD
|
|
|
|
dialogOK=false;
|
2022-06-19 23:51:51 +00:00
|
|
|
#ifdef NFD_NON_THREADED
|
2022-07-15 07:23:16 +00:00
|
|
|
_nfdThread(NFDState(true,header,filter,path,NULL,false),&dialogOK,&nfdResult,&hasError);
|
2022-06-19 23:51:51 +00:00
|
|
|
#else
|
2022-07-15 07:23:16 +00:00
|
|
|
dialogS=new std::thread(_nfdThread,NFDState(true,header,filter,path,NULL,false),&dialogOK,&nfdResult,&hasError);
|
2022-06-19 23:51:51 +00:00
|
|
|
#endif
|
2022-11-06 00:24:14 +00:00
|
|
|
#elif defined(ANDROID)
|
|
|
|
hasError=true; // TODO
|
2022-06-17 06:28:22 +00:00
|
|
|
#else
|
2022-03-14 04:10:43 +00:00
|
|
|
dialogS=new pfd::save_file(header,path,filter);
|
2022-07-14 06:59:55 +00:00
|
|
|
hasError=!pfd::settings::available();
|
2022-06-17 06:28:22 +00:00
|
|
|
#endif
|
2022-03-13 23:32:35 +00:00
|
|
|
} else {
|
2022-07-14 06:59:55 +00:00
|
|
|
hasError=false;
|
2022-03-13 23:32:35 +00:00
|
|
|
ImGuiFileDialog::Instance()->DpiScale=dpiScale;
|
2022-03-14 04:10:43 +00:00
|
|
|
ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite);
|
2022-03-13 23:32:35 +00:00
|
|
|
}
|
2022-03-14 04:10:43 +00:00
|
|
|
opened=true;
|
2022-03-13 23:32:35 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FurnaceGUIFileDialog::accepted() {
|
|
|
|
if (sysDialog) {
|
2022-07-15 07:23:16 +00:00
|
|
|
return (!fileName.empty());
|
2022-03-13 23:32:35 +00:00
|
|
|
} else {
|
|
|
|
return ImGuiFileDialog::Instance()->IsOk();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FurnaceGUIFileDialog::close() {
|
|
|
|
if (sysDialog) {
|
|
|
|
if (saving) {
|
|
|
|
if (dialogS!=NULL) {
|
2022-06-17 06:28:22 +00:00
|
|
|
#ifdef USE_NFD
|
|
|
|
dialogS->join();
|
|
|
|
#endif
|
2022-11-06 00:24:14 +00:00
|
|
|
#ifndef ANDROID
|
2022-03-13 23:32:35 +00:00
|
|
|
delete dialogS;
|
2022-11-06 00:24:14 +00:00
|
|
|
#endif
|
2022-03-13 23:32:35 +00:00
|
|
|
dialogS=NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (dialogO!=NULL) {
|
2022-06-17 06:28:22 +00:00
|
|
|
#ifdef USE_NFD
|
|
|
|
dialogO->join();
|
|
|
|
#endif
|
2022-11-06 00:24:14 +00:00
|
|
|
#ifndef ANDROID
|
2022-03-13 23:32:35 +00:00
|
|
|
delete dialogO;
|
2022-11-06 00:24:14 +00:00
|
|
|
#endif
|
2022-03-13 23:32:35 +00:00
|
|
|
dialogO=NULL;
|
|
|
|
}
|
|
|
|
}
|
2022-06-17 06:28:22 +00:00
|
|
|
#ifdef USE_NFD
|
|
|
|
dialogOK=false;
|
|
|
|
#endif
|
2022-03-13 23:32:35 +00:00
|
|
|
} else {
|
|
|
|
ImGuiFileDialog::Instance()->Close();
|
|
|
|
}
|
2022-03-14 04:10:43 +00:00
|
|
|
opened=false;
|
2022-03-13 23:32:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool FurnaceGUIFileDialog::render(const ImVec2& min, const ImVec2& max) {
|
|
|
|
if (sysDialog) {
|
2022-06-17 06:28:22 +00:00
|
|
|
#ifdef USE_NFD
|
|
|
|
if (dialogOK) {
|
2022-07-15 07:23:16 +00:00
|
|
|
fileName.clear();
|
2022-07-16 06:52:15 +00:00
|
|
|
fileName=nfdResult;
|
2022-07-15 07:23:16 +00:00
|
|
|
if (!fileName.empty()) {
|
|
|
|
size_t dsPos=fileName[0].rfind(DIR_SEPARATOR);
|
|
|
|
if (dsPos!=String::npos) curPath=fileName[0].substr(0,dsPos);
|
|
|
|
}
|
|
|
|
for (String& i: fileName) {
|
|
|
|
logD("- returning %s",i);
|
|
|
|
}
|
2022-06-17 06:28:22 +00:00
|
|
|
dialogOK=false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2022-11-06 00:24:14 +00:00
|
|
|
#elif defined(ANDROID)
|
|
|
|
// TODO: detect when file picker is closed
|
|
|
|
return false;
|
2022-06-17 06:28:22 +00:00
|
|
|
#else
|
2022-03-13 23:32:35 +00:00
|
|
|
if (saving) {
|
|
|
|
if (dialogS!=NULL) {
|
2022-03-29 21:18:38 +00:00
|
|
|
if (dialogS->ready(0)) {
|
2022-07-15 07:23:16 +00:00
|
|
|
fileName.clear();
|
|
|
|
fileName.push_back(dialogS->result());
|
|
|
|
size_t dsPos=fileName[0].rfind(DIR_SEPARATOR);
|
|
|
|
if (dsPos!=String::npos) curPath=fileName[0].substr(0,dsPos);
|
|
|
|
logD("returning %s",fileName[0]);
|
2022-03-13 23:32:35 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (dialogO!=NULL) {
|
2022-03-29 21:18:38 +00:00
|
|
|
if (dialogO->ready(0)) {
|
2022-03-13 23:32:35 +00:00
|
|
|
if (dialogO->result().empty()) {
|
2022-07-15 07:23:16 +00:00
|
|
|
fileName.clear();
|
2022-04-11 03:12:02 +00:00
|
|
|
logD("returning nothing");
|
2022-03-13 23:32:35 +00:00
|
|
|
} else {
|
2022-07-15 07:23:16 +00:00
|
|
|
fileName=dialogO->result();
|
|
|
|
if (fileName.empty()) {
|
|
|
|
// don't touch
|
|
|
|
} else {
|
|
|
|
size_t dsPos=fileName[0].rfind(DIR_SEPARATOR);
|
|
|
|
if (dsPos!=String::npos) curPath=fileName[0].substr(0,dsPos);
|
|
|
|
for (String& i: fileName) {
|
|
|
|
logD("- returning %s",i);
|
|
|
|
}
|
|
|
|
}
|
2022-03-13 23:32:35 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2022-06-17 06:28:22 +00:00
|
|
|
#endif
|
2022-03-13 23:32:35 +00:00
|
|
|
} else {
|
|
|
|
return ImGuiFileDialog::Instance()->Display("FileDialog",ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove,min,max);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-30 07:02:55 +00:00
|
|
|
bool FurnaceGUIFileDialog::isOpen() {
|
|
|
|
return opened;
|
|
|
|
}
|
|
|
|
|
2022-07-14 06:59:55 +00:00
|
|
|
bool FurnaceGUIFileDialog::isError() {
|
|
|
|
return hasError;
|
|
|
|
}
|
|
|
|
|
2022-03-13 23:32:35 +00:00
|
|
|
String FurnaceGUIFileDialog::getPath() {
|
|
|
|
if (sysDialog) {
|
2022-04-11 09:07:01 +00:00
|
|
|
if (curPath.size()>1) {
|
2022-04-11 08:14:12 +00:00
|
|
|
if (curPath[curPath.size()-1]==DIR_SEPARATOR) {
|
|
|
|
curPath=curPath.substr(0,curPath.size()-1);
|
|
|
|
}
|
|
|
|
}
|
2022-04-11 09:07:01 +00:00
|
|
|
logD("curPath: %s",curPath.c_str());
|
2022-03-13 23:32:35 +00:00
|
|
|
return curPath;
|
|
|
|
} else {
|
|
|
|
return ImGuiFileDialog::Instance()->GetCurrentPath();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-15 07:23:16 +00:00
|
|
|
std::vector<String>& FurnaceGUIFileDialog::getFileName() {
|
2022-03-13 23:32:35 +00:00
|
|
|
if (sysDialog) {
|
|
|
|
return fileName;
|
|
|
|
} else {
|
2022-07-15 07:23:16 +00:00
|
|
|
fileName.clear();
|
2022-07-16 06:52:15 +00:00
|
|
|
if (saving) {
|
|
|
|
fileName.push_back(ImGuiFileDialog::Instance()->GetFilePathName());
|
|
|
|
} else {
|
|
|
|
for (auto& i: ImGuiFileDialog::Instance()->GetSelection()) {
|
|
|
|
fileName.push_back(i.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
2022-07-15 07:23:16 +00:00
|
|
|
return fileName;
|
2022-03-13 23:32:35 +00:00
|
|
|
}
|
2022-03-14 02:19:52 +00:00
|
|
|
}
|