Open Source Tomb Raider Engine
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

imguifilesystem.cpp 72KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658
  1. // This software is provided 'as-is', without any express or implied
  2. // warranty. In no event will the authors be held liable for any damages
  3. // arising from the use of this software.
  4. // Permission is granted to anyone to use this software for any purpose,
  5. // including commercial applications, and to alter it and redistribute it
  6. // freely, subject to the following restrictions:
  7. // 1. The origin of this software must not be misrepresented; you must not
  8. // claim that you wrote the original software. If you use this software
  9. // in a product, an acknowledgment in the product documentation would be
  10. // appreciated but is not required.
  11. // 2. Altered source versions must be plainly marked as such, and must not be
  12. // misrepresented as being the original software.
  13. // 3. This notice may not be removed or altered from any source distribution.
  14. #include "imguifilesystem.h"
  15. #ifdef _WIN32
  16. #include <shlobj.h> // Known Directory locations
  17. # ifndef CSIDL_MYPICTURES
  18. # define CSIDL_MYPICTURES 0x0027
  19. # endif //CSIDL_MYPICTURES
  20. # ifndef CSIDL_MYMUSIC
  21. # define CSIDL_MYMUSIC 0x000d
  22. # endif //CSIDL_MYMUSIC
  23. # ifndef CSIDL_MYVIDEO
  24. # define CSIDL_MYVIDEO 0x000e
  25. # endif //CSIDL_MYVIDEO
  26. #else // _WIN32
  27. # include <unistd.h> // getpwuid
  28. # include <pwd.h> // getenv ?
  29. #endif //#ifdef _WIN32
  30. #include "dirent_portable.h"
  31. #include <sys/stat.h>
  32. #include <ctype.h> // tolower,...
  33. #include <string.h> // strcmp
  34. #include <stdio.h> // FILENAME_MAX
  35. #include <new> // operator new
  36. #ifndef PATH_MAX
  37. #define PATH_MAX 4096
  38. #endif
  39. namespace ImGuiFs {
  40. const int MAX_FILENAME_BYTES = FILENAME_MAX;
  41. const int MAX_PATH_BYTES = PATH_MAX;
  42. enum Sorting {
  43. SORT_ORDER_ALPHABETIC=0,
  44. SORT_ORDER_ALPHABETIC_INVERSE=1,
  45. SORT_ORDER_LAST_MODIFICATION=2,
  46. SORT_ORDER_LAST_MODIFICATION_INVERSE=3,
  47. SORT_ORDER_SIZE=4,
  48. SORT_ORDER_SIZE_INVERSE=5,
  49. SORT_ORDER_TYPE=6,
  50. SORT_ORDER_TYPE_INVERSE=7,
  51. SORT_ORDER_COUNT
  52. };
  53. // Definitions of some helper classes (String,Path,SortingHelper,Directory). Better not expose them in the header file----------
  54. /*
  55. // MAIN ISSUE: All these string handling methods work with ASCII strings,
  56. // but the ones returned by dirent are multibyte OS dependant strings.
  57. // That means that there are some limitations:
  58. // LIMITATIONS:
  59. // -> paths with '/','\\','.' bytes (and possibly a few others) inside multibyte codepoints are not supported (*)
  60. // -> file extensions composed by characters with more than one byte are not supported (**)
  61. //(*) That's because when I call something like: mystring.find_last_of('/') or Path::Split('/')
  62. // the result might not be correct if there's some multibyte codepoint that includes that byte(s) (bytes include '/','\\','.').
  63. // (**) String::ToLower() deeply breaks any multibyte char.
  64. // They're currently used only in:
  65. // Path::GetExtension(...)
  66. // Directory::GetFiles(const string& path,const string& wantedExtensions,const string& unwantedExtensions)
  67. // That's because file extensions must be returned lowercase, so that e.g. ".PNG" and ".png" can be string matched (even on case sensitive file systems).
  68. */
  69. class String {
  70. protected:
  71. String() {}
  72. public:
  73. inline static void PushBack(ImVector<char[MAX_FILENAME_BYTES]>& rv,const char* s) {
  74. const size_t sz = rv.size();
  75. rv.resize(sz+1);
  76. strcpy(&rv[sz][0], s ? s : "\0");
  77. }
  78. # if (FILENAME_MAX!=PATH_MAX) // Will this work ? (I don't want to use templates)
  79. inline static void PushBack(ImVector<char[MAX_PATH_BYTES]>& rv,const char* s) {
  80. const size_t sz = rv.size();
  81. rv.resize(sz+1);
  82. strcpy(&rv[sz][0], s ? s : "\0");
  83. }
  84. # endif //#if (FILENAME_MAX!=PATH_MAX)
  85. inline static void Substr(const char* text,char* rv,int start,int count=-1) {
  86. if (!text) count=0;
  87. if (count<0) count = (int) strlen(text) - start;
  88. if (count>0) strncpy(rv,&text[start],count);
  89. rv[count]='\0';
  90. }
  91. inline static int Find(const char* text,const char toFind,int beg=0) {
  92. if (!text) return -1;
  93. for (size_t i=beg,len=strlen(text);i<len;i++) {
  94. if (text[i]==toFind) return i;
  95. }
  96. return -1;
  97. }
  98. inline static int FindLastOf(const char* text,const char toFind) {
  99. if (!text) return -1;
  100. for (int i=(int)strlen(text)-1;i>=0;i--) {
  101. if (text[i]==toFind) return i;
  102. }
  103. return -1;
  104. }
  105. inline static void ToLower(const char* text,char* rv) {
  106. if (!text) {
  107. rv[0]='\0';
  108. return;
  109. }
  110. const size_t len = strlen(text);
  111. for (size_t i=0;i<len;i++) {
  112. rv[i]=tolower(text[i]);
  113. }
  114. rv[len]='\0';
  115. }
  116. inline static void ToLowerInPlace(char* text) {
  117. if (!text) return;
  118. for (size_t i=0,len = strlen(text);i<len;i++) {
  119. char& c = text[i];
  120. c=tolower(c);
  121. }
  122. }
  123. inline static void Split(const char* text,ImVector<char[MAX_FILENAME_BYTES]>& rv,const char c=' ') {
  124. rv.clear();
  125. if (!text) return;
  126. const int len = (int)strlen(text);
  127. if (len==0) return;
  128. int beg = 0;char tmp[MAX_FILENAME_BYTES];
  129. for (int i=0;i<len;i++) {
  130. const char ch = text[i];
  131. if (ch==c) {
  132. Substr(text,tmp,beg,i-beg);
  133. PushBack(rv,tmp);
  134. beg = i+1;
  135. }
  136. }
  137. if (beg<len) {
  138. Substr(text,tmp,beg,len-beg);
  139. PushBack(rv,tmp);
  140. }
  141. }
  142. inline static void Replace(const char* baseText,const char textToReplace,const char replacement,char* rv) {
  143. strcpy(rv,baseText);
  144. ReplaceInPlace(rv,textToReplace,replacement);
  145. }
  146. inline static void ReplaceInPlace(char* text,const char toReplace,const char replacement) {
  147. if (!text) return;
  148. for (size_t i=0,len = strlen(text);i<len;i++) {
  149. char& c = text[i];
  150. if (c==toReplace) c=replacement;
  151. }
  152. }
  153. # ifdef _WIN32
  154. // Convert a wide Unicode string to an UTF8 string
  155. inline static void wide_to_utf8(const wchar_t* wstr,char* rv) {
  156. rv[0]='\0';
  157. if (!wstr) return;
  158. int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
  159. WideCharToMultiByte (CP_UTF8, 0, wstr, -1, &rv[0], size_needed, NULL, NULL);
  160. //rv[size_needed]='\0'; // If the parameter after wstr is -1, the function processes the entire input string, including the terminating null character. Therefore, the resulting character string has a terminating null character, and the length returned by the function includes this character.
  161. return ;
  162. }
  163. // Convert an UTF8 string to a wide Unicode String
  164. inline static void utf8_to_wide(const char* str,wchar_t* rv) {
  165. rv[0]=L'\0';
  166. if (!str) return;
  167. int size_needed = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
  168. MultiByteToWideChar (CP_UTF8, 0, str, -1, &rv[0], size_needed);
  169. //rv[size_needed]=L'\0'; // // If the parameter after str is -1, the function processes the entire input string, including the terminating null character. Therefore, the resulting character string has a terminating null character, and the length returned by the function includes this character.
  170. return;
  171. }
  172. # endif // _WIN32
  173. };
  174. class Path {
  175. protected:
  176. Path() {}
  177. public:
  178. static void GetAbsolutePath(const char *path, char *rv) {
  179. rv[0]='\0';
  180. # ifndef _WIN32
  181. if (!path || strlen(path)==0) realpath("./", rv);
  182. else realpath(path, rv);
  183. # else //_WIN32
  184. static const int bufferSize = PATH_MAX+1; // 4097 is good (PATH_MAX should be in <limits.h>, or something like that)
  185. static wchar_t buffer[bufferSize];
  186. static wchar_t wpath[bufferSize];
  187. String::utf8_to_wide(path ? path : "",wpath);
  188. ::GetFullPathNameW(&wpath[0],bufferSize,&buffer[0],NULL);
  189. String::wide_to_utf8(&buffer[0],rv);
  190. String::ReplaceInPlace(rv,'\\','/');
  191. size_t len;
  192. while ( (len=strlen(rv))>0 && rv[len-1]=='/') rv[len-1]='\0';
  193. //fprintf(stderr,"AbsolutePath = \"%s\" (len:%d)\n",rv,(int) strlen(rv)); // TO remove!
  194. # endif // _WIN32
  195. }
  196. static void GetDirectoryName(const char *filePath, char *rv) {
  197. rv[0]='\0';if (!filePath) return;
  198. const int sz = strlen(filePath);
  199. if (sz==0 || strcmp(filePath,"/")==0 || strcmp(filePath,"\\")==0) {
  200. strcpy(rv,filePath);
  201. return;
  202. }
  203. const char c = filePath[sz-1];
  204. if (c == '/' || c=='\\') {
  205. char tmp[MAX_PATH_BYTES];
  206. String::Substr(filePath,tmp,0,sz-1);
  207. GetDirectoryName(tmp,rv);
  208. return;
  209. }
  210. if (c==':') {
  211. strcpy(rv,filePath);
  212. return;
  213. }
  214. int beg=String::FindLastOf(filePath,'\\');
  215. int beg2=String::FindLastOf(filePath,'/');
  216. beg=(beg>beg2?beg:beg2);
  217. if (beg==0) {
  218. String::Substr(filePath,rv,0,1);
  219. return;
  220. }
  221. if (beg!=-1) {
  222. String::Substr(filePath,rv,0,beg);
  223. return;
  224. }
  225. rv[0]='\0';
  226. return;
  227. }
  228. static void GetFileName(const char *filePath, char *rv) {
  229. int beg=String::FindLastOf(filePath,'\\');
  230. int beg2=String::FindLastOf(filePath,'/');
  231. beg=(beg>beg2?beg:beg2);
  232. if (beg!=-1) {
  233. String::Substr(filePath,rv,beg+1);
  234. return;
  235. }
  236. strcpy(rv,filePath);
  237. return;
  238. }
  239. static void GetExtension(const char* filePath,char *rv) {
  240. int beg=String::FindLastOf(filePath,'.');
  241. int beg2=String::FindLastOf(filePath,'/');
  242. int beg3=String::FindLastOf(filePath,'\\');
  243. if (beg2!=-1) {
  244. if (beg3!=-1) beg2 = beg3;
  245. else beg2 = beg2 > beg3 ? beg2 : beg3;
  246. }
  247. else if (beg3!=-1) beg2 = beg3;
  248. else {
  249. if (beg!=-1) {
  250. String::Substr(filePath,rv,beg);
  251. String::ToLowerInPlace(rv);
  252. return;
  253. }
  254. }
  255. if (beg>beg2) {
  256. if (beg!=-1) {
  257. String::Substr(filePath,rv,beg);
  258. String::ToLowerInPlace(rv);
  259. return;
  260. }
  261. }
  262. rv[0]='\0';
  263. return;
  264. }
  265. static void Combine(const char* directory,const char* fileName,char* rv,bool appendMode=true) {
  266. if (!appendMode) rv[0]='\0';
  267. const size_t size= directory ? strlen(directory) : 0;
  268. if (size==0) {
  269. strcat(rv,fileName);
  270. return;
  271. }
  272. strcat(rv,directory);
  273. if (directory[size-1]!='\\' && directory[size-1]!='/') {
  274. strcat(rv,"/");
  275. strcat(rv,fileName);
  276. }
  277. else strcat(rv,fileName);
  278. return;
  279. }
  280. static void Append(const char* directory,char* rv) {
  281. if (!directory || strlen(directory)==0) return;
  282. size_t size = strlen(rv);
  283. if (size>0 && (rv[size-1]!='\\' && rv[size-1]!='/')) {strcat(rv,"/");++size;}
  284. strcat(rv,directory);
  285. size = strlen(rv);
  286. while (size>0 && (rv[size-1]=='\\' || rv[size-1]=='/')) {rv[size-1]='\0';--size;}
  287. if (size==0 || rv[size-1]==':') strcat(rv,"/");
  288. }
  289. static void Split(const char* path,ImVector<char[MAX_FILENAME_BYTES]>& rv,bool leaveIntermediateTrailingSlashes=true) {
  290. rv.clear();
  291. static char tex[MAX_PATH_BYTES];
  292. String::Replace(path,'\\','/',tex);
  293. size_t len = strlen(tex);
  294. static char tex2[MAX_PATH_BYTES];
  295. # ifdef _WIN32
  296. while ((len = strlen(tex))>0 && tex[len-1]=='/') {
  297. strncpy(tex2,tex,len+1);
  298. String::Substr(tex2,tex,0,len-1);
  299. }
  300. # endif //_WIN32
  301. if (len==0) return;
  302. int beg=-1;
  303. while ( (beg = String::Find(tex,'/'))!=-1) {
  304. static char tmp[MAX_FILENAME_BYTES];
  305. String::Substr(tex,tmp,0,leaveIntermediateTrailingSlashes ? beg+1 : beg);
  306. String::PushBack(rv,tmp);
  307. strcpy(tex2,tex);
  308. String::Substr(tex2,tex,beg+1);
  309. }
  310. String::PushBack(rv,tex);
  311. if (rv.size()>0 && strlen(rv[0])==0) strcpy((char*)&rv[0][0],"/\0");
  312. # ifdef _WIN32
  313. if (rv.size()==1 && strlen(rv[0])>0 && rv[0][strlen(rv[0])-1]==':') strcat((char*)&rv[0][0],"/");
  314. # endif //_WIN32
  315. return;
  316. }
  317. /*
  318. inline static bool Exists(const char* path) {
  319. struct stat statbuf;
  320. return (stat(path, &statbuf) != -1);
  321. }
  322. */
  323. };
  324. /*
  325. class File {
  326. public:
  327. inline static bool Exists(const char* filePath) {
  328. struct stat statbuf;
  329. return (stat(path, &statbuf) != -1 && (S_ISREG(statbuf.st_mode)));// || (acceptLinks ? S_ISLNK(statbuf.st_mode) : 1));
  330. }
  331. protected:
  332. File() {}
  333. };
  334. */
  335. class SortingHelper {
  336. public:
  337. typedef int (*SorterSignature)(const struct dirent **e1,const struct dirent **e2);
  338. inline static const SorterSignature& SetSorter(Sorting sorting) {
  339. const int isort =(int) sorting;
  340. if (isort>=0 && isort<(int)SORT_ORDER_COUNT) return (sorter = Sorters[isort]);
  341. return (sorter = Sorters[0]);
  342. }
  343. protected:
  344. SortingHelper() {}
  345. const static SorterSignature Sorters[];
  346. static struct stat stat1;
  347. static struct stat stat2;
  348. static SorterSignature sorter;
  349. # ifdef _MSC_VER
  350. // Never tested (I've just been told that cl.exe does not have strcasecmp: please search the web for other possible alternative implementations)
  351. inline static int strcasecmp( const char *s1, const char *s2 ) {
  352. return _stricmp(s1,s2);
  353. //return lstrcmpiA(s1,s2); // Not sure this is better
  354. }
  355. # endif //_MSC_VER
  356. // Possible problem: sorting is in ASCII with these methods
  357. static int Alphasort(const struct dirent **e1,const struct dirent **e2) {
  358. return strcasecmp((*e1)->d_name,(*e2)->d_name);
  359. }
  360. static int Alphasortinverse (const struct dirent **e1,const struct dirent **e2) {
  361. return -strcasecmp((*e1)->d_name,(*e2)->d_name);
  362. }
  363. // Please note that calling stat(...) every time inside sorters is a suicide! And that I'm doing right that! (but I guess and hope that on many systems the calls get cached somewhere: otherwise it would take ages to sort)
  364. static int Lastmodsort (const struct dirent **e1,const struct dirent **e2) {
  365. if (stat((*e1)->d_name,&stat1)==-1) return -1;
  366. if (stat((*e2)->d_name,&stat2)==-1) return 1;
  367. return (stat1.st_mtime < stat2.st_mtime ? -1 : stat1.st_mtime > stat2.st_mtime ? 1 : 0);
  368. }
  369. static int Lastmodsortinverse(const struct dirent **e1,const struct dirent **e2) {
  370. if (stat((*e1)->d_name,&stat1)==-1) return 1;
  371. if (stat((*e2)->d_name,&stat2)==-1) return -1;
  372. return (stat2.st_mtime < stat1.st_mtime ? -1 : stat2.st_mtime > stat1.st_mtime ? 1 : 0);
  373. }
  374. static int Sizesort (const struct dirent **e1,const struct dirent **e2) {
  375. if (stat((*e1)->d_name,&stat1)==-1) return -1;
  376. if (stat((*e2)->d_name,&stat2)==-1) return 1;
  377. return (stat1.st_size < stat2.st_size ? -1 : stat1.st_size > stat2.st_size ? 1 : 0);
  378. }
  379. static int Sizesortinverse(const struct dirent **e1,const struct dirent **e2) {
  380. if (stat((*e1)->d_name,&stat1)==-1) return 1;
  381. if (stat((*e2)->d_name,&stat2)==-1) return -1;
  382. return (stat2.st_size < stat1.st_size ? -1 : stat2.st_size > stat1.st_size ? 1 : 0);
  383. }
  384. // Please note that calculating the file extension every time inside sorters is a suicide (well, much less than before...)!
  385. static int Typesort(const struct dirent **e1,const struct dirent **e2) {
  386. static const int dot = (int) '.';
  387. const char * p1 = strrchr((const char*) (*e1)->d_name, dot );
  388. const char * p2 = strrchr((const char*) (*e2)->d_name, dot );
  389. if (!p1) return (!p2?0:-1);
  390. else if (!p2) return 1;
  391. return strcasecmp(p1,p2);
  392. }
  393. static int Typesortinverse (const struct dirent **e1,const struct dirent **e2) {
  394. static const int dot = (int) '.';
  395. const char * p1 = strrchr((const char*) (*e1)->d_name, dot );
  396. const char * p2 = strrchr((const char*) (*e2)->d_name, dot );
  397. if (!p1) return (!p2?0:1);
  398. else if (!p2) return -1;
  399. return -strcasecmp(p1,p2);
  400. }
  401. };
  402. const SortingHelper::SorterSignature SortingHelper::Sorters[] = {&SortingHelper::Alphasort,&SortingHelper::Alphasortinverse,&SortingHelper::Lastmodsort,&SortingHelper::Lastmodsortinverse,&SortingHelper::Sizesort,&SortingHelper::Sizesortinverse,&Typesort,&SortingHelper::Typesortinverse};
  403. SortingHelper::SorterSignature SortingHelper::sorter;
  404. struct stat SortingHelper::stat1;
  405. struct stat SortingHelper::stat2;
  406. class Directory {
  407. public:
  408. static void GetDirectories(const char* directoryName,ImVector<char[MAX_PATH_BYTES]>& result,ImVector<char[MAX_FILENAME_BYTES]>* pOptionalNamesOut=NULL,Sorting sorting= SORT_ORDER_ALPHABETIC) {
  409. result.clear();if (pOptionalNamesOut) pOptionalNamesOut->clear();
  410. static char tempString[MAX_PATH_BYTES];size_t sz;
  411. struct dirent **eps = NULL;
  412. sz = strlen(directoryName);
  413. static char directoryName2[MAX_PATH_BYTES];
  414. strcpy(directoryName2,directoryName);
  415. # ifdef _WIN32
  416. if (sz>0 && directoryName[sz-1]==':') {directoryName2[sz]='\\';directoryName2[sz+1]='\0';}
  417. # endif //_WIN32
  418. const int n = scandir (directoryName2, &eps, DirentGetDirectories, SortingHelper::SetSorter(sorting));
  419. static char directoryNameWithoutSlash[MAX_PATH_BYTES];
  420. if (sz>0 && directoryName[sz-1] == '/') String::Substr(directoryName,directoryNameWithoutSlash,0,sz-1);
  421. else strcpy(directoryNameWithoutSlash,directoryName);
  422. if (n >= 0) {
  423. result.reserve((size_t)n);if (pOptionalNamesOut) pOptionalNamesOut->reserve((size_t)n);
  424. for (int cnt = 0; cnt < n; ++cnt) {
  425. const char* pName = &eps[cnt]->d_name[0];
  426. sz = strlen(pName);
  427. if (sz==0) continue;
  428. if (strcmp(pName,".")!=0 && strcmp(pName,"..")!=0 && pName[0]!='.' && pName[sz-1]!='~') {
  429. strcpy(tempString,directoryNameWithoutSlash);
  430. strcat(tempString,"/");
  431. strcat(tempString,pName);
  432. String::PushBack(result,tempString);
  433. if (pOptionalNamesOut) String::PushBack(*pOptionalNamesOut,pName);
  434. }
  435. }
  436. }
  437. if (eps) {free(eps);eps=NULL;}
  438. }
  439. static void GetFiles(const char* directoryName,ImVector<char[MAX_PATH_BYTES]>& result,ImVector<char[MAX_FILENAME_BYTES]>* pOptionalNamesOut=NULL, Sorting sorting= SORT_ORDER_ALPHABETIC) {
  440. result.clear();if (pOptionalNamesOut) pOptionalNamesOut->clear();
  441. static char tempString[MAX_PATH_BYTES];size_t sz;
  442. struct dirent **eps = NULL;
  443. sz = strlen(directoryName);
  444. static char directoryName2[MAX_PATH_BYTES];
  445. strcpy(directoryName2,directoryName);
  446. # ifdef _WIN32
  447. if (sz>0 && directoryName[sz-1]==':') {directoryName2[sz]='\\';directoryName2[sz+1]='\0';}
  448. # endif //_WIN32
  449. const int n = scandir (directoryName2, &eps, DirentGetFiles, SortingHelper::SetSorter(sorting));
  450. static char directoryNameWithoutSlash[MAX_PATH_BYTES];
  451. if (sz>0 && directoryName[sz-1] == '/') String::Substr(directoryName,directoryNameWithoutSlash,0,sz-1);
  452. else strcpy(directoryNameWithoutSlash,directoryName);
  453. if (n >= 0) {
  454. result.reserve((size_t)n);if (pOptionalNamesOut) pOptionalNamesOut->reserve((size_t)n);
  455. for (int cnt = 0; cnt < n; ++cnt) {
  456. const char* pName = &eps[cnt]->d_name[0];
  457. sz = strlen(pName);
  458. if (sz==0) continue;
  459. if (pName[0]!='.' && pName[sz-1]!='~') {
  460. strcpy(tempString,directoryNameWithoutSlash);
  461. strcat(tempString,"/");
  462. strcat(tempString,pName);
  463. String::PushBack(result,tempString);
  464. if (pOptionalNamesOut) String::PushBack(*pOptionalNamesOut,pName);
  465. }
  466. }
  467. }
  468. if (eps) {free(eps);eps=NULL;}
  469. }
  470. // e.g. ".txt;.jpg;.png". To use unwantedExtensions, set wantedExtensions="".
  471. static void GetFiles(const char* path,ImVector<char[MAX_PATH_BYTES]>& files,const char* wantedExtensions,const char* unwantedExtensions=NULL,ImVector<char[MAX_FILENAME_BYTES]>* pOptionalNamesOut=NULL,Sorting sorting= SORT_ORDER_ALPHABETIC) {
  472. ImVector<char[MAX_PATH_BYTES]> filesIn;
  473. ImVector<char[MAX_FILENAME_BYTES]> namesIn;
  474. GetFiles(path,filesIn,&namesIn,sorting);
  475. if ((wantedExtensions==0 || strlen(wantedExtensions)==0) && (unwantedExtensions==0 || strlen(unwantedExtensions)==0)) {files = filesIn;return;}
  476. files.clear();if (pOptionalNamesOut) pOptionalNamesOut->clear();
  477. char wext[MAX_PATH_BYTES];String::ToLower(wantedExtensions,wext);
  478. char woext[MAX_PATH_BYTES];String::ToLower(unwantedExtensions,woext);
  479. char ext[MAX_PATH_BYTES];
  480. if (wantedExtensions && strlen(wantedExtensions)>0) {
  481. files.reserve(filesIn.size());if (pOptionalNamesOut) pOptionalNamesOut->reserve(namesIn.size());
  482. ImVector<char[MAX_FILENAME_BYTES]> wExts;String::Split(wext,wExts,';');
  483. const size_t wExtsSize = wExts.size();
  484. if (wExtsSize>0) {
  485. for (size_t i = 0,sz = filesIn.size();i<sz;i++) {
  486. Path::GetExtension(filesIn[i],ext);
  487. for (size_t e=0;e<wExtsSize;e++) {
  488. if (strcmp(ext,wExts[e])==0) {
  489. String::PushBack(files,filesIn[i]);
  490. if (pOptionalNamesOut) String::PushBack(*pOptionalNamesOut,namesIn[i]);
  491. }
  492. }
  493. }
  494. }
  495. else return;
  496. }
  497. else if (unwantedExtensions && strlen(unwantedExtensions)>0) {
  498. files.reserve(filesIn.size());if (pOptionalNamesOut) pOptionalNamesOut->reserve(namesIn.size());
  499. ImVector<char[MAX_FILENAME_BYTES]> woExts;String::Split(woext,woExts,';');
  500. const size_t woExtsSize = woExts.size();
  501. if (woExts.size()==0) {files = filesIn;return;}
  502. bool match;
  503. for (size_t i = 0,sz = filesIn.size();i<sz;i++) {
  504. Path::GetExtension(filesIn[i],ext);
  505. match = false;
  506. for (size_t e=0;e<woExtsSize;e++) {
  507. if (strcmp(ext,woExts[e])==0) {
  508. match = true;
  509. break;
  510. }
  511. }
  512. if (!match) {
  513. String::PushBack(files,filesIn[i]);
  514. if (pOptionalNamesOut) String::PushBack(*pOptionalNamesOut,namesIn[i]);
  515. }
  516. }
  517. }
  518. else {
  519. files = filesIn;
  520. if (pOptionalNamesOut) *pOptionalNamesOut = namesIn;
  521. }
  522. }
  523. inline static void Create(const char* directoryName) {
  524. # ifndef _WIN32
  525. const mode_t mode = S_IFDIR | S_IREAD | S_IWRITE | S_IRWXU | S_IRWXG | S_IRWXO;
  526. mkdir(directoryName,mode);
  527. # else //_WIN32
  528. static wchar_t name[PATH_MAX+1];
  529. String::utf8_to_wide(directoryName,name);
  530. ::CreateDirectoryW(name,NULL);
  531. # endif //_WIN32
  532. }
  533. inline static bool Exists(const char* path) {
  534. struct stat statbuf;
  535. return (stat(path, &statbuf) != -1 && S_ISDIR(statbuf.st_mode));
  536. }
  537. inline static const ImVector<char[MAX_PATH_BYTES]> &GetUserKnownDirectories(const ImVector<char[MAX_FILENAME_BYTES]> **pOptionalUserKnownDirectoryDisplayNamesOut,const int** pOptionalNumberKnownUserDirectoriesExceptDrives=NULL,bool forceUpdate=false) {
  538. static bool init = false;
  539. static ImVector<char[MAX_PATH_BYTES]> rv;
  540. static ImVector<char[MAX_FILENAME_BYTES]> dn;
  541. static ImVector<char[MAX_PATH_BYTES]> mediaFolders;
  542. static int numberKnownUserDirectoriesExceptDrives = 0;
  543. if (pOptionalUserKnownDirectoryDisplayNamesOut) *pOptionalUserKnownDirectoryDisplayNamesOut = &dn;
  544. if (pOptionalNumberKnownUserDirectoriesExceptDrives) *pOptionalNumberKnownUserDirectoriesExceptDrives = &numberKnownUserDirectoriesExceptDrives;
  545. if (init && !forceUpdate) return rv;
  546. init = true;
  547. rv.clear();dn.clear();
  548. # ifdef _WIN32
  549. static const int csid[] = {
  550. CSIDL_DESKTOP,
  551. CSIDL_PERSONAL, //(Documents)
  552. CSIDL_FAVORITES,
  553. CSIDL_MYMUSIC,
  554. CSIDL_MYPICTURES,
  555. CSIDL_RECENT,
  556. CSIDL_MYVIDEO
  557. };
  558. static const char* name[] = {
  559. "Desktop",
  560. "Documents",
  561. "Favorites",
  562. "Music",
  563. "Pictures",
  564. "Recent",
  565. "Video"
  566. };
  567. static const int csidSize = sizeof(csid)/sizeof(csid[0]);
  568. static const int nameSize = sizeof(name)/sizeof(name[0]);
  569. IM_ASSERT(csidSize==nameSize);
  570. if (csidSize!=nameSize) fprintf(stderr,"ERROR in file: imguifilesystem.cpp. Directory::GetUserKnownDirectories(...) csidSize!=nameSize.\n");
  571. char tmp[MAX_PATH_BYTES] = "C:/";while (tmp[0]<='Z') {if (Directory::Exists(tmp)) String::PushBack(mediaFolders,tmp);tmp[0]=(char)((int)tmp[0]+1);}
  572. rv.reserve(csidSize+mediaFolders.size());
  573. dn.reserve(csidSize+mediaFolders.size());
  574. WCHAR path[MAX_PATH+1];
  575. for (int i=0;i<csidSize;i++) {
  576. if (!GetSpecialFolderPathW(csid[i],&path[0],NULL)) continue;
  577. static char tmp2[MAX_PATH_BYTES];
  578. String::wide_to_utf8(&path[0],tmp2);
  579. String::PushBack(rv,tmp2);
  580. String::PushBack(dn,name[i]);
  581. }
  582. numberKnownUserDirectoriesExceptDrives = (int) rv.size();
  583. static char mediaFolderName[MAX_PATH_BYTES];
  584. for (int i=0,msz=mediaFolders.size();i<msz;i++) {
  585. const char* mediaFolder = mediaFolders[i];
  586. String::PushBack(rv,mediaFolder);
  587. String::Substr(mediaFolder,mediaFolderName,0,strlen(mediaFolder)-1);
  588. String::PushBack(dn,mediaFolderName);
  589. }
  590. # else //_WIN32
  591. const char* homedir = NULL;
  592. if ((homedir = getenv("HOME")) == NULL) {
  593. homedir = getpwuid(getuid())->pw_dir;
  594. }
  595. if (homedir==NULL) return rv;
  596. char homeString[MAX_PATH_BYTES];strcpy(homeString,homedir);
  597. char userString[MAX_PATH_BYTES];Path::GetFileName(homeString,userString);
  598. // Known folders ---------------------------------------------
  599. static const char folder[][MAX_FILENAME_BYTES] = {
  600. "Desktop",
  601. "Documents",
  602. "Downloads",
  603. "Music",
  604. "Pictures",
  605. "Videos"
  606. };
  607. static const int folderSize = sizeof(folder)/sizeof(folder[0]);
  608. rv.reserve(folderSize+1);
  609. dn.reserve(rv.size());
  610. String::PushBack(rv,homeString);
  611. char temp[MAX_PATH_BYTES];
  612. strcpy(temp,"Home");
  613. String::PushBack(dn,temp);
  614. for (int i=0;i<folderSize;i++) {
  615. Path::Combine(homeString,folder[i],temp,false);
  616. if (Directory::Exists(temp)) {
  617. String::PushBack(rv,temp);
  618. String::PushBack(dn,folder[i]);
  619. }
  620. }
  621. numberKnownUserDirectoriesExceptDrives = (int) rv.size();
  622. // Additional Drives --------------------------------------------
  623. static const char* mountLocations[] = {"/media","/mnt","/Volumes","/vol","/data"};
  624. static const int mountLocationSize = sizeof(mountLocations)/sizeof(mountLocations[0]);
  625. static const bool ifHomeSubfolerIsFoundInMountLocationsForgetThatRootMountLocation = true; // That means: if "/media/myusername" exists, don't add any other "/media/..." entries.
  626. char userMediaString[MAX_PATH_BYTES];bool lastGood = false;
  627. for (int mntLocIndex=0,sz = 2*mountLocationSize;mntLocIndex<sz;mntLocIndex++) {
  628. const int index = mntLocIndex/2;
  629. const char* mntLocString = mountLocations[index];
  630. const bool useUserSuffix = (mntLocIndex%2)==0;
  631. if (useUserSuffix) {
  632. Path::Combine(mntLocString,userString,userMediaString,false);
  633. strcpy(temp,userMediaString);
  634. }
  635. else if (lastGood && ifHomeSubfolerIsFoundInMountLocationsForgetThatRootMountLocation) {lastGood = false;continue;} // see "ifHomeSubfolerIsFoundInMountLocationsForgetThatRootMountLocation" above
  636. else strcpy(userMediaString,mntLocString);
  637. lastGood = Directory::Exists(userMediaString);
  638. if (!lastGood) continue;
  639. Directory::GetDirectories(userMediaString,mediaFolders);
  640. if (mediaFolders.size()==0) continue;
  641. rv.reserve(rv.size()+mediaFolders.size());
  642. dn.reserve(rv.size());
  643. for (int i=0,msz=mediaFolders.size();i<msz;i++) {
  644. if (strcmp(mediaFolders[i],temp)==0) continue; // I we have processed "/media/myusername" once cycle before, exclude it from processing "/media" subfolders
  645. String::PushBack(rv,mediaFolders[i]);
  646. static char tmp[MAX_FILENAME_BYTES];
  647. Path::GetFileName(mediaFolders[i],tmp);
  648. String::PushBack(dn,tmp);
  649. }
  650. }
  651. # endif //_WIN32
  652. return rv;
  653. }
  654. protected:
  655. Directory() {}
  656. static int DirentGetDirectories(const struct dirent *de) {
  657. if (de->d_type==DT_DIR) return 1;
  658. return 0;
  659. }
  660. static int DirentGetFiles(const struct dirent *de) {
  661. if (de->d_type==DT_REG) return 1;
  662. return 0;
  663. }
  664. # ifdef _WIN32
  665. static bool GetSpecialFolderPathW(int specialFolderCSIDL,WCHAR* pathOutWithSizeMaxPathPlusOne,HWND parent) {
  666. // CSIDL_DESKTOP,CSIDL_BITBUCKET,CSIDL_CONTROLS,CSIDL_DESKTOP,CSIDL_DESKTOPDIRECTORY,
  667. // CSIDL_DRIVES,CSIDL_FONTS,CSIDL_NETHOOD,CSIDL_NETWORK,CSIDL_PERSONAL (Documents)
  668. // CSIDL_PRINTERS,CSIDL_PROGRAMS,CSIDL_RECENT,CSIDL_SENDTO,CSIDL_STARTMENU,
  669. // CSIDL_STARTUP,CSIDL_TEMPLATES
  670. // CSIDL_INTERNET_CACHE,CSIDL_COOKIES,CSIDL_HISTORY,CSIDL_COMMON_APPDATA,
  671. // CSIDL_WINDOWS,CSIDL_SYSTEM,CSIDL_PROGRAM_FILES,CSIDL_MYPICTURES,...
  672. WCHAR* temp_path = pathOutWithSizeMaxPathPlusOne;//[MAX_PATH+1];
  673. temp_path[0]=L'\0';
  674. LPITEMIDLIST pidl=NULL;
  675. if (!SUCCEEDED(::SHGetSpecialFolderLocation(parent,specialFolderCSIDL, &pidl)))
  676. {
  677. temp_path[0]=L'\0';return false;
  678. }
  679. bool ok=SUCCEEDED(::SHGetPathFromIDListW(pidl,&temp_path[0]));
  680. LPMALLOC mal = NULL;
  681. if ( ::SHGetMalloc( & mal ) == E_FAIL || !mal ) ::free( pidl );
  682. else
  683. {
  684. mal->Free( pidl );
  685. mal->Release();
  686. }
  687. if (!ok)
  688. {
  689. temp_path[0]=L'\0';return false;
  690. }
  691. return true;
  692. }
  693. # endif //_WIN32
  694. };
  695. // End definitions of some helper classes----------------------------------------------------------------------------------------
  696. // Internal usage----------------------------------------------------------------------------------------
  697. struct FolderInfo {
  698. char fullFolder[MAX_PATH_BYTES];
  699. char currentFolder[MAX_PATH_BYTES];
  700. int splitPathIndex;
  701. static ImVector<char[MAX_FILENAME_BYTES]> SplitPath; // tmp field used internally
  702. void display() const {
  703. fprintf(stderr,"fullFolder=\"%s\" currentFolder=\"%s\" splitPathIndex=%d\n",fullFolder,currentFolder,splitPathIndex);
  704. }
  705. void getSplitPath(ImVector<char[MAX_FILENAME_BYTES]>& splitPath) const {
  706. Path::Split(fullFolder,splitPath);
  707. }
  708. const FolderInfo& operator=(const FolderInfo& o) {
  709. strcpy(currentFolder,o.currentFolder);
  710. strcpy(fullFolder,o.fullFolder);
  711. splitPathIndex = o.splitPathIndex;
  712. return *this;
  713. }
  714. inline void reset() {
  715. currentFolder[0]='\0';fullFolder[0]='\0';splitPathIndex=-1;
  716. }
  717. FolderInfo() {reset();}
  718. FolderInfo(const FolderInfo& o) {*this=o;}
  719. void fromCurrentFolder(const char* path) {
  720. if (!path || strlen(path)==0) reset();
  721. else {
  722. strcpy(currentFolder,path);
  723. strcpy(fullFolder,path);
  724. Path::Split(fullFolder,SplitPath);
  725. splitPathIndex = (int) SplitPath.size()-1;
  726. }
  727. }
  728. bool isEqual(const FolderInfo& fi) const {
  729. return strcmp(fullFolder,fi.fullFolder)==0 && strcmp(currentFolder,fi.currentFolder)==0;
  730. }
  731. bool isEqual(const char* path) const {
  732. return strcmp(fullFolder,path)==0 && strcmp(currentFolder,path)==0;
  733. }
  734. int getSplitPathIndexFor(const char* path) const {
  735. if (!path || strncmp(path,fullFolder,strlen(path))!=0) return -1;
  736. Path::Split(fullFolder,SplitPath);
  737. char tmp[MAX_PATH_BYTES];tmp[0]='\0';
  738. for (int i=0,sz=(int)SplitPath.size();i<sz;i++) {
  739. Path::Append(SplitPath[i],tmp);
  740. //fprintf(stderr,"%d) \"%s\" <-> \"%s\"\n",i,tmp,path);
  741. if (strcmp(tmp,path)==0) return i;
  742. }
  743. return -1;
  744. }
  745. bool getFolderInfoForSplitPathIndex(int _splitPathIndex,FolderInfo& rv) const {
  746. Path::Split(fullFolder,SplitPath);
  747. const int splitPathSize = (int)SplitPath.size();
  748. if (_splitPathIndex<0 || _splitPathIndex>=splitPathSize) return false;
  749. rv = *this;
  750. rv.splitPathIndex = _splitPathIndex;
  751. rv.currentFolder[0]='\0';
  752. if (_splitPathIndex>=0 && _splitPathIndex<splitPathSize) {
  753. for (int i=0;i<=_splitPathIndex;i++) {
  754. Path::Append(SplitPath[i],rv.currentFolder);
  755. //fprintf(stderr,"%d) \"%s\" (\"%s\")\n",i,rv.currentFolder,SplitPath[i]);
  756. }
  757. }
  758. /*fprintf(stderr,"getFolderInfoForSplitPathIndex(%d):\nSource: ",_splitPathIndex);
  759. this->display();
  760. fprintf(stderr,"Result: ");
  761. rv.display();*/
  762. return true;
  763. }
  764. };
  765. ImVector<char[MAX_FILENAME_BYTES]> FolderInfo::SplitPath; // tmp field used internally
  766. struct History {
  767. protected:
  768. ImVector<FolderInfo> info;
  769. int currentInfoIndex; // into info
  770. public:
  771. inline bool canGoBack() {
  772. return currentInfoIndex>0;
  773. }
  774. inline bool canGoForward() {
  775. return currentInfoIndex>=0 && currentInfoIndex<(int)info.size()-1;
  776. }
  777. void reset() {info.clear();currentInfoIndex=-1;}
  778. History() {reset();}
  779. // -------------------------------------------------------------------------------------------------
  780. void goBack() {
  781. if (canGoBack()) --currentInfoIndex;
  782. }
  783. void goForward() {
  784. if (canGoForward()) ++currentInfoIndex;
  785. }
  786. bool switchTo(const char* currentFolder) {
  787. if (!currentFolder || strlen(currentFolder)==0) return false;
  788. if (currentInfoIndex<0) {
  789. ++currentInfoIndex;
  790. info.resize(currentInfoIndex+1);
  791. FolderInfo& fi = info[currentInfoIndex];
  792. fi.fromCurrentFolder(currentFolder);
  793. return true;
  794. }
  795. else {
  796. const FolderInfo& lastInfo = info[currentInfoIndex];
  797. if (lastInfo.isEqual(currentFolder)) return false;
  798. const int splitPathIndexInsideLastInfo = lastInfo.getSplitPathIndexFor(currentFolder);
  799. ++currentInfoIndex;
  800. info.resize(currentInfoIndex+1);
  801. FolderInfo& fi = info[currentInfoIndex];
  802. if (splitPathIndexInsideLastInfo==-1) fi.fromCurrentFolder(currentFolder);
  803. else {
  804. fi = lastInfo;
  805. fi.splitPathIndex = splitPathIndexInsideLastInfo;
  806. strcpy(fi.currentFolder,currentFolder);
  807. }
  808. return true;
  809. }
  810. }
  811. bool switchTo(const FolderInfo& fi) {
  812. if (!fi.currentFolder || strlen(fi.currentFolder)==0) return false;
  813. if (currentInfoIndex>=0) {
  814. const FolderInfo& lastInfo = info[currentInfoIndex];
  815. if (lastInfo.isEqual(fi)) return false;
  816. }
  817. ++currentInfoIndex;
  818. info.resize(currentInfoIndex+1);
  819. info[currentInfoIndex] = fi;
  820. return true;
  821. }
  822. //-----------------------------------------------------------------------------------------------------
  823. inline bool isValid() const {return (currentInfoIndex>=0 && currentInfoIndex<(int)info.size());}
  824. const FolderInfo* getCurrentFolderInfo() const {return isValid() ? &info[currentInfoIndex] : NULL;}
  825. const char* getCurrentFolder() const {return isValid() ? &info[currentInfoIndex].currentFolder[0] : NULL;}
  826. bool getCurrentSplitPath(ImVector<char[MAX_FILENAME_BYTES]>& rv) const {
  827. if (isValid()) {
  828. info[currentInfoIndex].getSplitPath(rv);
  829. return true;
  830. }
  831. else return false;
  832. }
  833. const int* getCurrentSplitPathIndex() const {return isValid() ? &info[currentInfoIndex].splitPathIndex : NULL;}
  834. size_t getInfoSize() const {return info.size();}
  835. };
  836. struct Internal {
  837. ImVector<char[MAX_PATH_BYTES]> dirs,files;
  838. ImVector<char[MAX_FILENAME_BYTES]> dirNames,fileNames,currentSplitPath;
  839. char currentFolder[MAX_PATH_BYTES];
  840. bool forceRescan;
  841. bool open;
  842. ImVec2 wndPos;
  843. ImVec2 wndSize;
  844. char wndTitle[MAX_PATH_BYTES];
  845. int sortingMode;
  846. History history;
  847. //-----------------------------------------------------
  848. bool isSelectFolderDialog;
  849. bool isSaveFileDialog;
  850. bool allowDirectoryCreation,forbidDirectoryCreation;
  851. bool allowKnownDirectoriesSection;
  852. char newDirectoryName[MAX_FILENAME_BYTES];
  853. char saveFileName[MAX_FILENAME_BYTES];
  854. //----------------------------------------------------
  855. char chosenPath[MAX_PATH_BYTES];
  856. bool rescan;
  857. int uniqueNumber;
  858. ImGuiTextFilter filter;
  859. bool allowFiltering;
  860. int totalNumBrowsingEntries;
  861. int numBrowsingColumns;
  862. int numBrowsingEntriesPerColumn;
  863. static bool BrowsingPerRow;
  864. bool allowDisplayByOption;
  865. bool detectKnownDirectoriesAtEveryOpening;
  866. bool mustFilterSaveFilePathWithFileFilterExtensionString;
  867. bool editLocationCheckButtonPressed;
  868. char editLocationInputText[MAX_PATH_BYTES];
  869. void resetVariables() {
  870. strcpy(currentFolder,"./");
  871. forceRescan = false;
  872. open = true;
  873. wndTitle[0] = '\0';
  874. sortingMode = 0;
  875. history.reset();
  876. isSelectFolderDialog = false;
  877. isSaveFileDialog = false;
  878. allowDirectoryCreation = true;
  879. forbidDirectoryCreation = false;
  880. strcpy(newDirectoryName,"New Folder");
  881. saveFileName[0] = '\0';
  882. uniqueNumber = 0;
  883. rescan = true;
  884. chosenPath[0] = '\0';
  885. filter.Clear();
  886. allowFiltering = false;
  887. totalNumBrowsingEntries = 0;
  888. numBrowsingColumns = 1;
  889. numBrowsingEntriesPerColumn = 1000;
  890. detectKnownDirectoriesAtEveryOpening = false;
  891. allowDisplayByOption = false;
  892. allowKnownDirectoriesSection = true;
  893. mustFilterSaveFilePathWithFileFilterExtensionString = true;
  894. editLocationCheckButtonPressed = false;
  895. strcpy(editLocationInputText,"\0");
  896. }
  897. // Just a convenience enum used internally
  898. enum Color {
  899. ImGuiCol_Dialog_Directory_Background,
  900. ImGuiCol_Dialog_Directory_Hover,
  901. ImGuiCol_Dialog_Directory_Pressed,
  902. ImGuiCol_Dialog_Directory_Text,
  903. ImGuiCol_Dialog_File_Background,
  904. ImGuiCol_Dialog_File_Hover,
  905. ImGuiCol_Dialog_File_Pressed,
  906. ImGuiCol_Dialog_File_Text,
  907. ImGuiCol_Dialog_SelectedFolder_Text,
  908. ImGuiCol_Dialog_Size
  909. };
  910. inline static void ColorCombine(ImVec4& c,const ImVec4& r,const ImVec4& factor) {
  911. const float rr = (r.x+r.y+r.z)*0.3334f;
  912. c.x = rr * factor.x;c.y = rr * factor.y;c.z = rr * factor.z;c.w = r.w;
  913. }
  914. };
  915. bool Internal::BrowsingPerRow = false;
  916. // End Internal Usage-------------------------------------------------------------------------------------
  917. Dialog::Dialog(bool noKnownDirectoriesSection,bool noCreateDirectorySection,bool noFilteringSection,bool detectKnownDirectoriesAtEachOpening,bool addDisplayByOption,bool dontFilterSaveFilePathsEnteredByTheUser) {
  918. internal = (Internal*) ImGui::MemAlloc(sizeof(Internal));
  919. new(internal) Internal();
  920. internal->resetVariables();
  921. static int un = 0;
  922. internal->uniqueNumber = un++;
  923. internal->detectKnownDirectoriesAtEveryOpening = detectKnownDirectoriesAtEachOpening;
  924. internal->allowDisplayByOption = addDisplayByOption;
  925. internal->forbidDirectoryCreation = noCreateDirectorySection;
  926. internal->allowKnownDirectoriesSection = !noKnownDirectoriesSection;
  927. internal->allowFiltering = !noFilteringSection;
  928. internal->mustFilterSaveFilePathWithFileFilterExtensionString = !dontFilterSaveFilePathsEnteredByTheUser;
  929. }
  930. Dialog::~Dialog() {
  931. if (internal) {
  932. ImGui::MemFree(internal);
  933. //delete internal;
  934. internal = NULL;
  935. }
  936. }
  937. const char* Dialog::getChosenPath() const {return internal->chosenPath;}
  938. const char* Dialog::getLastDirectory() const {return internal->currentFolder;}
  939. // -- from imgui.cpp --
  940. static size_t ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
  941. {
  942. va_list args;
  943. va_start(args, fmt);
  944. int w = vsnprintf(buf, buf_size, fmt, args);
  945. va_end(args);
  946. buf[buf_size-1] = 0;
  947. return (w == -1) ? buf_size : (size_t)w;
  948. }
  949. // ---------------------
  950. // 90% of the functionality of the whole imguifilesystem.cpp is inside this single method
  951. const char* ChooseFileMainMethod(Dialog& ist,const char* directory,const bool _isFolderChooserDialog,const bool _isSaveFileDialog,const char* _saveFileName,const char* fileFilterExtensionString,const char* windowTitle,const ImVec2& windowSize,const ImVec2& windowPos,const float windowAlpha) {
  952. //-----------------------------------------------------------------------------
  953. Internal& I = *ist.internal;
  954. char* rv = I.chosenPath;rv[0] = '\0';
  955. //-----------------------------------------------------
  956. bool& isSelectFolderDialog = I.isSelectFolderDialog = _isFolderChooserDialog;
  957. bool& isSaveFileDialog = I.isSaveFileDialog = _isSaveFileDialog;
  958. bool& allowDirectoryCreation = I.allowDirectoryCreation = I.forbidDirectoryCreation ? false : (isSelectFolderDialog || isSaveFileDialog);
  959. //----------------------------------------------------------
  960. static const int* pNumberKnownUserDirectoriesExceptDrives=NULL;
  961. static const ImVector<char[MAX_FILENAME_BYTES]>* pUserKnownDirectoryDisplayNames=NULL;
  962. static const ImVector<char[MAX_PATH_BYTES]>* pUserKnownDirectories = &Directory::GetUserKnownDirectories(&pUserKnownDirectoryDisplayNames,&pNumberKnownUserDirectoriesExceptDrives);
  963. //----------------------------------------------------------
  964. const ImGuiStyle& style = ImGui::GetStyle();
  965. ImVec4 dummyButtonColor(0.0f,0.0f,0.0f,0.5f); // Only the alpha is twickable from here
  966. static ImVec4 ColorSet[Internal::ImGuiCol_Dialog_Size];
  967. // Fill ColorSet above and fix dummyButtonColor here
  968. {
  969. static const ImVec4 df(0.9,0.9,0.3,1); // directory color factor
  970. static const ImVec4 ff(0.7,0.7,0.7,1); // file color factor
  971. for (int i=0,sz=(int)Internal::ImGuiCol_Dialog_Directory_Text;i<=sz;i++) {
  972. ImVec4& c = ColorSet[i];
  973. const ImVec4& r = style.Colors[i<sz ? ((int)ImGuiCol_Button + i) : ImGuiCol_Text];
  974. Internal::ColorCombine(c,r,df);
  975. }
  976. for (int i=(int)Internal::ImGuiCol_Dialog_File_Background,sz=(int)Internal::ImGuiCol_Dialog_File_Text;i<=sz;i++) {
  977. ImVec4& c = ColorSet[i];
  978. const ImVec4& r = style.Colors[i<sz ? ((int)ImGuiCol_Button-(int)Internal::ImGuiCol_Dialog_File_Background + i) : ImGuiCol_Text];
  979. Internal::ColorCombine(c,r,ff);
  980. }
  981. if (dummyButtonColor.w>0) {
  982. const ImVec4& bbc = style.Colors[ImGuiCol_Button];
  983. dummyButtonColor.x = bbc.x;dummyButtonColor.y = bbc.y;dummyButtonColor.z = bbc.z;dummyButtonColor.w *= bbc.w;
  984. }
  985. }
  986. if (I.rescan) {
  987. char validDirectory[MAX_PATH_BYTES];validDirectory[0]='\0'; // for robustness
  988. if (directory && strlen(directory)>0) {
  989. if (Directory::Exists(directory)) strcpy(validDirectory,directory);
  990. else {
  991. Path::GetDirectoryName(directory,validDirectory);
  992. if (!Directory::Exists(validDirectory)) validDirectory[0]='\0';
  993. }
  994. }
  995. Path::GetAbsolutePath(validDirectory,I.currentFolder);
  996. I.editLocationCheckButtonPressed = false;
  997. I.history.reset(); // reset history
  998. I.history.switchTo(I.currentFolder); // init history
  999. I.dirs.clear();I.files.clear();I.dirNames.clear();I.fileNames.clear();I.currentSplitPath.clear();
  1000. strcpy(&I.newDirectoryName[0],"New Folder");
  1001. if (_saveFileName) {
  1002. //strcpy(&I.saveFileName[0],_saveFileName);
  1003. Path::GetFileName(_saveFileName,I.saveFileName); // Better!
  1004. }
  1005. else I.saveFileName[0]='\0';
  1006. isSelectFolderDialog = _isFolderChooserDialog;
  1007. isSaveFileDialog = _isSaveFileDialog;
  1008. allowDirectoryCreation = I.forbidDirectoryCreation ? false : (isSelectFolderDialog || isSaveFileDialog);
  1009. if (isSelectFolderDialog && I.sortingMode>SORT_ORDER_LAST_MODIFICATION_INVERSE) I.sortingMode = 0;
  1010. I.forceRescan = true;
  1011. I.open = true;
  1012. I.filter.Clear();
  1013. if (!windowTitle || strlen(windowTitle)==0) {
  1014. if (isSelectFolderDialog) strcpy(I.wndTitle,"Please select a folder");
  1015. else if (isSaveFileDialog) strcpy(I.wndTitle,"Please choose/create a file for saving");
  1016. else strcpy(I.wndTitle,"Please choose a file");
  1017. }
  1018. else strcpy(I.wndTitle,windowTitle);
  1019. strcat(I.wndTitle,"##");
  1020. char tmpWndTitleNumber[12];
  1021. ImFormatString(tmpWndTitleNumber,11,"%d",I.uniqueNumber);
  1022. strcat(I.wndTitle,tmpWndTitleNumber);
  1023. I.wndPos = windowPos;
  1024. I.wndSize = windowSize;
  1025. if (I.wndSize.x<=0) I.wndSize.x = 400;
  1026. if (I.wndSize.y<=0) I.wndSize.y = 400;
  1027. const ImVec2 mousePos = ImGui::GetMousePos();//
  1028. ImGui::GetCursorPos();
  1029. if (I.wndPos.x<=0) I.wndPos.x = mousePos.x - I.wndSize.x*0.5f;
  1030. if (I.wndPos.y<=0) I.wndPos.y = mousePos.y - I.wndSize.y*0.5f;
  1031. const ImVec2 screenSize = ImGui::GetIO().DisplaySize;
  1032. if (I.wndPos.x>screenSize.x-I.wndSize.x) I.wndPos.x = screenSize.x-I.wndSize.x;
  1033. if (I.wndPos.y>screenSize.y-I.wndSize.y) I.wndPos.y = screenSize.y-I.wndSize.y;
  1034. if (I.wndPos.x < 0) I.wndPos.x = 0;
  1035. if (I.wndPos.y < 0) I.wndPos.y = 0;
  1036. //fprintf(stderr,"screenSize = %f,%f mousePos = %f,%f wndPos = %f,%f wndSize = %f,%f\n",screenSize.x,screenSize.y,mousePos.x,mousePos.y,wndPos.x,wndPos.y,wndSize.x,wndSize.y);
  1037. if (I.detectKnownDirectoriesAtEveryOpening) pUserKnownDirectories = &Directory::GetUserKnownDirectories(&pUserKnownDirectoryDisplayNames,&pNumberKnownUserDirectoriesExceptDrives,true);
  1038. }
  1039. if (!I.open) return rv;
  1040. if (I.forceRescan) {
  1041. I.forceRescan = false;
  1042. const int sortingModeForDirectories = (I.sortingMode <= (int)SORT_ORDER_LAST_MODIFICATION_INVERSE) ? I.sortingMode : (I.sortingMode%2);
  1043. Directory::GetDirectories(I.currentFolder,I.dirs,&I.dirNames,(Sorting)sortingModeForDirectories); // this is because directories don't return their size or their file extensions (so if needed we sort them alphabetically)
  1044. //I.dirNames.resize(I.dirs.size());for (int i=0,sz=I.dirs.size();i<sz;i++) Path::GetFileName(I.dirs[i],(char*)I.dirNames[i]);
  1045. if (!isSelectFolderDialog) {
  1046. if (!fileFilterExtensionString || strlen(fileFilterExtensionString)==0) Directory::GetFiles(I.currentFolder,I.files,&I.fileNames,(Sorting)I.sortingMode);
  1047. else Directory::GetFiles(I.currentFolder,I.files,fileFilterExtensionString,NULL,&I.fileNames,(Sorting)I.sortingMode);
  1048. //I.fileNames.resize(I.files.size());for (int i=0,sz=I.files.size();i<sz;i++) Path::GetFileName(I.files[i],(char*)I.fileNames[i]);
  1049. }
  1050. else {
  1051. I.files.clear();I.fileNames.clear();
  1052. I.saveFileName[0]='\0';
  1053. char currentFolderName[MAX_FILENAME_BYTES];
  1054. Path::GetFileName(I.currentFolder,currentFolderName);
  1055. const size_t currentFolderNameSize = strlen(currentFolderName);
  1056. if (currentFolderNameSize==0 || currentFolderName[currentFolderNameSize-1]==':') strcat(currentFolderName,"/");
  1057. strcat(I.saveFileName,currentFolderName);
  1058. }
  1059. I.history.getCurrentSplitPath(I.currentSplitPath);
  1060. const static int approxNumEntriesPerColumn = 20;//(int) (20.f / browseSectionFontScale);// tweakable
  1061. I.totalNumBrowsingEntries = (int)(I.dirs.size()+I.files.size());
  1062. I.numBrowsingColumns = I.totalNumBrowsingEntries/approxNumEntriesPerColumn;
  1063. if (I.numBrowsingColumns<=0) I.numBrowsingColumns = 1;
  1064. if (I.totalNumBrowsingEntries%approxNumEntriesPerColumn>(approxNumEntriesPerColumn/2)) ++I.numBrowsingColumns;
  1065. if (I.numBrowsingColumns>6) I.numBrowsingColumns = 6;
  1066. I.numBrowsingEntriesPerColumn = I.totalNumBrowsingEntries/I.numBrowsingColumns;
  1067. if (I.totalNumBrowsingEntries%I.numBrowsingColumns!=0) ++I.numBrowsingEntriesPerColumn;
  1068. //# define DEBUG_HISTORY
  1069. # ifdef DEBUG_HISTORY
  1070. if (I.history.getInfoSize()>0) fprintf(stderr,"\nHISTORY: currentFolder:\"%s\" history.canGoBack=%s history.canGoForward=%s currentHistory:\n",I.currentFolder,I.history.canGoBack()?"true":"false",I.history.canGoForward()?"true":"false");
  1071. if (I.history.getCurrentFolderInfo()) I.history.getCurrentFolderInfo()->display();
  1072. # endif //DEBUG_HISTORY
  1073. }
  1074. if (I.rescan) {
  1075. I.rescan = false; // Mandatory
  1076. ImGui::Begin(I.wndTitle, &I.open, I.wndSize,windowAlpha);
  1077. ImGui::SetWindowPos(I.wndPos);
  1078. ImGui::SetWindowSize(I.wndSize);
  1079. //fprintf(stderr,"\"%s\" wndPos={%1.2f,%1.2f}\n",wndTitle.c_str(),wndPos.x,wndPos.y);
  1080. }
  1081. else ImGui::Begin(I.wndTitle, &I.open,ImVec2(0,0),windowAlpha);
  1082. ImGui::Separator();
  1083. //------------------------------------------------------------------------------------
  1084. // History (=buttons: < and >)
  1085. {
  1086. bool historyBackClicked = false;
  1087. bool historyForwardClicked = false;
  1088. // history -----------------------------------------------
  1089. ImGui::PushID("historyDirectoriesID");
  1090. const bool historyCanGoBack = I.history.canGoBack();
  1091. const bool historyCanGoForward = I.history.canGoForward();
  1092. if (!historyCanGoBack) {
  1093. ImGui::PushStyleColor(ImGuiCol_Button,dummyButtonColor);
  1094. ImGui::PushStyleColor(ImGuiCol_ButtonHovered,dummyButtonColor);
  1095. ImGui::PushStyleColor(ImGuiCol_ButtonActive,dummyButtonColor);
  1096. }
  1097. historyBackClicked = ImGui::Button("<")&historyCanGoBack;
  1098. ImGui::SameLine();
  1099. if (!historyCanGoBack) {
  1100. ImGui::PopStyleColor();
  1101. ImGui::PopStyleColor();
  1102. ImGui::PopStyleColor();
  1103. }
  1104. if (!historyCanGoForward) {
  1105. ImGui::PushStyleColor(ImGuiCol_Button,dummyButtonColor);
  1106. ImGui::PushStyleColor(ImGuiCol_ButtonHovered,dummyButtonColor);
  1107. ImGui::PushStyleColor(ImGuiCol_ButtonActive,dummyButtonColor);
  1108. }
  1109. historyForwardClicked = ImGui::Button(">")&historyCanGoForward;
  1110. ImGui::SameLine();
  1111. if (!historyCanGoForward) {
  1112. ImGui::PopStyleColor();
  1113. ImGui::PopStyleColor();
  1114. ImGui::PopStyleColor();
  1115. }
  1116. ImGui::PopID();
  1117. // -------------------------------------------------------
  1118. if (historyBackClicked || historyForwardClicked) {
  1119. ImGui::End();
  1120. if (historyBackClicked) I.history.goBack();
  1121. else if (historyForwardClicked) I.history.goForward();
  1122. I.forceRescan = true;
  1123. strcpy(I.currentFolder,I.history.getCurrentFolder());
  1124. strcpy(I.editLocationInputText,I.currentFolder);
  1125. # ifdef DEBUG_HISTORY
  1126. if (historyBackClicked) fprintf(stderr,"\nPressed BACK to\t");
  1127. else fprintf(stderr,"\nPressed FORWARD to\t");
  1128. fprintf(stderr,"\"%s\" (%d)\n",I.currentFolder,(int)*I.history.getCurrentSplitPathIndex());
  1129. # undef DEBUG_HISTOTY
  1130. # endif //DEBUG_HISTORY
  1131. return rv;
  1132. }
  1133. }
  1134. //------------------------------------------------------------------------------------
  1135. // Edit Location CheckButton
  1136. bool editLocationInputTextReturnPressed = false;
  1137. {
  1138. bool mustValidateInputPath = false;
  1139. ImGui::PushStyleColor(ImGuiCol_Button,I.editLocationCheckButtonPressed ? dummyButtonColor : style.Colors[ImGuiCol_Button]);
  1140. if (ImGui::Button("L##EditLocationCheckButton")) {
  1141. I.editLocationCheckButtonPressed = !I.editLocationCheckButtonPressed;
  1142. if (I.editLocationCheckButtonPressed) {
  1143. strcpy(I.editLocationInputText,I.currentFolder);
  1144. ImGui::SetKeyboardFocusHere();
  1145. }
  1146. //if (!I.editLocationCheckButtonPressed) mustValidateInputPath = true; // or not ? I mean: the user wants to quit or to validate in this case ?
  1147. }
  1148. ImGui::PopStyleColor();
  1149. if (I.editLocationCheckButtonPressed) {
  1150. ImGui::SameLine();
  1151. editLocationInputTextReturnPressed = ImGui::InputText("##EditLocationInputText",I.editLocationInputText,MAX_PATH_BYTES,ImGuiInputTextFlags_AutoSelectAll|ImGuiInputTextFlags_EnterReturnsTrue);
  1152. if (editLocationInputTextReturnPressed) mustValidateInputPath = true;
  1153. else ImGui::Separator();
  1154. }
  1155. if (mustValidateInputPath) {
  1156. // it's better to clean the input path here from trailing slashes:
  1157. char cleanEnteredPath[MAX_PATH_BYTES];
  1158. strcpy(cleanEnteredPath,I.editLocationInputText);
  1159. size_t len = strlen(cleanEnteredPath);
  1160. while (len>0 && (cleanEnteredPath[len-1]=='/' || cleanEnteredPath[len-1]=='\\')) {cleanEnteredPath[len-1]='\0';len = strlen(cleanEnteredPath);}
  1161. if (len==0 || strcmp(I.currentFolder,cleanEnteredPath)==0) I.editLocationCheckButtonPressed = false;
  1162. else if (Directory::Exists(cleanEnteredPath)) {
  1163. I.editLocationCheckButtonPressed = false; // Optional (return to split-path buttons)
  1164. //----------------------------------------------------------------------------------
  1165. I.history.switchTo(cleanEnteredPath);
  1166. strcpy(I.currentFolder,cleanEnteredPath);
  1167. I.forceRescan = true;
  1168. }
  1169. //else fprintf(stderr,"mustValidateInputPath NOOP: \"%s\" \"%s\"\n",I.currentFolder,cleanEnteredPath);
  1170. }
  1171. else ImGui::SameLine();
  1172. }
  1173. //------------------------------------------------------------------------------------
  1174. // Split Path control
  1175. if (!I.editLocationCheckButtonPressed && !editLocationInputTextReturnPressed) {
  1176. bool mustSwitchSplitPath = false;
  1177. const FolderInfo& fi = *I.history.getCurrentFolderInfo();
  1178. ImVec2& framePadding = ImGui::GetStyle().FramePadding;
  1179. const float originalFramePaddingX = framePadding.x;
  1180. framePadding.x = 0;
  1181. // Split Path
  1182. // Tab:
  1183. {
  1184. //-----------------------------------------------------
  1185. // TAB LABELS
  1186. //-----------------------------------------------------
  1187. {
  1188. const int numTabs=(int) I.currentSplitPath.size();
  1189. int newSelectedTab = fi.splitPathIndex;
  1190. for (int t=0;t<numTabs;t++) {
  1191. if (t>0) ImGui::SameLine(0,0);
  1192. if (t==fi.splitPathIndex) {
  1193. ImGui::PushStyleColor(ImGuiCol_Button,dummyButtonColor);
  1194. ImGui::PushStyleColor(ImGuiCol_ButtonHovered,dummyButtonColor);
  1195. ImGui::PushStyleColor(ImGuiCol_ButtonActive,dummyButtonColor);
  1196. }
  1197. ImGui::PushID(&I.currentSplitPath[t]);
  1198. const bool pressed = ImGui::Button(I.currentSplitPath[t]);
  1199. ImGui::PopID();
  1200. if (pressed) {
  1201. if (fi.splitPathIndex!=t && !mustSwitchSplitPath) mustSwitchSplitPath = true;
  1202. newSelectedTab = t;
  1203. }
  1204. if (t==fi.splitPathIndex) {
  1205. ImGui::PopStyleColor();
  1206. ImGui::PopStyleColor();
  1207. ImGui::PopStyleColor();
  1208. }
  1209. }
  1210. if (mustSwitchSplitPath) {
  1211. FolderInfo mfi;
  1212. fi.getFolderInfoForSplitPathIndex(newSelectedTab,mfi);
  1213. I.history.switchTo(mfi);
  1214. I.forceRescan = true;
  1215. strcpy(I.currentFolder,I.history.getCurrentFolder());
  1216. strcpy(I.editLocationInputText,I.currentFolder);
  1217. //fprintf(stderr,"%s\n",I.currentFolder);
  1218. }
  1219. }
  1220. }
  1221. framePadding.x = originalFramePaddingX;
  1222. }
  1223. //------------------------------------------------------------------------------------
  1224. // Start collapsable regions----------------------------------------------------------
  1225. // User Known directories-------------------------------------------------------------
  1226. if (I.allowKnownDirectoriesSection && pUserKnownDirectories->size()>0) {
  1227. ImGui::Separator();
  1228. if (ImGui::CollapsingHeader("Known Directories##UserKnownDirectories")) {
  1229. static int id;
  1230. ImGui::PushID(&id);
  1231. ImGui::PushStyleColor(ImGuiCol_Text,ColorSet[Internal::ImGuiCol_Dialog_Directory_Text]);
  1232. ImGui::PushStyleColor(ImGuiCol_Button,ColorSet[Internal::ImGuiCol_Dialog_Directory_Background]);
  1233. ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ColorSet[Internal::ImGuiCol_Dialog_Directory_Hover]);
  1234. ImGui::PushStyleColor(ImGuiCol_ButtonActive,ColorSet[Internal::ImGuiCol_Dialog_Directory_Pressed]);
  1235. for (int i=0,sz=(int)pUserKnownDirectories->size();i<sz;i++) {
  1236. const char* userKnownFolder = (*pUserKnownDirectories)[i];
  1237. const char* userKnownFolderDisplayName = (*pUserKnownDirectoryDisplayNames)[i];
  1238. if (ImGui::SmallButton(userKnownFolderDisplayName) && strcmp(userKnownFolder,I.currentFolder)!=0) {
  1239. strcpy(I.currentFolder,userKnownFolder);
  1240. strcpy(I.editLocationInputText,I.currentFolder);
  1241. I.history.switchTo(I.currentFolder);
  1242. I.forceRescan = true;
  1243. //------------------------------------------------------------------------------------------------------------------------------
  1244. }
  1245. if (i!=sz-1 && (i>=*pNumberKnownUserDirectoriesExceptDrives || i%7!=6)) ImGui::SameLine();
  1246. }
  1247. ImGui::PopStyleColor();
  1248. ImGui::PopStyleColor();
  1249. ImGui::PopStyleColor();
  1250. ImGui::PopStyleColor();
  1251. ImGui::PopID();
  1252. }
  1253. }
  1254. // End User Known directories---------------------------------------------------------
  1255. // Allow directory creation ----------------------------------------------------------
  1256. if (allowDirectoryCreation) {
  1257. ImGui::Separator();
  1258. bool mustCreate = false;
  1259. if (ImGui::CollapsingHeader("New Directory##allowDirectoryCreation")) {
  1260. static int id;
  1261. ImGui::PushID(&id);
  1262. ImGui::InputText("##createNewFolderName",&I.newDirectoryName[0],MAX_FILENAME_BYTES);
  1263. ImGui::SameLine();
  1264. mustCreate = ImGui::Button("CREATE");
  1265. ImGui::PopID();
  1266. }
  1267. if (mustCreate && strlen(I.newDirectoryName)>0) {
  1268. char newDirPath[MAX_PATH_BYTES];
  1269. Path::Combine(I.currentFolder,I.newDirectoryName,newDirPath,false);
  1270. if (!Directory::Exists(newDirPath)) {
  1271. //# define SIMULATING_ONLY
  1272. # ifdef SIMULATING_ONLY
  1273. fprintf(stderr,"creating: \"%s\"\n",newDirPath);
  1274. # undef SIMULATING_ONLY
  1275. # else //SIMULATING_ONLY
  1276. Directory::Create(newDirPath);
  1277. if (!Directory::Exists(newDirPath)) fprintf(stderr,"Error creating new folder: \"%s\"\n",newDirPath);
  1278. else I.forceRescan = true; // Just update
  1279. # endif //SIMULATING_ONLY
  1280. }
  1281. }
  1282. }
  1283. // End allow directory creation ------------------------------------------------------
  1284. // Filtering entries -----------------------------------------------------------------
  1285. if (I.allowFiltering) {
  1286. ImGui::Separator();
  1287. if (ImGui::CollapsingHeader("Filtering##fileNameFiltering")) {
  1288. static int id;
  1289. ImGui::PushID(&id);
  1290. I.filter.Draw();
  1291. ImGui::PopID();
  1292. }
  1293. }
  1294. // End filtering entries -------------------------------------------------------------
  1295. // End collapsable regions------------------------------------------------------------
  1296. // Selection field -------------------------------------------------------------------
  1297. if (isSaveFileDialog || isSelectFolderDialog) {
  1298. ImGui::Separator();
  1299. bool selectionButtonPressed = false;
  1300. static int id;
  1301. ImGui::PushID(&id);
  1302. if (isSaveFileDialog) {
  1303. ImGui::AlignFirstTextHeightToWidgets();
  1304. ImGui::Text("File:");ImGui::SameLine();
  1305. ImGui::InputText("##saveFileName",&I.saveFileName[0],MAX_FILENAME_BYTES);
  1306. ImGui::SameLine();
  1307. }
  1308. else {
  1309. ImGui::AlignFirstTextHeightToWidgets();
  1310. ImGui::Text("Folder:");ImGui::SameLine();
  1311. static const ImVec4 sf(1.0,0.8,0.5,1); // delected folder color factor
  1312. ImVec4& c = ColorSet[Internal::ImGuiCol_Dialog_SelectedFolder_Text];
  1313. const ImVec4& r = style.Colors[ImGuiCol_Text];
  1314. Internal::ColorCombine(c,r,sf);
  1315. ImGui::TextColored(ColorSet[Internal::ImGuiCol_Dialog_SelectedFolder_Text],"%s",&I.saveFileName[0],MAX_FILENAME_BYTES);
  1316. ImGui::SameLine();
  1317. }
  1318. if (isSelectFolderDialog) selectionButtonPressed = ImGui::Button("Select");
  1319. else selectionButtonPressed = ImGui::Button("Save");
  1320. ImGui::PopID();
  1321. if (selectionButtonPressed) {
  1322. if (isSelectFolderDialog) {
  1323. strcpy(rv,I.currentFolder);
  1324. I.open = true;
  1325. }
  1326. else if (isSaveFileDialog) {
  1327. if (strlen(I.saveFileName)>0) {
  1328. bool pathOk = true;
  1329. if (I.mustFilterSaveFilePathWithFileFilterExtensionString && fileFilterExtensionString && strlen(fileFilterExtensionString)>0) {
  1330. pathOk = false;
  1331. char saveFileNameExtension[MAX_FILENAME_BYTES];Path::GetExtension(I.saveFileName,saveFileNameExtension);
  1332. const bool saveFileNameHasExtension = strlen(saveFileNameExtension)>0;
  1333. //-------------------------------------------------------------------
  1334. ImVector<char[MAX_FILENAME_BYTES]> wExts;String::Split(fileFilterExtensionString,wExts,';');
  1335. const size_t wExtsSize = wExts.size();
  1336. if (!saveFileNameHasExtension) {
  1337. if (wExtsSize==0) pathOk = true; // Bad situation, better allow this case
  1338. else strcat(I.saveFileName,wExts[0]);
  1339. }
  1340. else {
  1341. // saveFileNameHasExtension
  1342. for (size_t i = 0;i<wExtsSize;i++) {
  1343. const char* ext = wExts[i];
  1344. if (strcmp(ext,saveFileNameExtension)==0) {
  1345. pathOk = true;
  1346. break;
  1347. }
  1348. }
  1349. if (!pathOk && wExtsSize>0) strcat(I.saveFileName,wExts[0]);
  1350. }
  1351. }
  1352. if (pathOk) {
  1353. char savePath[MAX_PATH_BYTES];
  1354. Path::Combine(I.currentFolder,I.saveFileName,savePath,false);
  1355. strcpy(rv,savePath);
  1356. I.open = true;
  1357. }
  1358. }
  1359. }
  1360. }
  1361. //ImGui::Spacing();
  1362. }
  1363. // End selection field----------------------------------------------------------------
  1364. ImGui::Separator();
  1365. // sorting --------------------------------------------------------------------
  1366. ImGui::Text("Sorting by: ");ImGui::SameLine();
  1367. {
  1368. const int oldSortingMode = I.sortingMode;
  1369. const int oldSelectedTab = I.sortingMode/2;
  1370. //-----------------------------------------------------
  1371. // TAB LABELS
  1372. //-----------------------------------------------------
  1373. {
  1374. static const int numTabs=(int)SORT_ORDER_COUNT/2;
  1375. int newSortingMode = oldSortingMode;
  1376. static const char* names[numTabs] = {"Name","Modified","Size","Type"};
  1377. const int numUsedTabs = isSelectFolderDialog ? 2 : numTabs;
  1378. for (int t=0;t<numUsedTabs;t++) {
  1379. if (t>0) ImGui::SameLine();
  1380. if (t==oldSelectedTab) {
  1381. ImGui::PushStyleColor(ImGuiCol_Button,dummyButtonColor);
  1382. }
  1383. ImGui::PushID(&names[t]);
  1384. const bool pressed = ImGui::SmallButton(names[t]);
  1385. ImGui::PopID();
  1386. if (pressed) {
  1387. if (oldSelectedTab==t) {
  1388. newSortingMode = oldSortingMode;
  1389. if (newSortingMode%2==0) ++newSortingMode;// 0,2,4
  1390. else --newSortingMode;
  1391. }
  1392. else newSortingMode = t*2;
  1393. }
  1394. if (t==oldSelectedTab) {
  1395. ImGui::PopStyleColor();
  1396. }
  1397. }
  1398. if (newSortingMode!=oldSortingMode) {
  1399. I.sortingMode = newSortingMode;
  1400. //printf("sortingMode = %d\n",sortingMode);
  1401. I.forceRescan = true;
  1402. }
  1403. //-- Browsing per row -----------------------------------
  1404. if (I.allowDisplayByOption && I.numBrowsingColumns>1) {
  1405. ImGui::SameLine();
  1406. ImGui::Text(" Display by:");
  1407. ImGui::SameLine();
  1408. ImGui::PushStyleColor(ImGuiCol_Button,dummyButtonColor);
  1409. if (ImGui::SmallButton(!Internal::BrowsingPerRow ? "Column##browsingPerRow" : "Row##browsingPerRow")) {
  1410. Internal::BrowsingPerRow = !Internal::BrowsingPerRow;
  1411. }
  1412. ImGui::PopStyleColor();
  1413. }
  1414. //-- End browsing per row -------------------------------
  1415. }
  1416. }
  1417. //-----------------------------------------------------------------------------
  1418. ImGui::Separator();
  1419. //-----------------------------------------------------------------------------
  1420. // MAIN BROWSING FRAME:
  1421. //-----------------------------------------------------------------------------
  1422. {
  1423. ImGui::BeginChild("BrowsingFrame");
  1424. // ImGui::SetScrollPosHere(); // possible future ref: while drawing to place the scroll bar
  1425. ImGui::Columns(I.numBrowsingColumns);
  1426. static int id;
  1427. ImGui::PushID(&id);
  1428. int cntEntries = 0;
  1429. // Directories --------------------------------------------------------------
  1430. if (I.dirs.size()>0) {
  1431. ImGui::PushStyleColor(ImGuiCol_Text,ColorSet[Internal::ImGuiCol_Dialog_Directory_Text]);
  1432. ImGui::PushStyleColor(ImGuiCol_Button,ColorSet[Internal::ImGuiCol_Dialog_Directory_Background]);
  1433. ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ColorSet[Internal::ImGuiCol_Dialog_Directory_Hover]);
  1434. ImGui::PushStyleColor(ImGuiCol_ButtonActive,ColorSet[Internal::ImGuiCol_Dialog_Directory_Pressed]);
  1435. for (int i=0,sz=(int)I.dirs.size();i<sz;i++) {
  1436. const char* dirName = &I.dirNames[i][0];
  1437. if (I.filter.PassFilter(dirName)) {
  1438. if (ImGui::SmallButton(dirName)) {
  1439. strcpy(I.currentFolder,I.dirs[i]);
  1440. strcpy(I.editLocationInputText,I.currentFolder);
  1441. I.history.switchTo(I.currentFolder);
  1442. I.forceRescan = true;
  1443. //------------------------------------------------------------------------------------------------------------------------------
  1444. }
  1445. ++cntEntries;
  1446. if (Internal::BrowsingPerRow) ImGui::NextColumn();
  1447. else if (cntEntries==I.numBrowsingEntriesPerColumn) {
  1448. cntEntries = 0;
  1449. ImGui::NextColumn();
  1450. }
  1451. }
  1452. }
  1453. ImGui::PopStyleColor();
  1454. ImGui::PopStyleColor();
  1455. ImGui::PopStyleColor();
  1456. ImGui::PopStyleColor();
  1457. }
  1458. // Files ----------------------------------------------------------------------
  1459. if (!isSelectFolderDialog && I.files.size()>0) {
  1460. ImGui::PushStyleColor(ImGuiCol_Text,ColorSet[Internal::ImGuiCol_Dialog_File_Text]);
  1461. ImGui::PushStyleColor(ImGuiCol_Button,ColorSet[Internal::ImGuiCol_Dialog_File_Background]);
  1462. ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ColorSet[Internal::ImGuiCol_Dialog_File_Hover]);
  1463. ImGui::PushStyleColor(ImGuiCol_ButtonActive,ColorSet[Internal::ImGuiCol_Dialog_File_Pressed]);
  1464. for (int i=0,sz=(int)I.files.size();i<sz;i++) {
  1465. const char* fileName = &I.fileNames[i][0];
  1466. if (I.filter.PassFilter(fileName)) {
  1467. if (ImGui::SmallButton(fileName)) {
  1468. if (!isSaveFileDialog) {
  1469. strcpy(rv,I.files[i]);
  1470. I.open = true;
  1471. }
  1472. else {
  1473. Path::GetFileName(I.files[i],I.saveFileName);
  1474. }
  1475. }
  1476. ++cntEntries;
  1477. if (Internal::BrowsingPerRow) ImGui::NextColumn();
  1478. else if (cntEntries==I.numBrowsingEntriesPerColumn) {
  1479. cntEntries = 0;
  1480. ImGui::NextColumn();
  1481. }
  1482. }
  1483. }
  1484. ImGui::PopStyleColor();
  1485. ImGui::PopStyleColor();
  1486. ImGui::PopStyleColor();
  1487. ImGui::PopStyleColor();
  1488. }
  1489. //-----------------------------------------------------------------------------
  1490. ImGui::PopID();
  1491. ImGui::EndChild();
  1492. }
  1493. //-----------------------------------------------------------------------------
  1494. ImGui::End();
  1495. return rv;
  1496. }
  1497. const char* Dialog::chooseFileDialog(bool dialogTriggerButton,const char* directory,const char* fileFilterExtensionString,const char* windowTitle,const ImVec2& windowSize,const ImVec2& windowPos,const float windowAlpha) {
  1498. if (dialogTriggerButton) {internal->rescan = true;internal->chosenPath[0]='\0';}
  1499. if (dialogTriggerButton || (!internal->rescan && strlen(getChosenPath())==0)) {
  1500. ChooseFileMainMethod(*this,directory,false,false,"",fileFilterExtensionString,windowTitle,windowSize,windowPos,windowAlpha);
  1501. }
  1502. return getChosenPath();
  1503. }
  1504. const char* Dialog::chooseFolderDialog(bool dialogTriggerButton,const char* directory,const char* windowTitle,const ImVec2& windowSize,const ImVec2& windowPos,const float windowAlpha) {
  1505. if (dialogTriggerButton) {internal->rescan = true;internal->chosenPath[0]='\0';}
  1506. if (dialogTriggerButton || (!internal->rescan && strlen(getChosenPath())==0)) {
  1507. ChooseFileMainMethod(*this,directory,true,false,"","",windowTitle,windowSize,windowPos,windowAlpha);
  1508. }
  1509. return getChosenPath();
  1510. }
  1511. const char* Dialog::saveFileDialog(bool dialogTriggerButton,const char* directory,const char* startingFileNameEntry,const char* fileFilterExtensionString,const char* windowTitle,const ImVec2& windowSize,const ImVec2& windowPos,const float windowAlpha) {
  1512. if (dialogTriggerButton) {internal->rescan = true;internal->chosenPath[0]='\0';}
  1513. if (dialogTriggerButton || (!internal->rescan && strlen(getChosenPath())==0)) {
  1514. ChooseFileMainMethod(*this,directory,false,true,startingFileNameEntry,fileFilterExtensionString,windowTitle,windowSize,windowPos,windowAlpha);
  1515. }
  1516. return getChosenPath();
  1517. }
  1518. } // namespace ImGuiFs