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