Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
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#ifdef _WIN32
16# include <Windows.h>
17#endif //_WIN32
18
19//- Common Code For All Addons needed just to ease inclusion as separate files in user code ----------------------
20#include <imgui.h>
21#undef IMGUI_DEFINE_MATH_OPERATORS
22#define IMGUI_DEFINE_MATH_OPERATORS
23#include <imgui_internal.h>
24//-----------------------------------------------------------------------------------------------------------------
25
26
27#include "imguifilesystem.h"
28
29
30#ifdef DIRENT_USES_UTF8_CHARS
31# warning DIRENT_USES_UTF8_CHARS is deprecated and has become the default. (IMGUIFILESYSTEM_USE_ASCII_SHORT_PATHS_ON_WINDOWS can be used to disable it).
32#endif //IMGUIBINDINGS_CLEAR_INPUT_DATA_SOON
33
34#ifdef _WIN32
35#include <ShlObj.h> // Known Directory locations
36# ifndef CSIDL_MYPICTURES
37# define CSIDL_MYPICTURES 0x0027
38# endif //CSIDL_MYPICTURES
39# ifndef CSIDL_MYMUSIC
40# define CSIDL_MYMUSIC 0x000d
41# endif //CSIDL_MYMUSIC
42# ifndef CSIDL_MYVIDEO
43# define CSIDL_MYVIDEO 0x000e
44# endif //CSIDL_MYVIDEO
45#else // _WIN32
46# include <unistd.h> // getpwuid
47# include <pwd.h> // getenv ?
48#endif //#ifdef _WIN32
49
50// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall
51#ifdef _MSC_VER
52#define IMGUIFS_CDECL __cdecl
53#else
54#define IMGUIFS_CDECL
55#endif
56
57#ifdef IMGUIFS_NO_EXTRA_METHODS
58// We copy the code for FILENAME_MAX and PATH_MAX
59# include <stdint.h> // this is included by imgui.cpp, and the following headers might redefine incorrectly some types otherwise.
60# include <stdio.h> // just for FILENAME_MAX
61# include <limits.h> // just for PATH_MAX
62# if (defined(__linux__) && !defined(PATH_MAX))
63# include <linux/limits.h>
64# endif //(defined(__linux__) && !defined(PATH_MAX))
65# ifndef PATH_MAX
66# define PATH_MAX 1024 // Or 4096 ?
67# endif //IMGUIFS_NO_EXTRA_METHODS
68# ifndef FILENAME_MAX
69# define FILENAME_MAX PATH_MAX
70# endif //FILENAME_MAX
71# ifdef _WIN32
72# include <windef.h> // On Windows we have MAX_PATH too
73# endif //_WIN32
74# if (defined(MAX_PATH) && MAX_PATH>PATH_MAX)
75# define DIRENT_MAX_PATH MAX_PATH
76# else // (defined(MAX_PATH) && MAX_PATH>PATH_MAX)
77# define DIRENT_MAX_PATH PATH_MAX
78# endif // (defined(MAX_PATH) && MAX_PATH>PATH_MAX)
79
80namespace ImGuiFs {
81
82# if (!defined(IMGUIFS_MEMORY_USES_CHARS_AS_BYTES) || !defined(IMGUIFILESYSTEM_USE_ASCII_SHORT_PATHS_ON_WINDOWS))
83const int MAX_FILENAME_BYTES = FILENAME_MAX*4; // Worst case: 4 bytes per char, but huge waste of memory [we SHOULD have used imguistring.h!]
85#else //IMGUIFS_MEMORY_USES_CHARS_AS_BYTES
88#endif //IMGUIFS_MEMORY_USES_CHARS_AS_BYTES
89// A bit dangerous typedefs:
91typedef char PathString[MAX_PATH_BYTES];
92// Handy typedefs:
93typedef ImVector<FilenameString> FilenameStringVector;
94typedef ImVector<PathString> PathStringVector;
95
96enum Sorting {
106};
107
108} // namespace ImGuiFs
109#endif //IMGUIFS_NO_EXTRA_METHODS
110
111
112
113#ifdef IMGUIFILESYSTEM_USE_ASCII_SHORT_PATHS_ON_WINDOWS
114#undef DIRENT_USE_ASCII_SHORT_PATHS_ON_WINDOWS
115#define DIRENT_USE_ASCII_SHORT_PATHS_ON_WINDOWS
116#endif //IMGUIFILESYSTEM_USE_ASCII_SHORT_PATHS_ON_WINDOWS
117#include "dirent_portable.h"
118#include <sys/stat.h>
119#include <ctype.h> // tolower,...
120#include <string.h> // strcmp
121#ifndef _WIN32
122#include <stdlib.h> // realpath getenv
123#endif //_WIN32
124
125
126#if (defined(_MSC_VER) && !defined(strcasecmp))
127# define strcasecmp _stricmp
128/*
129 // Never tested (I've just been told that cl.exe does not have strcasecmp: please search the web for other possible alternative implementations)
130 inline static int strcasecmp( const char *s1, const char *s2 ) {
131 return _stricmp(s1,s2);
132 //return lstrcmpiA(s1,s2); // Not sure this is better
133 }
134*/
135# endif //(defined(_MSC_VER) && !defined(strcasecmp))
136
137
138namespace ImGuiFs {
139
140// Definitions of some helper classes (String,Path,SortingHelper,Directory). Better not expose them in the header file----------
141/*
142// MAIN ISSUE: All these string handling methods work with ASCII strings,
143// but the ones returned by dirent are multibyte OS dependant strings.
144// That means that there are some limitations:
145
146// LIMITATIONS:
147// -> paths with '/','\\','.' bytes (and possibly a few others) inside multibyte codepoints are not supported (*)
148// -> file extensions composed by characters with more than one byte are not supported (**)
149
150//(*) That's because when I call something like: mystring.find_last_of('/') or Path::Split('/')
151// the result might not be correct if there's some multibyte codepoint that includes that byte(s) (bytes include '/','\\','.').
152
153// (**) String::ToLower() deeply breaks any multibyte char.
154// They're currently used only in:
155// Path::GetExtension(...)
156// Directory::GetFiles(const string& path,const string& wantedExtensions,const string& unwantedExtensions)
157// 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).
158*/
159class String {
160protected:
162public:
163 inline static void PushBack(FilenameStringVector& rv,const char* s) {
164 if (rv.Size == rv.Capacity) rv.reserve(rv._grow_capacity(rv.Size+1)); // optional optimization from ImVector<>::push_back()
165 const size_t sz = rv.size();
166 rv.resize(sz+1);
167 strcpy(&rv[sz][0], s ? s : "\0");
168 }
169# if (FILENAME_MAX!=DIRENT_MAX_PATH) // Will this work ? (I don't want to use templates)
170 inline static void PushBack(PathStringVector& rv,const char* s) {
171 const size_t sz = rv.size();
172 rv.resize(sz+1);
173 strcpy(&rv[sz][0], s ? s : "\0");
174 }
175# endif //#if (FILENAME_MAX!=DIRENT_MAX_PATH)
176 inline static void Substr(const char* text,char* rv,int start,int count=-1) {
177 if (!text) count=0;
178 if (count<0) count = (int) strlen(text) - start;
179 if (count>0) strncpy(rv,&text[start],count);
180 rv[count]='\0';
181 }
182 inline static int Find(const char* text,const char toFind,int beg=0) {
183 if (!text) return -1;
184 for (size_t i=beg,len=strlen(text);i<len;i++) {
185 if (text[i]==toFind) return i;
186 }
187 return -1;
188 }
189 inline static int FindLastOf(const char* text,const char toFind) {
190 if (!text) return -1;
191 for (int i=(int)strlen(text)-1;i>=0;i--) {
192 if (text[i]==toFind) return i;
193 }
194 return -1;
195 }
196 inline static void ToLower(const char* text,char* rv) {
197 if (!text) {
198 rv[0]='\0';
199 return;
200 }
201 const size_t len = strlen(text);
202 for (size_t i=0;i<len;i++) {
203 rv[i]=tolower(text[i]);
204 }
205 rv[len]='\0';
206 }
207 inline static void ToLowerInPlace(char* text) {
208 if (!text) return;
209 for (size_t i=0,len = strlen(text);i<len;i++) {
210 char& c = text[i];
211 c=tolower(c);
212 }
213 }
214 inline static void Split(const char* text,FilenameStringVector& rv,const char c=' ') {
215 rv.clear();
216 if (!text) return;
217 const int len = (int)strlen(text);
218 if (len==0) return;
219 int beg = 0;char tmp[MAX_FILENAME_BYTES];
220 for (int i=0;i<len;i++) {
221 const char ch = text[i];
222 if (ch==c) {
223 Substr(text,tmp,beg,i-beg);
224 PushBack(rv,tmp);
225 beg = i+1;
226 }
227 }
228 if (beg<len) {
229 Substr(text,tmp,beg,len-beg);
230 PushBack(rv,tmp);
231 }
232 }
233 inline static void Replace(const char* baseText,const char textToReplace,const char replacement,char* rv) {
234 strcpy(rv,baseText);
235 ReplaceInPlace(rv,textToReplace,replacement);
236 }
237 inline static void ReplaceInPlace(char* text,const char toReplace,const char replacement) {
238 if (!text) return;
239 for (size_t i=0,len = strlen(text);i<len;i++) {
240 char& c = text[i];
241 if (c==toReplace) c=replacement;
242 }
243 }
244
245# ifdef _WIN32
246 // Convert a wide Unicode string to an UTF8 string
247 inline static void wide_to_utf8(const wchar_t* wstr,char* rv) {
248 rv[0]='\0';
249 if (!wstr) return;
250 int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
251 WideCharToMultiByte (CP_UTF8, 0, wstr, -1, &rv[0], size_needed, NULL, NULL);
252 //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.
253 return ;
254 }
255 // Convert an UTF8 string to a wide Unicode String
256 inline static void utf8_to_wide(const char* str,wchar_t* rv) {
257 rv[0]=L'\0';
258 if (!str) return;
259 int size_needed = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
260 MultiByteToWideChar (CP_UTF8, 0, str, -1, &rv[0], size_needed);
261 //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.
262 return;
263 }
264# endif // _WIN32
265};
266class Path {
267protected:
268 Path() {}
269public:
270
271 static void GetAbsolutePath(const char *path, char *rv) {
272 rv[0]='\0';
273# ifndef _WIN32
274 if (!path || strlen(path)==0) {
275 realpath("./", rv);
276 }
277 else {
278 realpath(path, rv);
279 }
280 //printf("GetAbsolutePath(\"%s\",\"%s\");\n",path,rv);fflush(stdout);
281# else //_WIN32
282 //fprintf(stderr,"GetAbsolutePath(\"%s\"); (len:%d)\n",path,(int) strlen(path)); // TO remove!
283 static const int bufferSize = DIRENT_MAX_PATH+1; // 4097 is good (PATH_MAX should be in <limits.h>, or something like that)
284 static wchar_t buffer[bufferSize];
285 static wchar_t wpath[bufferSize];
286 String::utf8_to_wide((path && strlen(path)>0) ? path : "./",wpath);
287 ::GetFullPathNameW(&wpath[0],bufferSize,&buffer[0],NULL);
288 String::wide_to_utf8(&buffer[0],rv);
289
290 String::ReplaceInPlace(rv,'\\','/');
291 size_t len;
292 while ( (len=strlen(rv))>0 && rv[len-1]=='/') rv[len-1]='\0';
293 //fprintf(stderr,"AbsolutePath = \"%s\" (len:%d)\n",rv,(int) strlen(rv)); // TO remove!
294# endif // _WIN32
295 }
296 static void GetDirectoryName(const char *filePath, char *rv) {
297 rv[0]='\0';if (!filePath) return;
298 const int sz = strlen(filePath);
299 if (sz==0 || strcmp(filePath,"/")==0 || strcmp(filePath,"\\")==0) {
300 strcpy(rv,filePath);
301 return;
302 }
303 const char c = filePath[sz-1];
304 if (c == '/' || c=='\\') {
305 char tmp[MAX_PATH_BYTES];
306 String::Substr(filePath,tmp,0,sz-1);
307 GetDirectoryName(tmp,rv);
308 return;
309 }
310
311 if (c==':') {
312 strcpy(rv,filePath);
313 return;
314 }
315 int beg=String::FindLastOf(filePath,'\\');
316 int beg2=String::FindLastOf(filePath,'/');
317 beg=(beg>beg2?beg:beg2);
318 if (beg==0) {
319 String::Substr(filePath,rv,0,1);
320 return;
321 }
322 if (beg!=-1) {
323 String::Substr(filePath,rv,0,beg);
324 return;
325 }
326 rv[0]='\0';
327 return;
328 }
329 static void GetFileName(const char *filePath, char *rv) {
330 int beg=String::FindLastOf(filePath,'\\');
331 int beg2=String::FindLastOf(filePath,'/');
332 beg=(beg>beg2?beg:beg2);
333 if (beg!=-1) {
334 String::Substr(filePath,rv,beg+1);
335 return;
336 }
337 strcpy(rv,filePath);
338 return;
339 }
340 static void GetFileNameWithoutExtension(const char *filePath, char *rv) {
341 int beg=String::FindLastOf(filePath,'\\');
342 int beg2=String::FindLastOf(filePath,'/');
343 int beg3=String::FindLastOf(filePath,'.');
344 beg=(beg>beg2?beg:beg2);
345 if (beg!=-1) {
346 if (beg3<beg) {
347 String::Substr(filePath,rv,beg+1,(beg3<beg+1)?-1:(beg3-beg+1));
348 return;
349 }
350 }
351 if (beg3!=-1) {
352 String::Substr(filePath,rv,0,beg3);
353 return;
354 }
355 strcpy(rv,filePath);
356 return;
357 }
358 static void GetExtension(const char* filePath,char *rv) {
359 int beg=String::FindLastOf(filePath,'.');
360 int beg2=String::FindLastOf(filePath,'/');
361 int beg3=String::FindLastOf(filePath,'\\');
362 if (beg2!=-1) {
363 if (beg3!=-1) beg2 = beg3;
364 else beg2 = beg2 > beg3 ? beg2 : beg3;
365 }
366 else if (beg3!=-1) beg2 = beg3;
367 else {
368 if (beg!=-1) {
369 String::Substr(filePath,rv,beg);
371 return;
372 }
373 }
374 if (beg>beg2) {
375 if (beg!=-1) {
376 String::Substr(filePath,rv,beg);
378 return;
379 }
380 }
381 rv[0]='\0';
382 return;
383 }
384 static void ChangeExtension(const char* filePath,const char* newExtension,char *rv) {
385 strcpy(rv,filePath);
386 int beg=String::FindLastOf(rv,'.');
387 int beg2=String::FindLastOf(rv,'/');
388 int beg3=String::FindLastOf(rv,'\\');
389 if (beg2!=-1) {
390 if (beg3!=-1) beg2 = beg3;
391 else beg2 = beg2 > beg3 ? beg2 : beg3;
392 }
393 else if (beg3!=-1) beg2 = beg3;
394 else {
395 if (beg!=-1) {
396 strcpy(&rv[beg],newExtension);
397 return;
398 }
399 }
400 if (beg>beg2) {
401 if (beg!=-1) {
402 strcpy(&rv[beg],newExtension);
403 return;
404 }
405 }
406 return;
407 }
408 static inline bool HasZipExtension(const char* filePath) {
409 static const int dot = (int) '.';
410 const char * p1 = strrchr(filePath, dot );
411 if (!p1) return false;
412 const int len = strlen(p1);
413 if (len!=4) return false;
414 static const char lower[]=".zip";static const char upper[]=".ZIP";
415 char c;
416 for (int i=1;i<4;i++) {
417 c = p1[i];
418 if (c!=lower[i] && c!=upper[i]) return false;
419 }
420 return true;
421 }
422 static void Combine(const char* directory,const char* fileName,char* rv,bool appendMode=true) {
423 if (!appendMode) rv[0]='\0';
424 const size_t size= directory ? strlen(directory) : 0;
425 if (size==0) {
426 strcat(rv,fileName);
427 return;
428 }
429 strcat(rv,directory);
430 if (directory[size-1]!='\\' && directory[size-1]!='/') {
431 strcat(rv,"/");
432 strcat(rv,fileName);
433 }
434 else strcat(rv,fileName);
435 return;
436 }
437 static void Append(const char* directory,char* rv) {
438 if (!directory || strlen(directory)==0) return;
439 size_t size = strlen(rv);
440 if (size>0 && (rv[size-1]!='\\' && rv[size-1]!='/')) {strcat(rv,"/");++size;}
441 strcat(rv,directory);
442 size = strlen(rv);
443 while (size>0 && (rv[size-1]=='\\' || rv[size-1]=='/')) {rv[size-1]='\0';--size;}
444 if (size==0 || rv[size-1]==':') strcat(rv,"/");
445 }
446 static void Split(const char* path,FilenameStringVector& rv,bool leaveIntermediateTrailingSlashes=true) {
447 rv.clear();
448 static char tex[MAX_PATH_BYTES];
449 String::Replace(path,'\\','/',tex);
450 size_t len = strlen(tex);
451 static char tex2[MAX_PATH_BYTES];
452# ifdef _WIN32
453 while ((len = strlen(tex))>0 && tex[len-1]=='/') {
454 strncpy(tex2,tex,len+1);
455 String::Substr(tex2,tex,0,len-1);
456 }
457# endif //_WIN32
458 if (len==0) return;
459 int beg=-1;
460 while ( (beg = String::Find(tex,'/'))!=-1) {
461 static char tmp[MAX_FILENAME_BYTES];
462 String::Substr(tex,tmp,0,leaveIntermediateTrailingSlashes ? beg+1 : beg);
463 String::PushBack(rv,tmp);
464 strcpy(tex2,tex);
465 String::Substr(tex2,tex,beg+1);
466 }
467 String::PushBack(rv,tex);
468 if (rv.size()>0 && strlen(rv[0])==0) strcpy((char*)&rv[0][0],"/\0");
469# ifdef _WIN32
470 if (rv.size()==1 && strlen(rv[0])>0 && rv[0][strlen(rv[0])-1]==':') strcat((char*)&rv[0][0],"/");
471# endif //_WIN32
472 return;
473 }
474 /*
475 inline static bool Exists(const char* path) {
476 struct stat statbuf;
477 return (stat(path, &statbuf) != -1);
478 }
479 */
480};
481/*
482class File {
483public:
484 inline static bool Exists(const char* path) {
485 struct stat statbuf;
486 return (stat(path, &statbuf) != -1 && (S_ISREG(statbuf.st_mode)));// || (acceptLinks ? S_ISLNK(statbuf.st_mode) : 1));
487 }
488protected:
489 File() {}
490};
491*/
493public:
494 typedef int (*SorterSignature)(const struct dirent **e1,const struct dirent **e2);
495 inline static const SorterSignature& SetSorter(Sorting sorting) {
496 const int isort =(int) sorting;
497 if (isort>=0 && isort<(int)SORT_ORDER_COUNT) return (sorter = Sorters[isort]);
498 return (sorter = Sorters[0]);
499 }
500protected:
502 const static SorterSignature Sorters[];
503 static struct stat stat1;
504 static struct stat stat2;
506 // Possible problem: sorting is in ASCII with these methods
507 static int IMGUIFS_CDECL Alphasort(const struct dirent **e1,const struct dirent **e2) {
508 return strcasecmp((*e1)->d_name,(*e2)->d_name);
509 }
510 static int IMGUIFS_CDECL Alphasortinverse (const struct dirent **e1,const struct dirent **e2) {
511 return -strcasecmp((*e1)->d_name,(*e2)->d_name);
512 }
513 // 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)
514 static int IMGUIFS_CDECL Lastmodsort (const struct dirent **e1,const struct dirent **e2) {
515 if (stat((*e1)->d_name,&stat1)==-1) return -1;
516 if (stat((*e2)->d_name,&stat2)==-1) return 1;
517 return (stat1.st_mtime < stat2.st_mtime ? -1 : stat1.st_mtime > stat2.st_mtime ? 1 : 0);
518 }
519 static int IMGUIFS_CDECL Lastmodsortinverse(const struct dirent **e1,const struct dirent **e2) {
520 if (stat((*e1)->d_name,&stat1)==-1) return 1;
521 if (stat((*e2)->d_name,&stat2)==-1) return -1;
522 return (stat2.st_mtime < stat1.st_mtime ? -1 : stat2.st_mtime > stat1.st_mtime ? 1 : 0);
523 }
524 static int IMGUIFS_CDECL Sizesort (const struct dirent **e1,const struct dirent **e2) {
525 if (stat((*e1)->d_name,&stat1)==-1) return -1;
526 if (stat((*e2)->d_name,&stat2)==-1) return 1;
527 return (stat1.st_size < stat2.st_size ? -1 : stat1.st_size > stat2.st_size ? 1 : 0);
528 }
529 static int IMGUIFS_CDECL Sizesortinverse(const struct dirent **e1,const struct dirent **e2) {
530 if (stat((*e1)->d_name,&stat1)==-1) return 1;
531 if (stat((*e2)->d_name,&stat2)==-1) return -1;
532 return (stat2.st_size < stat1.st_size ? -1 : stat2.st_size > stat1.st_size ? 1 : 0);
533 }
534 // Please note that calculating the file extension every time inside sorters is a suicide (well, much less than before...)!
535 static int IMGUIFS_CDECL Typesort(const struct dirent **e1,const struct dirent **e2) {
536 static const int dot = (int) '.';
537 const char * p1 = strrchr((const char*) (*e1)->d_name, dot );
538 const char * p2 = strrchr((const char*) (*e2)->d_name, dot );
539 if (!p1) return (!p2?0:-1);
540 else if (!p2) return 1;
541 return strcasecmp(p1,p2);
542 }
543 static int IMGUIFS_CDECL Typesortinverse (const struct dirent **e1,const struct dirent **e2) {
544 static const int dot = (int) '.';
545 const char * p1 = strrchr((const char*) (*e1)->d_name, dot );
546 const char * p2 = strrchr((const char*) (*e2)->d_name, dot );
547 if (!p1) return (!p2?0:1);
548 else if (!p2) return -1;
549 return -strcasecmp(p1,p2);
550 }
551
552};
555struct stat SortingHelper::stat1;
556struct stat SortingHelper::stat2;
558public:
559 static void GetDirectories(const char* directoryName,PathStringVector& result,FilenameStringVector* pOptionalNamesOut=NULL,Sorting sorting= SORT_ORDER_ALPHABETIC) {
560 result.clear();if (pOptionalNamesOut) pOptionalNamesOut->clear();
561 static char tempString[MAX_PATH_BYTES];size_t sz;
562 struct dirent **eps = NULL;
563
564 sz = strlen(directoryName);
565 static char directoryName2[MAX_PATH_BYTES];
566 strcpy(directoryName2,directoryName);
567# ifdef _WIN32
568 if (sz>0 && directoryName[sz-1]==':') {directoryName2[sz]='\\';directoryName2[sz+1]='\0';}
569# endif //_WIN32
570 const int n = scandir (directoryName2, &eps, DirentGetDirectories, SortingHelper::SetSorter(sorting));
571
572 static char directoryNameWithoutSlash[MAX_PATH_BYTES];
573 if (sz>0 && directoryName[sz-1] == '/') String::Substr(directoryName,directoryNameWithoutSlash,0,sz-1);
574 else strcpy(directoryNameWithoutSlash,directoryName);
575
576 if (n >= 0) {
577 result.reserve((size_t)n);
578 if (pOptionalNamesOut) pOptionalNamesOut->reserve((size_t)n);
579 for (int cnt = 0; cnt < n; ++cnt) {
580 const char* pName = &eps[cnt]->d_name[0];
581 sz = strlen(pName);
582 if (sz>0) {
583 if (strcmp(pName,".")!=0 && strcmp(pName,"..")!=0 && pName[0]!='.' && pName[sz-1]!='~'
584# ifdef __EMSCRIPTEN__
585 && (!(
586 strcmp(pName,"fd")==0 && strcmp(directoryNameWithoutSlash,"/proc/self")==0
587 ))
588# endif //__EMSCRIPTEN__
589 ) {
590 strcpy(tempString,directoryNameWithoutSlash);
591 strcat(tempString,"/");
592 strcat(tempString,pName);
593 String::PushBack(result,tempString);
594 if (pOptionalNamesOut) String::PushBack(*pOptionalNamesOut,pName);
595 }
596 }
597 free(eps[cnt]);
598 }
599 }
600 if (eps) {free(eps);eps=NULL;}
601 }
602 static void GetFiles(const char* directoryName,PathStringVector& result,FilenameStringVector* pOptionalNamesOut=NULL, Sorting sorting= SORT_ORDER_ALPHABETIC) {
603 result.clear();if (pOptionalNamesOut) pOptionalNamesOut->clear();
604 static char tempString[MAX_PATH_BYTES];size_t sz;
605 struct dirent **eps = NULL;
606
607 sz = strlen(directoryName);
608 static char directoryName2[MAX_PATH_BYTES];
609 strcpy(directoryName2,directoryName);
610# ifdef _WIN32
611 if (sz>0 && directoryName[sz-1]==':') {directoryName2[sz]='\\';directoryName2[sz+1]='\0';}
612# endif //_WIN32
613 const int n = scandir (directoryName2, &eps, DirentGetFiles, SortingHelper::SetSorter(sorting));
614
615 static char directoryNameWithoutSlash[MAX_PATH_BYTES];
616 if (sz>0 && directoryName[sz-1] == '/') String::Substr(directoryName,directoryNameWithoutSlash,0,sz-1);
617 else strcpy(directoryNameWithoutSlash,directoryName);
618
619 if (n >= 0) {
620 result.reserve((size_t)n);
621 if (pOptionalNamesOut) pOptionalNamesOut->reserve((size_t)n);
622 for (int cnt = 0; cnt < n; ++cnt) {
623 const char* pName = &eps[cnt]->d_name[0];
624 sz = strlen(pName);
625 if (sz>0) {
626 if (pName[0]!='.' && pName[sz-1]!='~') {
627 strcpy(tempString,directoryNameWithoutSlash);
628 strcat(tempString,"/");
629 strcat(tempString,pName);
630 String::PushBack(result,tempString);
631 if (pOptionalNamesOut) String::PushBack(*pOptionalNamesOut,pName);
632 }
633 }
634 free(eps[cnt]);
635 }
636 }
637 if (eps) {free(eps);eps=NULL;}
638 }
639
640 // e.g. ".txt;.jpg;.png". To use unwantedExtensions, set wantedExtensions="".
641 static void GetFiles(const char* path,PathStringVector& files,const char* wantedExtensions,const char* unwantedExtensions=NULL,FilenameStringVector* pOptionalNamesOut=NULL,Sorting sorting= SORT_ORDER_ALPHABETIC) {
642 PathStringVector filesIn;
643 FilenameStringVector namesIn;
644 GetFiles(path,filesIn,&namesIn,sorting);
645 if ((wantedExtensions==0 || strlen(wantedExtensions)==0) && (unwantedExtensions==0 || strlen(unwantedExtensions)==0)) {files = filesIn;return;}
646 files.clear();if (pOptionalNamesOut) pOptionalNamesOut->clear();
647
648 char wext[MAX_PATH_BYTES];String::ToLower(wantedExtensions,wext);
649 char woext[MAX_PATH_BYTES];String::ToLower(unwantedExtensions,woext);
650
651 char ext[MAX_PATH_BYTES];
652 if (wantedExtensions && strlen(wantedExtensions)>0) {
653 files.reserve(filesIn.size());
654 if (pOptionalNamesOut) pOptionalNamesOut->reserve(namesIn.size());
655 FilenameStringVector wExts;String::Split(wext,wExts,';');
656 const size_t wExtsSize = wExts.size();
657 if (wExtsSize>0) {
658 for (size_t i = 0,sz = filesIn.size();i<sz;i++) {
659 Path::GetExtension(filesIn[i],ext);
660 for (size_t e=0;e<wExtsSize;e++) {
661 if (strcmp(ext,wExts[e])==0) {
662 String::PushBack(files,filesIn[i]);
663 if (pOptionalNamesOut) String::PushBack(*pOptionalNamesOut,namesIn[i]);
664 }
665 }
666 }
667 }
668 else return;
669 }
670 else if (unwantedExtensions && strlen(unwantedExtensions)>0) {
671 //files.reserve(filesIn.size());if (pOptionalNamesOut) pOptionalNamesOut->reserve(namesIn.size());
672 FilenameStringVector woExts;String::Split(woext,woExts,';');
673 const size_t woExtsSize = woExts.size();
674 if (woExts.size()==0) {files = filesIn;return;}
675 bool match;
676 for (size_t i = 0,sz = filesIn.size();i<sz;i++) {
677 Path::GetExtension(filesIn[i],ext);
678 match = false;
679 for (size_t e=0;e<woExtsSize;e++) {
680 if (strcmp(ext,woExts[e])==0) {
681 match = true;
682 break;
683 }
684 }
685 if (!match) {
686 String::PushBack(files,filesIn[i]);
687 if (pOptionalNamesOut) String::PushBack(*pOptionalNamesOut,namesIn[i]);
688 }
689 }
690 }
691 else {
692 files = filesIn;
693 if (pOptionalNamesOut) *pOptionalNamesOut = namesIn;
694 }
695 }
696
697 inline static void Create(const char* directoryName) {
698# ifndef _WIN32
699 const mode_t mode = S_IFDIR | S_IREAD | S_IWRITE | S_IRWXU | S_IRWXG | S_IRWXO;
700 mkdir(directoryName,mode);
701# else //_WIN32
702 static wchar_t name[DIRENT_MAX_PATH+1];
703 String::utf8_to_wide(directoryName,name);
704 ::CreateDirectoryW(name,NULL);
705# endif //_WIN32
706 }
707
708 inline static bool Exists(const char* path) {
709 struct stat statbuf;
710 return (stat(path, &statbuf) != -1 && S_ISDIR(statbuf.st_mode));
711 }
712 inline static const PathStringVector &GetUserKnownDirectories(const FilenameStringVector **pOptionalUserKnownDirectoryDisplayNamesOut,const int** pOptionalNumberKnownUserDirectoriesExceptDrives=NULL,bool forceUpdate=false) {
713 static bool init = false;
714 static PathStringVector rv;
715 static FilenameStringVector dn;
716 static PathStringVector mediaFolders;
717 static int numberKnownUserDirectoriesExceptDrives = 0;
718 if (pOptionalUserKnownDirectoryDisplayNamesOut) *pOptionalUserKnownDirectoryDisplayNamesOut = &dn;
719 if (pOptionalNumberKnownUserDirectoriesExceptDrives) *pOptionalNumberKnownUserDirectoriesExceptDrives = &numberKnownUserDirectoriesExceptDrives;
720 if (init && !forceUpdate) return rv;
721 init = true;
722 rv.clear();dn.clear();
723# ifdef _WIN32
724 static const int csid[] = {
725 CSIDL_DESKTOP,
726 CSIDL_PERSONAL, //(Documents)
727 CSIDL_FAVORITES,
728 CSIDL_MYMUSIC,
729 CSIDL_MYPICTURES,
730 CSIDL_RECENT,
731 CSIDL_MYVIDEO
732 };
733 static const char* name[] = {
734 "Desktop",
735 "Documents",
736 "Favorites",
737 "Music",
738 "Pictures",
739 "Recent",
740 "Video"
741 };
742 static const int csidSize = sizeof(csid)/sizeof(csid[0]);
743 static const int nameSize = sizeof(name)/sizeof(name[0]);
744 IM_ASSERT(csidSize==nameSize);
745 if (csidSize!=nameSize) fprintf(stderr,"ERROR in file: imguifilesystem.cpp. Directory::GetUserKnownDirectories(...) csidSize!=nameSize.\n");
746 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);}
747 rv.reserve(csidSize+mediaFolders.size());
748 dn.reserve(csidSize+mediaFolders.size());
749 WCHAR path[MAX_PATH+1];
750 for (int i=0;i<csidSize;i++) {
751 if (!GetSpecialFolderPathW(csid[i],&path[0],NULL)) continue;
752 static char tmp2[MAX_PATH_BYTES];
753 String::wide_to_utf8(&path[0],tmp2);
754 String::PushBack(rv,tmp2);
755 String::PushBack(dn,name[i]);
756 }
757 numberKnownUserDirectoriesExceptDrives = (int) rv.size();
758 static char mediaFolderName[MAX_PATH_BYTES];
759 for (int i=0,msz=mediaFolders.size();i<msz;i++) {
760 const char* mediaFolder = mediaFolders[i];
761 String::PushBack(rv,mediaFolder);
762 String::Substr(mediaFolder,mediaFolderName,0,strlen(mediaFolder)-1);
763 String::PushBack(dn,mediaFolderName);
764 }
765# else //_WIN32
766 const char* homedir = NULL;
767 if ((homedir = getenv("HOME")) == NULL) {
768 homedir = getpwuid(getuid())->pw_dir;
769 }
770 if (homedir==NULL) return rv;
771 char homeString[MAX_PATH_BYTES];strcpy(homeString,homedir);
772 char userString[MAX_PATH_BYTES];Path::GetFileName(homeString,userString);
773 // Known folders ---------------------------------------------
774 static const char folder[][MAX_FILENAME_BYTES] = {
775 "Desktop",
776 "Documents",
777 "Downloads",
778 "Music",
779 "Pictures",
780 "Videos"
781 };
782 static const int folderSize = sizeof(folder)/sizeof(folder[0]);
783 rv.reserve(folderSize+1);
784 dn.reserve(rv.size());
785 String::PushBack(rv,homeString);
786 char temp[MAX_PATH_BYTES];
787 strcpy(temp,"Home");
788 String::PushBack(dn,temp);
789 for (int i=0;i<folderSize;i++) {
790 Path::Combine(homeString,folder[i],temp,false);
791 if (Directory::Exists(temp)) {
792 String::PushBack(rv,temp);
793 String::PushBack(dn,folder[i]);
794 }
795 }
796 numberKnownUserDirectoriesExceptDrives = (int) rv.size();
797 // Additional Drives --------------------------------------------
798 static const char* mountLocations[] = {"/media","/mnt","/Volumes","/vol","/data"};
799 static const int mountLocationSize = sizeof(mountLocations)/sizeof(mountLocations[0]);
800 static const bool ifHomeSubfolerIsFoundInMountLocationsForgetThatRootMountLocation = true; // That means: if "/media/myusername" exists, don't add any other "/media/..." entries.
801 char userMediaString[MAX_PATH_BYTES];bool lastGood = false;
802 for (int mntLocIndex=0,sz = 2*mountLocationSize;mntLocIndex<sz;mntLocIndex++) {
803 const int index = mntLocIndex/2;
804 const char* mntLocString = mountLocations[index];
805 const bool useUserSuffix = (mntLocIndex%2)==0;
806 if (useUserSuffix) {
807 Path::Combine(mntLocString,userString,userMediaString,false);
808 strcpy(temp,userMediaString);
809 }
810 else if (lastGood && ifHomeSubfolerIsFoundInMountLocationsForgetThatRootMountLocation) {lastGood = false;continue;} // see "ifHomeSubfolerIsFoundInMountLocationsForgetThatRootMountLocation" above
811 else strcpy(userMediaString,mntLocString);
812 lastGood = Directory::Exists(userMediaString);
813 if (!lastGood) continue;
814 Directory::GetDirectories(userMediaString,mediaFolders);
815 if (mediaFolders.size()==0) continue;
816 rv.reserve(rv.size()+mediaFolders.size());
817 dn.reserve(rv.size());
818 for (int i=0,msz=mediaFolders.size();i<msz;i++) {
819 if (strcmp(mediaFolders[i],temp)==0) continue; // I we have processed "/media/myusername" once cycle before, exclude it from processing "/media" subfolders
820 String::PushBack(rv,mediaFolders[i]);
821 static char tmp[MAX_FILENAME_BYTES];
822 Path::GetFileName(mediaFolders[i],tmp);
823 String::PushBack(dn,tmp);
824 }
825 }
826# endif //_WIN32
827
828 return rv;
829 }
830
831
832protected:
834
835 static int DirentGetDirectories(const struct dirent *de) {
836 if (de->d_type==DT_DIR) return 1;
837 return 0;
838 }
839 static int DirentGetFiles(const struct dirent *de) {
840 if (de->d_type==DT_REG) return 1;
841 return 0;
842 }
843
844# ifdef _WIN32
845 static bool GetSpecialFolderPathW(int specialFolderCSIDL,WCHAR* pathOutWithSizeMaxPathPlusOne,HWND parent) {
846 // CSIDL_DESKTOP,CSIDL_BITBUCKET,CSIDL_CONTROLS,CSIDL_DESKTOP,CSIDL_DESKTOPDIRECTORY,
847 // CSIDL_DRIVES,CSIDL_FONTS,CSIDL_NETHOOD,CSIDL_NETWORK,CSIDL_PERSONAL (Documents)
848 // CSIDL_PRINTERS,CSIDL_PROGRAMS,CSIDL_RECENT,CSIDL_SENDTO,CSIDL_STARTMENU,
849 // CSIDL_STARTUP,CSIDL_TEMPLATES
850
851 // CSIDL_INTERNET_CACHE,CSIDL_COOKIES,CSIDL_HISTORY,CSIDL_COMMON_APPDATA,
852 // CSIDL_WINDOWS,CSIDL_SYSTEM,CSIDL_PROGRAM_FILES,CSIDL_MYPICTURES,...
853
854 WCHAR* temp_path = pathOutWithSizeMaxPathPlusOne;//[MAX_PATH+1];
855 temp_path[0]=L'\0';
856 LPITEMIDLIST pidl=NULL;
857 if (!SUCCEEDED(::SHGetSpecialFolderLocation(parent,specialFolderCSIDL, &pidl)))
858 {
859 temp_path[0]=L'\0';return false;
860 }
861 bool ok=SUCCEEDED(::SHGetPathFromIDListW(pidl,&temp_path[0]));
862
863 LPMALLOC mal = NULL;
864 if ( ::SHGetMalloc( & mal ) == E_FAIL || !mal ) ::free( pidl );
865 else
866 {
867 mal->Free( pidl );
868 mal->Release();
869 }
870 if (!ok)
871 {
872 temp_path[0]=L'\0';return false;
873 }
874 return true;
875 }
876# endif //_WIN32
877
878};
879#ifndef IMGUIFS_NO_EXTRA_METHODS
880#ifdef IMGUI_USE_MINIZIP
881struct UnZipFileImpl {
882 unzFile uf;
883 char zipFilePath[MAX_PATH_BYTES];
884
885 struct unz_file_info64_plus {
886 unz_file_info64 info;
887 char path[MAX_PATH_BYTES];
888 char name[MAX_PATH_BYTES];
889 void assign(const char* _path,const char* _name,const unz_file_info64& _info) {
890 strcpy(path,_path);strcpy(name,_name);
891 memcpy(&info,&_info,sizeof(info));
892 }
893 static void Sort(ImVector<unz_file_info64_plus>& fileInfos,Sorting sorting) {
894 if (fileInfos.size()==0) return;
895 typedef int (*Sorter)(const void *, const void *);
896 Sorter sorter = NULL;
897 switch (sorting) {
898 case SORT_ORDER_ALPHABETIC: sorter = &unz_file_info64_plus::Alphasort;break;
899 case SORT_ORDER_ALPHABETIC_INVERSE: sorter = &unz_file_info64_plus::Alphasortinverse;break;
900 case SORT_ORDER_LAST_MODIFICATION: sorter = &unz_file_info64_plus::LastModsort;break;
901 case SORT_ORDER_LAST_MODIFICATION_INVERSE: sorter = &unz_file_info64_plus::LastModsortinverse;break;
902 case SORT_ORDER_SIZE: sorter = &unz_file_info64_plus::Sizesort;break;
903 case SORT_ORDER_SIZE_INVERSE: sorter = &unz_file_info64_plus::Sizesortinverse;break;
904 case SORT_ORDER_TYPE: sorter = &unz_file_info64_plus::Typesort;break;
905 case SORT_ORDER_TYPE_INVERSE: sorter = &unz_file_info64_plus::Typesortinverse;break;
906 default: break;
907 }
908 if (sorter) qsort(&fileInfos[0],fileInfos.size(),sizeof(unz_file_info64_plus),sorter);
909 }
910 //void qsort( void *ptr, size_t count, size_t size,int (*comp)(const void *, const void *) );
911 inline static int IMGUIFS_CDECL Alphasort(const void* e1,const void* e2) {
912 return strcasecmp(static_cast<const unz_file_info64_plus*>(e1)->name,static_cast<const unz_file_info64_plus*>(e2)->name);
913 }
914 inline static int IMGUIFS_CDECL Alphasortinverse(const void* e1,const void* e2) {
915 return -strcasecmp(static_cast<const unz_file_info64_plus*>(e1)->name,static_cast<const unz_file_info64_plus*>(e2)->name);
916 }
917 inline static int IMGUIFS_CDECL LastModsort(const void* e1,const void* e2) { // or tmu_date ?
918 const uLong& dosDate1 = static_cast<const unz_file_info64_plus*>(e1)->info.dosDate;
919 const uLong& dosDate2 = static_cast<const unz_file_info64_plus*>(e2)->info.dosDate;
920 return (dosDate1<dosDate2) ? -1 : (dosDate1>dosDate2) ? 1 : 0;
921 }
922 inline static int IMGUIFS_CDECL LastModsortinverse(const void* e1,const void* e2) {
923 const uLong& dosDate1 = static_cast<const unz_file_info64_plus*>(e1)->info.dosDate;
924 const uLong& dosDate2 = static_cast<const unz_file_info64_plus*>(e2)->info.dosDate;
925 return (dosDate1<dosDate2) ? 1 : (dosDate1>dosDate2) ? -1 : 0;
926 }
927 inline static int IMGUIFS_CDECL Sizesort(const void* e1,const void* e2) {
928 const ZPOS64_T& size1 = static_cast<const unz_file_info64_plus*>(e1)->info.uncompressed_size;
929 const ZPOS64_T& size2 = static_cast<const unz_file_info64_plus*>(e2)->info.uncompressed_size;
930 return (size1<size2) ? -1 : (size1>size2) ? 1 : 0;
931 }
932 inline static int IMGUIFS_CDECL Sizesortinverse(const void* e1,const void* e2) {
933 const ZPOS64_T& size1 = static_cast<const unz_file_info64_plus*>(e1)->info.uncompressed_size;
934 const ZPOS64_T& size2 = static_cast<const unz_file_info64_plus*>(e2)->info.uncompressed_size;
935 return (size1<size2) ? 1 : (size1>size2) ? -1 : 0;
936 }
937 inline static int IMGUIFS_CDECL Typesort(const void* e1,const void* e2) {
938 static const int dot = (int) '.';
939 const char * p1 = strrchr((const char*) static_cast<const unz_file_info64_plus*>(e1)->name, dot );
940 const char * p2 = strrchr((const char*) static_cast<const unz_file_info64_plus*>(e2)->name, dot );
941 if (!p1) return (!p2?0:-1);
942 else if (!p2) return 1;
943 return strcasecmp(p1,p2);
944 }
945 inline static int IMGUIFS_CDECL Typesortinverse(const void* e1,const void* e2) {
946 static const int dot = (int) '.';
947 const char * p1 = strrchr((const char*) static_cast<const unz_file_info64_plus*>(e1)->name, dot );
948 const char * p2 = strrchr((const char*) static_cast<const unz_file_info64_plus*>(e2)->name, dot );
949 if (!p1) return (!p2?0:-1);
950 else if (!p2) return -1;
951 return -strcasecmp(p1,p2);
952 }
953 };
954
955 bool getFilesOrDirectories(bool fileMode,const char* directoryName,PathStringVector& result,FilenameStringVector* pOptionalNamesOut,Sorting sorting,bool prefixResultWithTheFullPathOfTheZipFile) const {
956 result.clear();if (pOptionalNamesOut) pOptionalNamesOut->clear();
957 if (!uf) return false;
958 char dirName[MAX_PATH_BYTES];
959
960 // copy a clean directoryName into dirName
961 size_t dirNameLen = 0;
962 if (directoryName) {
963 dirNameLen = strlen(directoryName);
964 if (dirNameLen>1 && directoryName[0]=='.' && (directoryName[1]=='/' || directoryName[1]=='\\')) {
965 strcpy(dirName,&directoryName[2]);
966 }
967 else strcpy(dirName,directoryName);
968 }
969 else strcpy(dirName,directoryName ? directoryName : "");
970 dirNameLen = strlen(dirName);
971 while (dirNameLen>0 && (dirName[dirNameLen-1]=='/' || dirName[dirNameLen-1]=='\\')) {dirName[dirNameLen-1]='\0';--dirNameLen;}
972
973 // get gi
974 uLong i;unz_global_info64 gi;int err;
975 err = unzGetGlobalInfo64(uf,&gi);
976 if (err!=UNZ_OK) {
977 fprintf(stderr,"error %d with zipfile in unzGetGlobalInfo \n",err);
978 return false;
979 }
980
981 ImVector<unz_file_info64_plus> fileInfos;
982
983 // Fill fileInfos
984 char tmp[MAX_PATH_BYTES]="";char filename_inzip[MAX_PATH_BYTES]="";const char charCrypt[2]="*";
985 unz_file_info64 file_info;bool ok=false;
986 err = unzGoToFirstFile(uf);
987 if (err!=UNZ_OK) {
988 fprintf(stderr,"error %d with zipfile in unzGoToFirstFile",err);
989 return false;
990 }
991
992 for (i=0;i<gi.number_entry;i++) {
993 err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
994 if (err!=UNZ_OK) {
995 fprintf(stderr,"Error %d with zipfile in unzGetCurrentFileInfo\n",err);
996 return false;
997 }
998
999 ok = false;
1000 size_t filename_inzip_len = strlen(filename_inzip);
1001 const bool hasZeroSize = file_info.compressed_size == 0 && file_info.uncompressed_size == 0;
1002 const bool isDirectory = filename_inzip_len>0 && (filename_inzip[filename_inzip_len-1]=='/' || filename_inzip[filename_inzip_len-1]=='\\');
1003 ok = fileMode ? (!isDirectory && !hasZeroSize) : (isDirectory && hasZeroSize) ? true : false;
1004
1005 if (ok) {
1006 if (dirNameLen>0) {
1007 if (dirNameLen>=filename_inzip_len) ok = false;
1008 if (strncmp(dirName,filename_inzip,dirNameLen)!=0) ok = false;
1009 if (filename_inzip[dirNameLen]!='/' && filename_inzip[dirNameLen]!='\\') ok = false;
1010 if (!fileMode && dirNameLen+1 == filename_inzip_len) ok = false;
1011 }
1012 if (ok) {
1013 // remove trailing dash from filename_inzip
1014 if (filename_inzip_len>0 && (filename_inzip[filename_inzip_len-1]=='/' || filename_inzip[filename_inzip_len-1]=='\\')) {filename_inzip[filename_inzip_len-1]='\0';--filename_inzip_len;} // remove trailing dash
1015
1016 strcpy(tmp,dirNameLen>0 ? &filename_inzip[dirNameLen+1] : filename_inzip); // Here "tmp" if the plain name, and "filename_inzip" the full name
1017 size_t tmpLen = strlen(tmp);
1018 for (size_t k=0;k<tmpLen;k++) {
1019 if (tmp[k]=='/' || tmp[k]=='\\') {
1020 if (fileMode || k!=tmpLen-1)
1021 {ok = false;break;}
1022 }
1023 }
1024 if (ok) {
1025 if ((file_info.flag & 1) != 0) strcat(tmp,charCrypt); // Optional display a '*' in the file name only if it is crypted
1026
1027 size_t fileInfosSize = fileInfos.size();
1028 fileInfos.resize(fileInfosSize+1);
1029 fileInfos[fileInfosSize].assign(filename_inzip,tmp,file_info);
1030 //fprintf(stderr,"%s\t%s\t%u\t%u\n",filename_inzip,tmp,(unsigned)file_info.compressed_size,(unsigned)file_info.uncompressed_size);
1031 }
1032 }
1033 }
1034
1035 if (i<gi.number_entry-1) {
1036 err = unzGoToNextFile(uf);
1037 if (err!=UNZ_OK) {
1038 fprintf(stderr,"error %d with zipfile in unzGoToNextFile\n",err);
1039 return false;
1040 }
1041 }
1042
1043 } // for loop
1044
1045if (fileInfos.size()>0) {
1046 //sorting:
1047 if (!fileMode && sorting >= SORT_ORDER_SIZE) sorting = (Sorting)(sorting%2);
1048 unz_file_info64_plus::Sort(fileInfos,sorting);
1049
1050 for (size_t j=0,jsz=fileInfos.size();j<jsz;j++) {
1051 const unz_file_info64_plus& fip = fileInfos[j];
1052 //const unz_file_info64& fi = fip.info;
1053
1054 const size_t resultSize = result.size();
1055 result.resize(resultSize+1);
1056 char* pResult = &result[resultSize][0];
1057 pResult[0]='\0';
1058 if (prefixResultWithTheFullPathOfTheZipFile) {
1059 strcpy(pResult,zipFilePath);
1060 strcat(pResult,"/");
1061 }
1062 strcat(pResult,fip.path);
1063
1064
1065 if (pOptionalNamesOut) {
1066 pOptionalNamesOut->resize(resultSize+1);
1067 char* pName = &(*pOptionalNamesOut)[resultSize][0];
1068 strcpy(pName,fip.name);
1069
1070 //const unz_file_info64& file_info = fip.info;
1071 //fprintf(stderr,"%s\t%s\t%u\t%u\n",pResult,pName,(unsigned)file_info.compressed_size,(unsigned)file_info.uncompressed_size);
1072 }
1073
1074 }
1075
1076}
1077/*
1078 if ((file_info.flag & 1) != 0) strcat(tmp,charCrypt);
1079 const size_t resultSize = result.size();
1080 result.resize(resultSize+1);
1081 char* pResult = &result[resultSize][0];
1082 strcpy(pResult,filename_inzip);
1083
1084 if (pOptionalNamesOut) {
1085 pOptionalNamesOut->resize(resultSize+1);
1086 char* pName = &(*pOptionalNamesOut)[resultSize][0];
1087 strcpy(pName,tmp);
1088 if ((file_info.flag & 1) != 0) strcat(pName,charCrypt); // display a '*' if the file is crypted
1089
1090 //fprintf(stderr,"%s\t%s\t%u\t%u\t%d\n",pResult,pName,(unsigned)file_info.compressed_size,(unsigned)file_info.uncompressed_size,isDirectory);
1091 }
1092*/
1093 err = unzGoToFirstFile(uf);
1094 if (err!=UNZ_OK) {
1095 fprintf(stderr,"error %d with zipfile in unzGoToFirstFile",err);
1096 return false;
1097 }
1099 return true;
1100
1101 }
1102
1103 static bool PathSplitFirstZipFolder(const char* path, char* rv1,char* rv2,bool rv1IsAbsolutePath=true) {
1104 rv1[0]='\0';rv2[0]='\0';
1105 if (path) {
1106 int gc = 0;
1107 const char* lowerZip = ".zip";const char* upperZip = ".ZIP";const int numCharsToMath = (int)strlen(lowerZip);
1108 //fprintf(stderr,"%s %s %s %d %d\n",path,rv1,rv2,(int)strlen(path)-numCharsToMath,numCharsToMath);
1109 for (int i=0,isz=(int)strlen(path)-numCharsToMath;i<=isz;i++) {
1110 char c = path[i];
1111 gc=0;
1112 while (c==lowerZip[gc] || c==upperZip[gc]) {
1113 if (++gc==numCharsToMath) {
1114 gc=i+numCharsToMath;
1115 while (gc<isz && (path[gc]=='/' || path[gc]=='\\')) ++gc;
1116 strcpy(rv2,&path[gc]);
1117
1118 if (rv1IsAbsolutePath) {
1119 char rv3[MAX_PATH_BYTES];
1120 strncpy(rv3,path,i+numCharsToMath);
1121 rv3[i+numCharsToMath]='\0';
1122 //fprintf(stderr,"rv3 %s\n",rv3);
1123 Path::GetAbsolutePath(rv3,rv1);
1124 }
1125 else {
1126 strncpy(rv1,path,i+numCharsToMath);
1127 rv1[i+numCharsToMath]='\0';
1128 }
1129
1130 //fprintf(stderr,"MATCH \"%s\": \"%s\"+\"%s\"\n",path,rv1,rv2);
1131
1132 return true;
1133 }
1134 c = path[i+gc];
1135 }
1136 }
1137 // No zip found:
1138 if (rv1IsAbsolutePath) Path::GetAbsolutePath(path,rv1);
1139 else strcpy(rv1,path);
1140 }
1141 return false;
1142 }
1143 static bool PathExistsWithZipSupport(const char* path, bool reportOnlyFiles, bool reportOnlyDirectories, bool checkAbsolutePath=true, bool* isInsideAZipFile=NULL) {
1144 char rv1[MAX_PATH_BYTES];char rv2[MAX_PATH_BYTES];rv1[0]='\0';rv2[0]='\0';
1145 if (PathSplitFirstZipFolder(path,rv1,rv2,checkAbsolutePath)) {
1146 if (isInsideAZipFile) *isInsideAZipFile = true;
1147 UnZipFile unz(rv1);
1148 return unz.exists(rv2,reportOnlyFiles,reportOnlyDirectories);
1149 }
1150 else {
1151 if (isInsideAZipFile) *isInsideAZipFile = false;
1152 return PathExists(rv1);
1153 }
1154 }
1155};
1156bool UnZipFile::load(const char* zipFilePath,bool reloadIfAlreadyLoaded) {
1157 if (!reloadIfAlreadyLoaded && isValid() && strcmp(zipFilePath,im->zipFilePath)==0) return true;
1158 close();
1159 if (zipFilePath) {
1160 Path::GetAbsolutePath(zipFilePath,im->zipFilePath);
1161 im->uf = unzOpen64(im->zipFilePath);
1162 }
1163 //else if (im->zipFilePath) im->uf = unzOpen64(im->zipFilePath);
1164 return im->uf!=NULL;
1165}
1166const char* UnZipFile::getZipFilePath() const {
1167 return im->zipFilePath;
1168}
1169bool UnZipFile::isValid() const {
1170 return im->uf!=NULL;
1171}
1172void UnZipFile::close() {
1173 if (im->uf) {unzClose(im->uf);im->uf=NULL;}
1174}
1175bool UnZipFile::getDirectories(const char* directoryName,PathStringVector& result,FilenameStringVector* pOptionalNamesOut,Sorting sorting,bool prefixResultWithTheFullPathOfTheZipFile) const {
1176 return im->getFilesOrDirectories(false,directoryName,result,pOptionalNamesOut,sorting,prefixResultWithTheFullPathOfTheZipFile);
1177}
1178bool UnZipFile::getFiles(const char* directoryName,PathStringVector& result,FilenameStringVector* pOptionalNamesOut,Sorting sorting,bool prefixResultWithTheFullPathOfTheZipFile) const {
1179 return im->getFilesOrDirectories(true,directoryName,result,pOptionalNamesOut,sorting,prefixResultWithTheFullPathOfTheZipFile);
1180}
1181unsigned int UnZipFile::getFileSize(const char* filePath) const {
1182 if (!im->uf || !filePath) return 0;
1183 if (unzLocateFile(im->uf,filePath,0)!=UNZ_OK) return 0;
1184 unz_file_info64 file_info;char filename_inzip[2048];
1185 int err = unzGetCurrentFileInfo64(im->uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
1186 if (err!=UNZ_OK) return 0;
1187 unsigned int sz = file_info.uncompressed_size;
1188 if (sz==file_info.uncompressed_size) return sz;
1189 return 0;
1190}
1191template<typename CharType> static bool UnZipFileGetFileContentBase(ImGuiFs::UnZipFileImpl* im,const char* filePath,ImVector<CharType>& bufferOut,const char* password) {
1192 bufferOut.clear();
1193 if (!im->uf || !filePath) return false;
1194 if (unzLocateFile(im->uf,filePath,0)!=UNZ_OK) return false;
1195 unz_file_info64 file_info;char filename_inzip[2048]; // it's the filename without path
1196 int err = unzGetCurrentFileInfo64(im->uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
1197 if (err<0) {
1198 fprintf(stderr,"Error while unzipping: \"%s\": %d with zipfile in unzGetCurrentFileInfo\n",filePath,err);
1199 return false;
1200 }
1201 //if (file_info.uncompressed_size>MAX_UNSIGNED_INT) return false; // where is MAX_UNSIGNED_INT ???
1202 err = unzOpenCurrentFilePassword(im->uf,password);
1203 if (err<0) {
1204 fprintf(stderr,"Error while unzipping: \"%s\": %d with zipfile in unzOpenCurrentFilePassword\n",filePath,err);
1205 return false;
1206 }
1207 bufferOut.resize(file_info.uncompressed_size);
1208 if ((unsigned)bufferOut.size()<file_info.uncompressed_size) {
1209 fprintf(stderr,"Error while unzipping: \"%s\": file is too big.\n",filePath);
1210 ImVector<CharType> tmp;bufferOut.swap(tmp);
1211 unzCloseCurrentFile (im->uf);
1212 return false;
1213 }
1214 err = unzReadCurrentFile(im->uf,&bufferOut[0],bufferOut.size());
1215 if (err<0) {
1216 fprintf(stderr,"Error while unzipping: \"%s\": %d with zipfile in unzReadCurrentFile\n",filePath,err);
1217 ImVector<CharType> tmp;bufferOut.swap(tmp);
1218 unzCloseCurrentFile (im->uf);
1219 return false;
1220 }
1221 err = unzCloseCurrentFile (im->uf);
1222 if (err<0) {
1223 fprintf(stderr,"Error while unzipping: \"%s\": %d with zipfile in unzCloseCurrentFile\n",filePath,err);
1224 }
1225 return true;
1226}
1227bool UnZipFile::getFileContent(const char* filePath,ImVector<unsigned char>& bufferOut,const char* password) const {return UnZipFileGetFileContentBase<unsigned char>(im,filePath,bufferOut,password);}
1228bool UnZipFile::getFileContent(const char* filePath,ImVector<char>& bufferOut,const char* password) const {return UnZipFileGetFileContentBase<char>(im,filePath,bufferOut,password);}
1229bool UnZipFile::exists(const char* pathInsideZip,bool reportOnlyFiles,bool reportOnlyDirectories) const {
1230 if (!im->uf || !pathInsideZip) return false;
1231 char path[MAX_PATH_BYTES];
1232
1233 // copy a clean pathInsideZip into path
1234 size_t dirNameLen = 0;
1235 if (pathInsideZip) {
1236 dirNameLen = strlen(pathInsideZip);
1237 if (dirNameLen>1 && pathInsideZip[0]=='.' && (pathInsideZip[1]=='/' || pathInsideZip[1]=='\\')) {
1238 strcpy(path,&pathInsideZip[2]);
1239 }
1240 else strcpy(path,pathInsideZip);
1241 }
1242 else strcpy(path,pathInsideZip ? pathInsideZip : "");
1243
1244 dirNameLen = strlen(path);
1245 while (dirNameLen>0 && (path[dirNameLen-1]=='/' || path[dirNameLen-1]=='\\')) {path[dirNameLen-1]='\0';--dirNameLen;}
1246
1247 bool onlyDirs = false,onlyFiles=false;
1248 if ((reportOnlyFiles || reportOnlyDirectories) && !(reportOnlyFiles && reportOnlyDirectories)) {
1249 onlyDirs = reportOnlyDirectories;
1250 onlyFiles = reportOnlyFiles;
1251 }
1252
1253 if (strlen(path)==0 && !onlyFiles) return true; // base zip folder
1254
1255 if (!onlyFiles) {path[dirNameLen++]='/';path[dirNameLen]='\0';}
1256
1257 bool found = (unzLocateFile(im->uf,path,0)==UNZ_OK); // it becames the current file
1258
1259 //fprintf(stderr,"%s %s %s\n",pathInsideZip,path,found?"true":"false");
1260
1261 if (found && (onlyDirs || onlyFiles)) {
1262 unz_file_info64 file_info;
1263 char filename_inzip[2048]; // it's the filename without path
1264 int err = unzGetCurrentFileInfo64(im->uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
1265 if (err!=UNZ_OK) {
1266 fprintf(stderr,"Error while checking: \"%s\": %d in unzGetCurrentFileInfo\n",pathInsideZip,err);
1267 found = false;
1268 }
1269 else {
1270 size_t filename_inzip_len = strlen(filename_inzip);
1271 const bool hasZeroSize = file_info.compressed_size == 0 && file_info.uncompressed_size == 0;
1272 const bool isDirectory = filename_inzip_len>0 && (filename_inzip[filename_inzip_len-1]=='/' || filename_inzip[filename_inzip_len-1]=='\\');
1273 found = onlyFiles ? (!isDirectory && !hasZeroSize) : onlyDirs ? (isDirectory && hasZeroSize) : false;
1274 }
1275
1276 }
1277 unzCloseCurrentFile(im->uf);
1278
1279 return found;
1280}
1281bool UnZipFile::fileExists(const char* pathInsideZip) const {
1282 return exists(pathInsideZip,true,false);
1283}
1284bool UnZipFile::directoryExists(const char* pathInsideZip) const {
1285 return exists(pathInsideZip,false,true);
1286}
1287UnZipFile::UnZipFile(const char* zipFilePath) {
1288 im = (UnZipFileImpl*) ImGui::MemAlloc(sizeof(UnZipFileImpl));
1289 im->uf = NULL;im->zipFilePath[0]='\0';
1290 load(zipFilePath);
1291}
1292UnZipFile::~UnZipFile() {
1293 if (im) {
1294 close();
1295 ImGui::MemFree(im);
1296 im=NULL;
1297 }
1298}
1299
1300bool PathSplitFirstZipFolder(const char* path, char* rv1,char* rv2,bool rv1IsAbsolutePath) {
1301 return UnZipFileImpl::PathSplitFirstZipFolder(path,rv1,rv2,rv1IsAbsolutePath);
1302}
1303bool PathExistsWithZipSupport(const char* path, bool reportOnlyFiles, bool reportOnlyDirectories,bool checkAbsolutePath,bool* isInsideAZipFile) {
1304 return UnZipFileImpl::PathExistsWithZipSupport(path,reportOnlyFiles,reportOnlyDirectories,checkAbsolutePath,isInsideAZipFile);
1305}
1306bool PathIsInsideAZipFile(const char* path) {
1307 bool isInsideAZipFile = false;
1308 UnZipFileImpl::PathExistsWithZipSupport(path,true,true,true,&isInsideAZipFile);
1309 return isInsideAZipFile;
1310}
1311bool DirectoryGetDirectoriesWithZipSupport(const char* directoryName,PathStringVector& result,FilenameStringVector* pOptionalNamesOut,Sorting sorting,bool prefixResultWithTheFullPathOfTheZipFile) {
1312 result.clear();if (pOptionalNamesOut) pOptionalNamesOut->clear();
1313 char zipRoot[ImGuiFs::MAX_PATH_BYTES]="";
1314 char zipFolder[ImGuiFs::MAX_PATH_BYTES]="";
1315 if (PathSplitFirstZipFolder(directoryName,zipRoot,zipFolder)) {
1316 UnZipFile uzf(zipRoot);
1317 if (!uzf.isValid()) return false;
1318 return uzf.getDirectories(zipFolder,result,pOptionalNamesOut,sorting,prefixResultWithTheFullPathOfTheZipFile);
1319 }
1320 ImGuiFs::DirectoryGetDirectories(directoryName,result,pOptionalNamesOut,sorting);
1321 return true;
1322}
1323bool DirectoryGetFilesWithZipSupport(const char* directoryName,PathStringVector& result,FilenameStringVector* pOptionalNamesOut,Sorting sorting,bool prefixResultWithTheFullPathOfTheZipFile) {
1324 result.clear();if (pOptionalNamesOut) pOptionalNamesOut->clear();
1325 char zipRoot[ImGuiFs::MAX_PATH_BYTES]="";
1326 char zipFolder[ImGuiFs::MAX_PATH_BYTES]="";
1327 if (PathSplitFirstZipFolder(directoryName,zipRoot,zipFolder)) {
1328 UnZipFile uzf(zipRoot);
1329 if (!uzf.isValid()) return false;
1330 return uzf.getFiles(zipFolder,result,pOptionalNamesOut,sorting,prefixResultWithTheFullPathOfTheZipFile);
1331 }
1332 ImGuiFs::DirectoryGetFiles(directoryName,result,pOptionalNamesOut,sorting);
1333 return true;
1334}
1335void PathGetDirectoryNameWithZipSupport(const char* path,char* rv,bool prefixResultWithTheFullPathOfTheZipFile) {
1336 char zipRoot[ImGuiFs::MAX_PATH_BYTES]="";
1337 char zipRelativePath[ImGuiFs::MAX_PATH_BYTES]="";
1338 rv[0]='\0';
1339 if (PathSplitFirstZipFolder(path,zipRoot,zipRelativePath)) {
1340 char tmp[ImGuiFs::MAX_PATH_BYTES]="";
1341 ImGuiFs::PathGetDirectoryName(zipRelativePath,tmp);
1342 if (prefixResultWithTheFullPathOfTheZipFile) {
1343 strcpy(rv,zipRoot);
1344 if (strlen(tmp)>0 && tmp[0]!='/' && tmp[0]!='\\') strcat(rv,"/");
1345 }
1346 strcat(rv,tmp);
1347 }
1348 else ImGuiFs::PathGetDirectoryName(path,rv);
1349}
1350void PathGetAbsoluteWithZipSupport(const char* path,char* rv) {
1351 char zipRoot[ImGuiFs::MAX_PATH_BYTES]="";
1352 char zipRelativePath[ImGuiFs::MAX_PATH_BYTES]="";
1353 rv[0]='\0';
1354 if (PathSplitFirstZipFolder(path,zipRoot,zipRelativePath)) {
1355 char tmp[ImGuiFs::MAX_PATH_BYTES]="";
1356 ImGuiFs::PathGetAbsolute(zipRoot,tmp);
1357 strcpy(rv,tmp);
1358 if (strlen(zipRelativePath)>0 && zipRelativePath[0]!='/' && zipRelativePath[0]!='\\') strcat(rv,"/");
1359 strcat(rv,zipRelativePath);
1360 }
1361 else ImGuiFs::PathGetAbsolute(path,rv);
1362}
1363
1364#endif //IMGUI_USE_MINIZIP
1365template <typename CharType> bool FileGetContentBase(const char* path,ImVector<CharType>& bufferOut,bool openInTextMode,const char* password) {
1366 bufferOut.clear();
1367 char mainPath[MAX_PATH_BYTES];
1368# ifdef IMGUI_USE_MINIZIP
1369 char zipPath[MAX_PATH_BYTES];
1370 PathSplitFirstZipFolder(path,mainPath,zipPath,true);
1371 if (!FileExists(mainPath)) return false;
1372 if (strlen(zipPath)>0) {
1373 UnZipFile unz(mainPath);
1374 return unz.getFileContent(zipPath,bufferOut,password);
1375 }
1376# else //IMGUI_USE_MINIZIP
1377 strcpy(mainPath,path);
1378# endif //IMGUI_USE_MINIZIP
1379 FILE* fin = ImFileOpen(mainPath,openInTextMode?"rt":"rb");
1380 if (!fin) return false;
1381 fseek(fin,0,SEEK_END);
1382 const long szl = ftell(fin);
1383 const size_t sz = (size_t) szl;
1384 if ((sizeof(long)>sizeof(size_t) && szl!=(long)sz) || (sizeof(long)<sizeof(size_t) && (size_t)szl!=sz)) {
1385 fprintf(stderr,"Error in: FileGetContent(\"%s\"): file too big.\n",mainPath);
1386 fclose(fin);fin=NULL;
1387 return false;
1388 }
1389 fseek(fin,0,SEEK_SET);
1390 if (openInTextMode) {bufferOut.resize(sz+1);bufferOut[sz]='\0';}
1391 else bufferOut.resize(sz);
1392 if (sz>0) {
1393 if (bufferOut.size()>=(int)sz) fread(&bufferOut[0],(size_t)sz,1,fin);
1394 else {
1395 fprintf(stderr,"Error in: FileGetContent(\"%s\"): file too big.\n",mainPath);
1396 bufferOut.clear();
1397 fclose(fin);fin=NULL;
1398 return false;
1399 }
1400 }
1401 fclose(fin);fin=NULL;
1402 return true;
1403}
1404bool FileGetContent(const char* path,ImVector<unsigned char>& bufferOut,bool openInTextMode,const char* password) {return FileGetContentBase<unsigned char>(path,bufferOut,openInTextMode,password);}
1405bool FileGetContent(const char* path,ImVector<char>& bufferOut,bool openInTextMode,const char* password) {return FileGetContentBase<char>(path,bufferOut,openInTextMode,password);}
1406#endif // IMGUIFS_NO_EXTRA_METHODS
1407// End definitions of some helper classes----------------------------------------------------------------------------------------
1408
1409// Internal Usage----------------------------------------------------------------------------------------
1411 ImVector<char> v;
1412 ImVector<int> vStarters; // size = number of strings in v
1413 ImVector<int> vStartersLengths; // same size as vStarters
1414 ImVector<int> vStartersCounts; // same size as vStarters (map to ICON_FA_XXX)
1415 int add(const char* exts) {
1416 IM_ASSERT(exts && strlen(exts)>1);
1417 int cnt = 0;
1418 const int vStartersCountValue = vStartersCounts.size()>0 ? (vStartersCounts[vStartersCounts.size()-1]+1) : 0;
1419 static char tmp[512];
1420 IM_ASSERT(strlen(exts)<511);
1421 strcpy(tmp,exts);
1422 char* token = strtok(tmp,";");
1423 int sz = 0,vSz=0;
1424 while(token) {
1425 sz=strlen(token);
1426 vSz=v.size();
1427 vStarters.push_back(vSz);
1428 vStartersLengths.push_back(sz);
1429 vStartersCounts.push_back(vStartersCountValue);
1430 v.resize(vSz+sz+1);
1431 strcpy(&v[vSz],token);
1432
1433 token = strtok(NULL,";");
1434 ++cnt;
1435 }
1436 IM_ASSERT(cnt>0);
1437
1438 return cnt;
1439 }
1441 v.reserve(1024);
1442 vStarters.reserve(400);vStartersLengths.reserve(400);
1443 vStartersCounts.reserve(400);
1444
1445 add("bin"); // ICON_FA_FILE_O
1446 add("h;hpp;hh;hxx;inl"); // ICON_FA_H_SQUARE
1447 add("cpp;c;cxx;cc"); // ICON_FA_PLUS_SQUARE
1448 add("jpg;jpeg;png;bmp;ico;gif;tif;tiff;tga"); // ICON_FA_FILE_IMAGE_O
1449 add("pdf"); // ICON_FA_FILE_PDF_O
1450 add("doc;docx;odt;ott;uot"); // ICON_FA_FILE_WORD_O
1451 add("txt;setting;settings;layout;ini;md;sh;bat");// ICON_FA_FILE_TEXT_O
1452 add("db;sql;sqlite"); // ICON_FA_DATABASE
1453 add("ods;ots;uos;xlsx;xls"); // ICON_FA_FILE_EXCEL_O
1454 add("odp;otp;uop;pptx;ppt"); // ICON_FA_FILE_POWERPOINT_O
1455 add("7z;zip;bz2;gz;lz;lzma;ar;rar"); // ICON_FA_FILE_ARCHIVE_O
1456 add("mp3;wav;ogg;spx;opus;mid;mod;flac"); // ICON_FA_FILE_AUDIO_O
1457 add("mp4;flv;avi;ogv;theora;mkv;webm;mpg"); // ICON_FA_FILE_VIDEO_O
1458 add("xml"); // ICON_FA_FILE_CODE_O
1459 add("htm;html"); // ICON_FA_FILE_CODE_O
1460
1461 }
1462 int getExtensionType(const char* ext,bool caseSensitiveMatch=false) const {
1463 if (!ext) return -1;
1464 if (ext[0]=='.') ext+=1;
1465 const int extLen = strlen(ext);
1466 if (extLen==0) return -1;
1467 typedef int (*strcmpdelegate) (const char*, const char*);
1468 const strcmpdelegate myStrCmp = caseSensitiveMatch ? &strcmp : &strcasecmp;
1469 int start=0,startLen=0;
1470 for (int si = 0,siSz=vStarters.size();si<siSz;si++) {
1471 startLen = vStartersLengths[si];
1472 if (extLen!=startLen) continue;
1473 start = vStarters[si];
1474 if (myStrCmp(&v[start],ext)==0) return vStartersCounts[si];
1475 }
1476 return -1;
1477 }
1478 inline void fillExtensionTypesFromFilenames(ImVector<int>& fileExtensionTypes,const FilenameStringVector& fileNames) {
1479 fileExtensionTypes.resize(fileNames.size());
1480 for (int i=0,isz=fileNames.size();i<isz;i++) fileExtensionTypes[i] = getExtensionType(strrchr(fileNames[i],'.'));
1481 }
1482
1483 inline bool drawIcon(int extensionType,const ImVec4* pOptionalColorOverride=NULL) const {
1484 return ImGuiFs::Dialog::DrawFileIconCallback ? ImGuiFs::Dialog::DrawFileIconCallback(extensionType,pOptionalColorOverride) : false;
1485 }
1486 inline bool drawIcon(const char* ext,bool caseSensitiveMatch=false,const ImVec4* pOptionalColorOverride=NULL) const {
1487 const int extType = getExtensionType(ext,caseSensitiveMatch);
1488 return drawIcon(extType,pOptionalColorOverride);
1489 }
1490
1491 // New to avoid global static initialization: see https://github.com/Flix01/imgui/issues/38
1492 static ImGuiFsDrawIconStruct& Get() {static ImGuiFsDrawIconStruct instance;return instance;}
1493};
1494
1495#ifndef IMGUIFS_NO_EXTRA_METHODS
1496int FileGetExtensionType(const char* path) {
1497 return ImGuiFsDrawIconStruct::Get().getExtensionType(strrchr(path,'.'));
1498}
1499void FileGetExtensionTypesFromFilenames(ImVector<int>& fileExtensionTypesOut,const FilenameStringVector& fileNames) {
1500 ImGuiFsDrawIconStruct::Get().fillExtensionTypesFromFilenames(fileExtensionTypesOut,fileNames);
1501}
1502#if (defined(__EMSCRIPTEN__) && defined(EMSCRIPTEN_SAVE_SHELL))
1503bool FileDownload(const char* path,const char* optionalSaveFileName) {
1504 if (!path || !FileExists(path)) return false;
1505 ImGuiTextBuffer buffer;
1506 if (optionalSaveFileName) buffer.appendf("saveFileFromMemoryFSToDisk(\"%s\",\"%s\")",path,optionalSaveFileName);
1507 else {
1508 char fileName[ImGuiFs::MAX_FILENAME_BYTES]="";
1509 ImGuiFs::PathGetFileName(path,fileName);
1510 buffer.appendf("saveFileFromMemoryFSToDisk(\"%s\",\"%s\")",path,fileName);
1511 }
1512 emscripten_run_script(&buffer.Buf[0]);
1513 return true;
1514}
1515#endif // (defined(__EMSCRIPTEN__) && defined(EMSCRIPTEN_SAVE_SHELL))
1516#endif // IMGUIFS_NO_EXTRA_METHODS
1517//-------------------------------------------------------------------------------------------------------
1518
1519// Internal usage----------------------------------------------------------------------------------------
1524 static FilenameStringVector SplitPath; // tmp field used internally
1526
1527 void display() const {
1528 fprintf(stderr,"fullFolder=\"%s\" currentFolder=\"%s\" splitPathIndex=%d splitPathIndexOfZipFile=%d\n",fullFolder,currentFolder,splitPathIndex,splitPathIndexOfZipFile);
1529 }
1530 void getSplitPath(FilenameStringVector& splitPath) const {
1531 Path::Split(fullFolder,splitPath);
1532 }
1534 strcpy(currentFolder,o.currentFolder);
1535 strcpy(fullFolder,o.fullFolder);
1538 return *this;
1539 }
1540 inline void reset() {
1542 }
1544 FolderInfo(const FolderInfo& o) {*this=o;}
1545
1546 void fromCurrentFolder(const char* path) {
1547 if (!path || strlen(path)==0) reset();
1548 else {
1549 strcpy(currentFolder,path);
1550 strcpy(fullFolder,path);
1552 splitPathIndex = (int) SplitPath.size()-1;
1554 }
1555 }
1557 int splitPathIndexOfZipFile = -1;
1558# ifdef IMGUI_USE_MINIZIP
1559 const char* lower = ".zip";const char* upper = ".ZIP";const int numCharsToMath = (int)strlen(lower);int gc=0;
1560 for (int j=0,jsz=(int) SplitPath.size();j<jsz;j++) {
1561 const char* path = SplitPath[j];
1562 //fprintf(stderr,"%d) %s\n",j,path);
1563 const int sz = (int) strlen(path);
1564 const int startCharToMatch = (j==jsz-1) ? numCharsToMath : (numCharsToMath+1);
1565 if (sz<=startCharToMatch) continue;
1566 const int i = sz-startCharToMatch;
1567
1568 gc=0;
1569 char c = path[i];
1570 while (c==lower[gc] || c==upper[gc]) {
1571 //fprintf(stderr,"match: %c\n",c);
1572 if (++gc==numCharsToMath) {
1574 //fprintf(stderr,"splitPathIndexOfZipFile=%d (%s)\n",splitPathIndexOfZipFile,path);
1575 return splitPathIndexOfZipFile;// Warning: I should double break here!
1576 }
1577 c = path[i+gc];
1578 }
1579
1580 }
1581# endif //IMGUI_USE_MINIZIP
1583 }
1584 bool isEqual(const FolderInfo& fi) const {
1585 return strcmp(fullFolder,fi.fullFolder)==0 && strcmp(currentFolder,fi.currentFolder)==0;
1586 }
1587 bool isEqual(const char* path) const {
1588 return strcmp(fullFolder,path)==0 && strcmp(currentFolder,path)==0;
1589 }
1590 int getSplitPathIndexFor(const char* path/*,int* pOptionalSplitPathIndexOfZipFile=NULL*/) const {
1591 if (!path || strncmp(path,fullFolder,strlen(path))!=0) return -1;
1592
1593 int spi = -1;
1595 char tmp[MAX_PATH_BYTES];tmp[0]='\0';
1596 for (int i=0,sz=(int)SplitPath.size();i<sz;i++) {
1597 Path::Append(SplitPath[i],tmp);
1598 //fprintf(stderr,"%d) \"%s\" <-> \"%s\"\n",i,tmp,path);
1599 if (strcmp(tmp,path)==0) {spi=i;break;}
1600 }
1601 //if (pOptionalSplitPathIndexOfZipFile) *pOptionalSplitPathIndexOfZipFile = GetSplitPathIndexOfZipFile(SplitPath);
1602
1603 return spi;
1604 }
1605 bool getFolderInfoForSplitPathIndex(int _splitPathIndex,FolderInfo& rv) const {
1607 const int splitPathSize = (int)SplitPath.size();
1608 if (_splitPathIndex<0 || _splitPathIndex>=splitPathSize) return false;
1609 rv = *this;
1610 rv.splitPathIndex = _splitPathIndex;
1612
1613 rv.currentFolder[0]='\0';
1614 if (_splitPathIndex>=0 && _splitPathIndex<splitPathSize) {
1615 for (int i=0;i<=_splitPathIndex;i++) {
1617 //fprintf(stderr,"%d) \"%s\" (\"%s\")\n",i,rv.currentFolder,SplitPath[i]);
1618 }
1619 }
1620 /*fprintf(stderr,"getFolderInfoForSplitPathIndex(%d):\nSource: ",_splitPathIndex);
1621 this->display();
1622 fprintf(stderr,"Result: ");
1623 rv.display();*/
1624 return true;
1625 }
1626};
1627FilenameStringVector FolderInfo::SplitPath; // tmp field used internally
1628struct History {
1629protected:
1630 ImVector<FolderInfo> info;
1631 int currentInfoIndex; // into info
1632public:
1633 inline bool canGoBack() {
1634 return currentInfoIndex>0;
1635 }
1636 inline bool canGoForward() {
1637 return currentInfoIndex>=0 && currentInfoIndex<(int)info.size()-1;
1638 }
1639 void reset() {info.clear();currentInfoIndex=-1;}
1641 ~History() {info.clear();}
1642 // -------------------------------------------------------------------------------------------------
1643 void goBack() {
1644 if (canGoBack()) --currentInfoIndex;
1645 }
1646 void goForward() {
1648 }
1649 bool switchTo(const char* currentFolder) {
1650 if (!currentFolder || strlen(currentFolder)==0) return false;
1651 if (currentInfoIndex<0) {
1653 info.resize(currentInfoIndex+1);
1655 fi.fromCurrentFolder(currentFolder);
1656 //fprintf(stderr,"switchTo 1 %d\n",fi.splitPathIndexOfZipFile);
1657 return true;
1658 }
1659 else {
1660 const FolderInfo& lastInfo = info[currentInfoIndex];
1661 if (lastInfo.isEqual(currentFolder)) return false;
1662 const int splitPathIndexInsideLastInfo = lastInfo.getSplitPathIndexFor(currentFolder);
1664 info.resize(currentInfoIndex+1);
1666 if (splitPathIndexInsideLastInfo==-1) {
1667 fi.fromCurrentFolder(currentFolder);
1668 //fprintf(stderr,"switchTo 2a: %d (%s)\n",fi.splitPathIndexOfZipFile,currentFolder);
1669 }
1670 else {
1671 fi = lastInfo;
1672 fi.splitPathIndex = splitPathIndexInsideLastInfo;
1673 strcpy(fi.currentFolder,currentFolder);
1674
1675 //fprintf(stderr,"switchTo 2b: %d\n",fi.splitPathIndexOfZipFile);
1676 }
1677 return true;
1678 }
1679 }
1680 bool switchTo(const FolderInfo& fi) {
1681 if ( strlen(fi.currentFolder)==0) return false;
1682 if (currentInfoIndex>=0) {
1683 const FolderInfo& lastInfo = info[currentInfoIndex];
1684 if (lastInfo.isEqual(fi)) return false;
1685 }
1687 info.resize(currentInfoIndex+1);
1688 info[currentInfoIndex] = fi;
1689 //fprintf(stderr,"switchTo 3 %d\n",fi.splitPathIndexOfZipFile);
1690 return true;
1691 }
1692 //-----------------------------------------------------------------------------------------------------
1693
1694 inline bool isValid() const {return (currentInfoIndex>=0 && currentInfoIndex<(int)info.size());}
1695 const FolderInfo* getCurrentFolderInfo() const {return isValid() ? &info[currentInfoIndex] : NULL;}
1696 const char* getCurrentFolder() const {return isValid() ? &info[currentInfoIndex].currentFolder[0] : NULL;}
1698 if (isValid()) {
1699 info[currentInfoIndex].getSplitPath(rv);
1700 return true;
1701 }
1702 else return false;
1703 }
1704 const int* getCurrentSplitPathIndex() const {return isValid() ? &info[currentInfoIndex].splitPathIndex : NULL;}
1705 size_t getInfoSize() const {return info.size();}
1706};
1707
1708struct Internal {
1711 ImVector<int> fileExtensionTypes;
1714 bool open;
1715 ImVec2 wndPos;
1716 ImVec2 wndSize;
1720
1722# ifdef IMGUI_USE_MINIZIP
1723 UnZipFile unz;
1724# endif //IMGUI_USE_MINIZIP
1725 //-----------------------------------------------------
1728
1733 //----------------------------------------------------
1734
1738
1739 ImGuiTextFilter filter;
1741
1745 static bool BrowsingPerRow;
1747
1750
1754
1755
1757 dirs.clear();files.clear();
1758 dirNames.clear();fileNames.clear();
1759 currentSplitPath.clear();
1760 }
1761
1762 inline static void FreeMemory(PathStringVector& v) {PathStringVector o;v.swap(o);}
1763# if FILENAME_MAX!=DIRENT_MAX_PATH // otherwise PathStringVector == FilenameStringVector
1764 inline static void FreeMemory(FilenameStringVector& v) {FilenameStringVector o;v.swap(o);}
1765# endif //FILENAME_MAX!=DIRENT_MAX_PATH
1766 void freeMemory() {
1769 FreeMemory(FolderInfo::SplitPath); // Not too sure about this...
1770 }
1771
1773 strcpy(currentFolder,"./");
1774 forceRescan = false;
1775 open = true;
1776
1777 wndTitle[0] = '\0';
1778 sortingMode = 0;
1779
1780 history.reset();
1781
1782 isSelectFolderDialog = false;
1783 isSaveFileDialog = false;
1784
1787 strcpy(newDirectoryName,"New Folder");
1788 saveFileName[0] = '\0';
1789
1790 uniqueNumber = 0;
1791
1792 rescan = true;
1793 chosenPath[0] = '\0';
1794
1795 filter.Clear();
1796 allowFiltering = false;
1797
1801
1803 allowDisplayByOption = false;
1805
1807
1809 strcpy(editLocationInputText,"\0");
1810
1813
1814# ifdef IMGUI_USE_MINIZIP
1815 unz.close();
1816# endif //IMGUI_USE_MINIZIP
1817 }
1818
1819 inline void calculateBrowsingDataTableSizes(const ImVec2& childWindowSize=ImVec2(-1,-1)) {
1820 int approxNumEntriesPerColumn = 20;//(int) (20.f / browseSectionFontScale);// tweakable
1821 if (childWindowSize.y>0) {
1822 int numLinesThatFit = childWindowSize.y/ImGui::GetTextLineHeightWithSpacing();
1823 if (numLinesThatFit<=0) numLinesThatFit=1;
1824 approxNumEntriesPerColumn = numLinesThatFit;
1825 //static int tmp = 0;if (tmp!=numLinesThatFit) {tmp=numLinesThatFit;fprintf(stderr,"childWindowSize.y = %f numLinesThatFit=%d\n",childWindowSize.y,numLinesThatFit);}
1826 }
1827 numBrowsingColumns = totalNumBrowsingEntries/approxNumEntriesPerColumn;
1828 if (numBrowsingColumns<=0) {
1829 numBrowsingColumns = 1;numBrowsingEntriesPerColumn = approxNumEntriesPerColumn;
1830 return;
1831 }
1832 if (totalNumBrowsingEntries%approxNumEntriesPerColumn>(approxNumEntriesPerColumn/2)) ++numBrowsingColumns;
1833 int maxNumBrowsingColumns = (childWindowSize.x>0) ? (childWindowSize.x/100) : 6;
1834 if (maxNumBrowsingColumns<1) maxNumBrowsingColumns=1;
1835 if (numBrowsingColumns>maxNumBrowsingColumns) numBrowsingColumns = maxNumBrowsingColumns;
1838 }
1839
1840 // Just a convenience enum used internally
1841 enum Color {
1846
1851
1853
1858
1860
1862 inline static void ColorCombine(ImVec4& c,const ImVec4& r,const ImVec4& factor) {
1863 const float rr = (r.x+r.y+r.z)*0.3334f;
1864 c.x = rr * factor.x;c.y = rr * factor.y;c.z = rr * factor.z;c.w = r.w;
1865 }
1866};
1867bool Internal::BrowsingPerRow = false;
1868// End Internal Usage-------------------------------------------------------------------------------------
1869
1870bool Dialog::WrapMode = true;
1871ImVec2 Dialog::WindowSize(600,400);
1872ImVec4 Dialog::WindowLTRBOffsets(0,0,0,0);
1873ImGuiWindowFlags Dialog::ExtraWindowFlags = 0;
1876
1877
1878Dialog::Dialog(bool noKnownDirectoriesSection,bool noCreateDirectorySection,bool noFilteringSection,bool detectKnownDirectoriesAtEachOpening,bool addDisplayByOption,bool dontFilterSaveFilePathsEnteredByTheUser) {
1879 internal = (Internal*) ImGui::MemAlloc(sizeof(Internal));
1880 IM_PLACEMENT_NEW(internal) Internal();
1881
1883 static int un = 0;
1884 internal->uniqueNumber = un++;
1885
1886 internal->detectKnownDirectoriesAtEveryOpening = detectKnownDirectoriesAtEachOpening;
1887 internal->allowDisplayByOption = addDisplayByOption;
1888 internal->forbidDirectoryCreation = noCreateDirectorySection;
1889 internal->allowKnownDirectoriesSection = !noKnownDirectoriesSection;
1890 internal->allowFiltering = !noFilteringSection;
1891 internal->mustFilterSaveFilePathWithFileFilterExtensionString = !dontFilterSaveFilePathsEnteredByTheUser;
1892}
1894 if (internal) {
1896 ImGui::MemFree(internal);
1897 internal = NULL;
1898 }
1899}
1900const char* Dialog::getChosenPath() const {return internal->chosenPath;}
1903
1904// -- from imgui.cpp --
1905static size_t ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1906{
1907 va_list args;
1908 va_start(args, fmt);
1909 int w = vsnprintf(buf, buf_size, fmt, args);
1910 va_end(args);
1911 buf[buf_size-1] = 0;
1912 return (w == -1) ? buf_size : (size_t)w;
1913}
1914// ---------------------
1915
1916// 90% of the functionality of the whole imguifilesystem.cpp is inside this single method
1917const 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) {
1918 //-----------------------------------------------------------------------------
1919 Internal& I = *ist.internal;
1920 char* rv = I.chosenPath;rv[0] = '\0';
1921 //-----------------------------------------------------
1922 bool& isSelectFolderDialog = I.isSelectFolderDialog = _isFolderChooserDialog;
1923 bool& isSaveFileDialog = I.isSaveFileDialog = _isSaveFileDialog;
1924
1925 bool& allowDirectoryCreation = I.allowDirectoryCreation = I.forbidDirectoryCreation ? false : (isSelectFolderDialog || isSaveFileDialog);
1926 //----------------------------------------------------------
1927 static const int* pNumberKnownUserDirectoriesExceptDrives=NULL;
1928 static const FilenameStringVector* pUserKnownDirectoryDisplayNames=NULL;
1929 static const PathStringVector* pUserKnownDirectories = &Directory::GetUserKnownDirectories(&pUserKnownDirectoryDisplayNames,&pNumberKnownUserDirectoriesExceptDrives);
1930 //----------------------------------------------------------
1931 const ImGuiStyle& style = ImGui::GetStyle();
1932 ImVec4 dummyButtonColor(0.0f,0.0f,0.0f,0.5f); // Only the alpha is twickable from here
1933 ImVec4 dummyZipButtonColor(0.0f,0.0f,0.0f,0.5f); // Only the alpha is twickable from here
1934 static ImVec4 ColorSet[Internal::ImGuiCol_Dialog_Size];
1935 // Fill ColorSet above and fix dummyButtonColor here
1936 {
1937 static const ImVec4 df(0.9,0.9,0.3,0.65); // directory color factor
1938 static const ImVec4 ff(0.7,0.7,0.7,0.65); // file color factor
1939 static const ImVec4 zdf(1.5,0.8,0.8,0.65); // zip directory color factor
1940
1941 for (int i=0,sz=(int)Internal::ImGuiCol_Dialog_Directory_Text;i<=sz;i++) {
1942 ImVec4& c = ColorSet[i];
1943 const ImVec4& r = style.Colors[i<sz ? ((int)ImGuiCol_Button + i) : ImGuiCol_Text];
1944 Internal::ColorCombine(c,r,df);
1945 if (i<sz) c.w *= df.w;
1946 }
1948 ImVec4& c = ColorSet[i];
1949 const ImVec4& r = style.Colors[i<sz ? ((int)ImGuiCol_Button-(int)Internal::ImGuiCol_Dialog_File_Background + i) : ImGuiCol_Text];
1950 Internal::ColorCombine(c,r,ff);
1951 if (i<sz) c.w *= ff.w;
1952 }
1954 ImVec4& c = ColorSet[i];
1955 const ImVec4& r = style.Colors[i<sz ? ((int)ImGuiCol_Button + i) : ImGuiCol_Text];
1956 Internal::ColorCombine(c,r,zdf);
1957 if (c.x>1.f) c.x=1.f;
1958 if (c.y>1.f) c.y=1.f;
1959 if (c.z>1.f) c.z=1.f;
1960 if (c.w>1.f) c.w=1.f;
1961 if (i<sz) c.w *= zdf.w;
1962 }
1963 if (dummyButtonColor.w>0) {
1964 const ImVec4& bbc = style.Colors[ImGuiCol_Button];
1965 dummyButtonColor.x = bbc.x;dummyButtonColor.y = bbc.y;dummyButtonColor.z = bbc.z;dummyButtonColor.w *= bbc.w;
1966 }
1967 if (dummyZipButtonColor.w>0) {
1968 const ImVec4& bbc = ColorSet[Internal::ImGuiCol_Dialog_ZipDirectory_Background];
1969 dummyZipButtonColor.x = bbc.x;dummyZipButtonColor.y = bbc.y;dummyZipButtonColor.z = bbc.z;dummyZipButtonColor.w *= bbc.w;
1970 }
1971 }
1972
1973 if (I.rescan) {
1974 char validDirectory[MAX_PATH_BYTES];validDirectory[0]='\0'; // for robustness
1975# ifndef IMGUI_USE_MINIZIP
1976 if (directory && strlen(directory)>0) {
1977 if (Directory::Exists(directory)) strcpy(validDirectory,directory);
1978 else {
1979 Path::GetDirectoryName(directory,validDirectory);
1980 if (!Directory::Exists(validDirectory)) validDirectory[0]='\0';
1981 }
1982 }
1983 Path::GetAbsolutePath(validDirectory,I.currentFolder);
1984# else //IMGUI_USE_MINIZIP
1985 char basePath[MAX_PATH_BYTES];basePath[0]='\0';char zipPath[MAX_PATH_BYTES];zipPath[0]='\0';
1986 bool isInsideZipFile = false;
1987 if (directory && strlen(directory)>0) isInsideZipFile = PathSplitFirstZipFolder(directory,basePath,zipPath);
1988 else isInsideZipFile = PathSplitFirstZipFolder(I.currentFolder,basePath,zipPath);
1989 if (isInsideZipFile ? FileExists(basePath) : Directory::Exists(basePath)) {
1990 strcpy(validDirectory,basePath);
1991 if (zipPath[0]!='\0') {
1992 char zipPathDir[MAX_PATH_BYTES];zipPathDir[0]='\0';
1993 Path::GetDirectoryName(zipPath,zipPathDir);
1994 Path::Append(zipPathDir,validDirectory);
1995 }
1996 //fprintf(stderr,"basePath=%s zipPath=%s validDirectory=%s\n",basePath,zipPath,validDirectory);
1997 }
1998 else {
1999 Path::GetDirectoryName(directory,validDirectory);
2000 if (!Directory::Exists(validDirectory)) validDirectory[0]='\0';
2001 }
2002 strcpy(I.currentFolder,validDirectory);
2003 //fprintf(stderr,"%s %d\n",I.currentFolder,isInsideZipFile);
2004# endif //IMGUI_USE_MINIZIP
2005
2007 I.history.reset(); // reset history
2008 I.history.switchTo(I.currentFolder); // init history
2009 I.dirs.clear();I.files.clear();I.dirNames.clear();I.fileNames.clear();I.currentSplitPath.clear();I.fileExtensionTypes.clear();
2010 strcpy(&I.newDirectoryName[0],"New Folder");
2011 if (_saveFileName) {
2012 //strcpy(&I.saveFileName[0],_saveFileName);
2013 Path::GetFileName(_saveFileName,I.saveFileName); // Better!
2014 }
2015 else I.saveFileName[0]='\0';
2016 isSelectFolderDialog = _isFolderChooserDialog;
2017 isSaveFileDialog = _isSaveFileDialog;
2018 allowDirectoryCreation = I.forbidDirectoryCreation ? false : (isSelectFolderDialog || isSaveFileDialog);
2019 if (isSelectFolderDialog && I.sortingMode>SORT_ORDER_LAST_MODIFICATION_INVERSE) I.sortingMode = 0;
2020 I.forceRescan = true;
2021 I.open = true;
2022 I.filter.Clear();
2023 if (!windowTitle || strlen(windowTitle)==0) {
2024 if (isSelectFolderDialog) strcpy(I.wndTitle,"Please select a folder");
2025 else if (isSaveFileDialog) strcpy(I.wndTitle,"Please choose/create a file for saving");
2026 else strcpy(I.wndTitle,"Please choose a file");
2027 }
2028 else strcpy(I.wndTitle,windowTitle);
2029 strcat(I.wndTitle,"##");
2030 char tmpWndTitleNumber[12];
2031 ImFormatString(tmpWndTitleNumber,11,"%d",I.uniqueNumber);
2032 strcat(I.wndTitle,tmpWndTitleNumber);
2033 I.wndPos = windowPos;
2034 I.wndSize = windowSize;
2035 if (I.wndSize.x<=0) I.wndSize.x = Dialog::WindowSize.x;
2036 if (I.wndSize.y<=0) I.wndSize.y = Dialog::WindowSize.y;
2037 const ImVec2 mousePos = ImGui::GetMousePos();//
2038 ImGui::GetCursorPos();
2039 if (I.wndPos.x<=0) I.wndPos.x = mousePos.x - I.wndSize.x*0.5f;
2040 if (I.wndPos.y<=0) I.wndPos.y = mousePos.y - I.wndSize.y*0.5f;
2041 const ImVec2 screenSize = ImGui::GetIO().DisplaySize;
2042 if (I.wndPos.x>screenSize.x-I.wndSize.x-Dialog::WindowLTRBOffsets.z) I.wndPos.x = screenSize.x-I.wndSize.x-Dialog::WindowLTRBOffsets.z;
2043 if (I.wndPos.y>screenSize.y-I.wndSize.y-Dialog::WindowLTRBOffsets.w) I.wndPos.y = screenSize.y-I.wndSize.y-Dialog::WindowLTRBOffsets.w;
2046 //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);
2047 if (I.detectKnownDirectoriesAtEveryOpening) pUserKnownDirectories = &Directory::GetUserKnownDirectories(&pUserKnownDirectoryDisplayNames,&pNumberKnownUserDirectoriesExceptDrives,true);
2048
2049 //fprintf(stderr,"Pos: %1.0f %1.0f Size: %1.0f %1.0f\n",I.wndPos.x,I.wndPos.y,I.wndSize.x,I.wndSize.y);
2050 }
2051 if (!I.open) {
2052 // This case (user clicks the close button) does not seem to be triggered here...
2053 // No matter, I just handle it in the 3 caller methods, where it's triggered.
2054 return rv;
2055 }
2056
2057
2058 if (I.forceRescan) {
2059 I.forceRescan = false;
2060# ifndef IMGUI_USE_MINIZIP
2061 const int sortingModeForDirectories = (I.sortingMode <= (int)SORT_ORDER_LAST_MODIFICATION_INVERSE) ? I.sortingMode : (I.sortingMode%2);
2062 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)
2063
2064 if (!isSelectFolderDialog) {
2065 if (!fileFilterExtensionString || strlen(fileFilterExtensionString)==0) Directory::GetFiles(I.currentFolder,I.files,&I.fileNames,(Sorting)I.sortingMode);
2066 else Directory::GetFiles(I.currentFolder,I.files,fileFilterExtensionString,NULL,&I.fileNames,(Sorting)I.sortingMode);
2068 }
2069 else {
2070 I.files.clear();I.fileNames.clear();I.fileExtensionTypes.clear();
2071 I.saveFileName[0]='\0';
2072 char currentFolderName[MAX_FILENAME_BYTES];
2073 Path::GetFileName(I.currentFolder,currentFolderName);
2074 const size_t currentFolderNameSize = strlen(currentFolderName);
2075 if (currentFolderNameSize==0 || currentFolderName[currentFolderNameSize-1]==':') strcat(currentFolderName,"/");
2076 strcat(I.saveFileName,currentFolderName);
2077 }
2078# else //IMGUI_USE_MINIZIP
2079 const int sortingModeForDirectories = (I.sortingMode <= (int)SORT_ORDER_LAST_MODIFICATION_INVERSE) ? I.sortingMode : (I.sortingMode%2);
2080 char basePath[MAX_PATH_BYTES];basePath[0]='\0';char zipPath[MAX_PATH_BYTES];zipPath[0]='\0';
2081 const bool isInsideZipFile = PathSplitFirstZipFolder(I.currentFolder,basePath,zipPath);
2082 if (!isInsideZipFile) Directory::GetDirectories(basePath,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)
2083 else {
2084 if (I.unz.load(basePath,false)) I.unz.getDirectories(zipPath,I.dirs,&I.dirNames,(Sorting)sortingModeForDirectories,true);
2085 else I.unz.close();
2086 }
2087
2088 if (!isSelectFolderDialog) {
2089 if (!isInsideZipFile) {
2090 if (!fileFilterExtensionString || strlen(fileFilterExtensionString)==0) Directory::GetFiles(I.currentFolder,I.files,&I.fileNames,(Sorting)I.sortingMode);
2091 else Directory::GetFiles(I.currentFolder,I.files,fileFilterExtensionString,NULL,&I.fileNames,(Sorting)I.sortingMode);
2093 }
2094 else if (I.unz.isValid()) {
2095 /*if (!fileFilterExtensionString || strlen(fileFilterExtensionString)==0)*/ I.unz.getFiles(zipPath,I.files,&I.fileNames,(Sorting)I.sortingMode,true);
2096 //else I.unz.getFiles(zipPath,I.files,fileFilterExtensionString,NULL,&I.fileNames,(Sorting)I.sortingMode,true);
2097 }
2098 }
2099 else {
2100 I.files.clear();I.fileNames.clear();
2101 I.saveFileName[0]='\0';
2102 char currentFolderName[MAX_FILENAME_BYTES];
2103 Path::GetFileName(I.currentFolder,currentFolderName);
2104 const size_t currentFolderNameSize = strlen(currentFolderName);
2105 if (currentFolderNameSize==0 || currentFolderName[currentFolderNameSize-1]==':') strcat(currentFolderName,"/");
2106 strcat(I.saveFileName,currentFolderName);
2107 }
2108# endif //IMGUI_USE_MINIZIP
2109
2111 I.totalNumBrowsingEntries = (int)(I.dirs.size()+I.files.size());
2112
2113 //I.calculateBrowsingDataTableSizes(); // can we move it down ?
2114
2115 //# define DEBUG_HISTORY
2116# ifdef DEBUG_HISTORY
2117 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");
2119# endif //DEBUG_HISTORY
2120 }
2121
2122 if (I.rescan) {
2123 I.rescan = false; // Mandatory
2124 ImGui::OpenPopup(I.wndTitle);
2125 ImGui::SetNextWindowPos(I.wndPos);ImGui::SetNextWindowSize(I.wndSize);
2126 const bool popupOk = ImGui::BeginPopupModal(I.wndTitle, &I.open,Dialog::ExtraWindowFlags);
2127 if (!popupOk) return rv;
2128 }
2129 else {
2132 ImGui::SetNextWindowPos(I.wndPos);
2133 ImGui::SetNextWindowSize(I.wndSize);
2134 }
2135 const bool popupOk = ImGui::BeginPopupModal(I.wndTitle, &I.open,Dialog::ExtraWindowFlags);
2136 if (!popupOk) return rv;
2137 }
2138 ImGui::Separator();
2139
2140 //------------------------------------------------------------------------------------
2141 // History (=buttons: < and >)
2142 {
2143 bool historyBackClicked = false;
2144 bool historyForwardClicked = false;
2145
2146 // history -----------------------------------------------
2147 ImGui::PushID("historyDirectoriesID");
2148
2149 const bool historyCanGoBack = I.history.canGoBack();
2150 const bool historyCanGoForward = I.history.canGoForward();
2151
2152 if (!historyCanGoBack) {
2153 ImGui::PushStyleColor(ImGuiCol_Button,dummyButtonColor);
2154 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,dummyButtonColor);
2155 ImGui::PushStyleColor(ImGuiCol_ButtonActive,dummyButtonColor);
2156 }
2157 historyBackClicked = ImGui::Button("<")&historyCanGoBack;
2158 ImGui::SameLine();
2159 if (!historyCanGoBack) {
2160 ImGui::PopStyleColor();
2161 ImGui::PopStyleColor();
2162 ImGui::PopStyleColor();
2163 }
2164
2165 if (!historyCanGoForward) {
2166 ImGui::PushStyleColor(ImGuiCol_Button,dummyButtonColor);
2167 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,dummyButtonColor);
2168 ImGui::PushStyleColor(ImGuiCol_ButtonActive,dummyButtonColor);
2169 }
2170 historyForwardClicked = ImGui::Button(">")&historyCanGoForward;
2171 ImGui::SameLine();
2172 if (!historyCanGoForward) {
2173 ImGui::PopStyleColor();
2174 ImGui::PopStyleColor();
2175 ImGui::PopStyleColor();
2176 }
2177
2178 ImGui::PopID();
2179 // -------------------------------------------------------
2180
2181 if (historyBackClicked || historyForwardClicked) {
2182 ImGui::EndPopup();
2183
2184 if (historyBackClicked) I.history.goBack();
2185 else if (historyForwardClicked) I.history.goForward();
2186
2187 I.forceRescan = true;
2188
2191
2192# ifdef DEBUG_HISTORY
2193 if (historyBackClicked) fprintf(stderr,"\nPressed BACK to\t");
2194 else fprintf(stderr,"\nPressed FORWARD to\t");
2195 fprintf(stderr,"\"%s\" (%d)\n",I.currentFolder,(int)*I.history.getCurrentSplitPathIndex());
2196# undef DEBUG_HISTOTY
2197# endif //DEBUG_HISTORY
2198 return rv;
2199 }
2200 }
2201 //------------------------------------------------------------------------------------
2202 const FolderInfo* fi = I.history.getCurrentFolderInfo();
2203 const bool isBrowsingInsideZipFile = (fi && (!(fi->splitPathIndexOfZipFile<0 || fi->splitPathIndexOfZipFile>fi->splitPathIndex)));
2204
2205 // Edit Location CheckButton
2206 bool editLocationInputTextReturnPressed = false;
2207 {
2208
2209 bool mustValidateInputPath = false;
2210 ImGui::PushStyleColor(ImGuiCol_Button,I.editLocationCheckButtonPressed ? dummyButtonColor : style.Colors[ImGuiCol_Button]);
2211
2212 if (ImGui::Button("L##EditLocationCheckButton")) {
2214 //fprintf(stderr,"I.editLocationCheckButtonPressed=%s\n",I.editLocationCheckButtonPressed ? "true" : "false");
2217 ImGui::SetKeyboardFocusHere();
2218 }
2219 //if (!I.editLocationCheckButtonPressed) mustValidateInputPath = true; // or not ? I mean: the user wants to quit or to validate in this case ?
2220 }
2221
2222 ImGui::PopStyleColor();
2223
2225 ImGui::SameLine();
2226 editLocationInputTextReturnPressed = ImGui::InputText("##EditLocationInputText",I.editLocationInputText,MAX_PATH_BYTES,ImGuiInputTextFlags_AutoSelectAll|ImGuiInputTextFlags_EnterReturnsTrue);
2227 if (editLocationInputTextReturnPressed) mustValidateInputPath = true;
2228 else ImGui::Separator();
2229 }
2230
2231 if (mustValidateInputPath) {
2232 // it's better to clean the input path here from trailing slashes:
2233 char cleanEnteredPath[MAX_PATH_BYTES];
2234 strcpy(cleanEnteredPath,I.editLocationInputText);
2235 size_t len = strlen(cleanEnteredPath);
2236 while (len>0 && (cleanEnteredPath[len-1]=='/' || cleanEnteredPath[len-1]=='\\')) {cleanEnteredPath[len-1]='\0';len = strlen(cleanEnteredPath);}
2237
2238 if (len==0 || strcmp(I.currentFolder,cleanEnteredPath)==0) I.editLocationCheckButtonPressed = false;
2239# ifndef IMGUI_USE_MINIZIP
2240 else if (Directory::Exists(cleanEnteredPath)) {
2241 I.editLocationCheckButtonPressed = false; // Optional (return to split-path buttons)
2242 //----------------------------------------------------------------------------------
2243 I.history.switchTo(cleanEnteredPath);
2244 strcpy(I.currentFolder,cleanEnteredPath);
2245 I.forceRescan = true;
2246 }
2247# else //IMGUI_USE_MINIZIP
2248 else {
2249 bool isInsideZipFile = false;
2250 char basePath[MAX_PATH_BYTES];basePath[0]='\0';char zipPath[MAX_PATH_BYTES];zipPath[0]='\0';
2251 isInsideZipFile = PathSplitFirstZipFolder(cleanEnteredPath,basePath,zipPath,false);
2252 if (!isInsideZipFile) {
2253 if (Directory::Exists(cleanEnteredPath)) {
2254 I.editLocationCheckButtonPressed = false; // Optional (return to split-path buttons)
2255 //----------------------------------------------------------------------------------
2256 I.history.switchTo(cleanEnteredPath);
2257 strcpy(I.currentFolder,cleanEnteredPath);
2258 I.forceRescan = true;
2259 }
2260 }
2261 else {
2262 bool dirExists = false;
2263 if (strcmp(I.unz.getZipFilePath(),basePath)==0) dirExists = I.unz.directoryExists(zipPath);
2264 else {
2265 UnZipFile unz(basePath);
2266 dirExists = unz.directoryExists(zipPath);
2267 }
2268 if (dirExists) {
2269 I.editLocationCheckButtonPressed = false; // Optional (return to split-path buttons)
2270 //----------------------------------------------------------------------------------
2271 I.history.switchTo(cleanEnteredPath);
2272 strcpy(I.currentFolder,cleanEnteredPath);
2273 I.forceRescan = true;
2274 }
2275 }
2276 }
2277# endif //IMGUI_USE_MINIZIP
2278 //else fprintf(stderr,"mustValidateInputPath NOOP: \"%s\" \"%s\"\n",I.currentFolder,cleanEnteredPath);
2279 }
2280 else ImGui::SameLine();
2281
2282 }
2283 //------------------------------------------------------------------------------------
2284 // Split Path control
2285 if (!I.editLocationCheckButtonPressed && !editLocationInputTextReturnPressed) {
2286 bool mustSwitchSplitPath = false;
2287 const FolderInfo& fi = *I.history.getCurrentFolderInfo();
2288
2289 ImVec2& framePadding = ImGui::GetStyle().FramePadding;
2290 const float originalFramePaddingX = framePadding.x;
2291 framePadding.x = 0;
2292
2293 // Split Path
2294 // Tab:
2295 {
2296 //-----------------------------------------------------
2297 // TAB LABELS
2298 //-----------------------------------------------------
2299 {
2300 const int numTabs=(int) I.currentSplitPath.size();
2301 int newSelectedTab = fi.splitPathIndex;
2302
2303 /* // DEBUG--To remove--
2304 static int tmpzfi = -2;
2305 if (fi.splitPathIndexOfZipFile!=tmpzfi) {
2306 tmpzfi = fi.splitPathIndexOfZipFile;
2307 if (fi.splitPathIndexOfZipFile!=-1) fprintf(stderr,"%d %s\n",fi.splitPathIndexOfZipFile,I.currentSplitPath[fi.splitPathIndexOfZipFile]);
2308 else fprintf(stderr,"%d\n",fi.splitPathIndexOfZipFile);
2309 }*/
2310 //-------------------
2311 const bool wrapMode = Dialog::WrapMode;
2312 float windowWidth = -1;float sumX = 0;
2313 if (wrapMode) {
2314 sumX+=ImGui::GetCursorPosX();
2315 windowWidth=ImGui::GetWindowWidth()-ImGui::GetStyle().WindowPadding.x;
2316 }
2317 for (int t=0;t<numTabs;t++) {
2318 if (t==fi.splitPathIndex) {
2319 const ImVec4* pDummyButtonColor = &dummyButtonColor;
2320# ifdef IMGUI_USE_MINIZIP
2321 if (t==fi.splitPathIndexOfZipFile) pDummyButtonColor = &dummyZipButtonColor;
2322# endif // IMGUI_USE_MINIZIP
2323 ImGui::PushStyleColor(ImGuiCol_Button,*pDummyButtonColor);
2324 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,*pDummyButtonColor);
2325 ImGui::PushStyleColor(ImGuiCol_ButtonActive,*pDummyButtonColor);
2326 }
2327# ifdef IMGUI_USE_MINIZIP
2328 else if (t==fi.splitPathIndexOfZipFile) {
2329 ImGui::PushStyleColor(ImGuiCol_Button,ColorSet[Internal::ImGuiCol_Dialog_ZipDirectory_Background]);
2330 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ColorSet[Internal::ImGuiCol_Dialog_ZipDirectory_Hover]);
2331 ImGui::PushStyleColor(ImGuiCol_ButtonActive,ColorSet[Internal::ImGuiCol_Dialog_ZipDirectory_Pressed]);
2332 }
2333# endif // IMGUI_USE_MINIZIP
2334# ifndef _MSC_VER
2335 ImGui::PushID(&I.currentSplitPath[t]); // old cl.exe versions don't like this
2336# else //_MSC_VER
2337 ImGui::PushID(t); // will this work ?
2338# endif //_MSC_VER
2339 if (wrapMode) {
2340 sumX+=ImGui::CalcTextSize(I.currentSplitPath[t]).x;
2341 sumX+= 2.*ImGui::GetStyle().FramePadding.x; // needed ?
2342 if (sumX >= windowWidth) sumX=0;
2343 if (t!=0 && sumX>0) ImGui::SameLine(0,0);
2344 }
2345 else if (t>0) ImGui::SameLine(0,0);
2346 const bool pressed = ImGui::Button(I.currentSplitPath[t]);
2347 if (wrapMode && sumX==0) {sumX = ImGui::GetStyle().WindowPadding.x + ImGui::GetItemRectSize().x;}
2348 ImGui::PopID();
2349 if (pressed) {
2350 if (fi.splitPathIndex!=t && !mustSwitchSplitPath) mustSwitchSplitPath = true;
2351 newSelectedTab = t;
2352 }
2353 if (t==fi.splitPathIndex) {
2354 ImGui::PopStyleColor();
2355 ImGui::PopStyleColor();
2356 ImGui::PopStyleColor();
2357 }
2358# ifdef IMGUI_USE_MINIZIP
2359 else if (t==fi.splitPathIndexOfZipFile) {
2360 ImGui::PopStyleColor();
2361 ImGui::PopStyleColor();
2362 ImGui::PopStyleColor();
2363 }
2364# endif // IMGUI_USE_MINIZIP
2365 }
2366 if (mustSwitchSplitPath) {
2367 FolderInfo mfi;
2368 fi.getFolderInfoForSplitPathIndex(newSelectedTab,mfi);
2369 I.history.switchTo(mfi);
2370 I.forceRescan = true;
2373 //fprintf(stderr,"%s\n",I.currentFolder);
2374 }
2375 }
2376 }
2377
2378 framePadding.x = originalFramePaddingX;
2379 }
2380 //------------------------------------------------------------------------------------
2381
2382
2383 // Start collapsable regions----------------------------------------------------------
2384 // User Known directories-------------------------------------------------------------
2385 if (I.allowKnownDirectoriesSection && pUserKnownDirectories->size()>0) {
2386 ImGui::Separator();
2387
2388 if (ImGui::CollapsingHeader("Known Directories##imguifs_UserKnownDirectories")) {
2389 static int id;
2390 ImGui::PushID(&id);
2391
2392 ImGui::PushStyleColor(ImGuiCol_Text,ColorSet[Internal::ImGuiCol_Dialog_Directory_Text]);
2393 ImGui::PushStyleColor(ImGuiCol_Button,ColorSet[Internal::ImGuiCol_Dialog_Directory_Background]);
2394 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ColorSet[Internal::ImGuiCol_Dialog_Directory_Hover]);
2395 ImGui::PushStyleColor(ImGuiCol_ButtonActive,ColorSet[Internal::ImGuiCol_Dialog_Directory_Pressed]);
2396 //const ImVec4& iconsColor = ColorSet[Internal::ImGuiCol_Dialog_Directory_Text];
2397
2398 for (int i=0,sz=(int)pUserKnownDirectories->size();i<sz;i++) {
2399 const char* userKnownFolder = (*pUserKnownDirectories)[i];
2400 const char* userKnownFolderDisplayName = (*pUserKnownDirectoryDisplayNames)[i];
2401 //if (Dialog::DrawFolderIconCallback && Dialog::DrawFolderIconCallback(false,&iconsColor)) ImGui::SameLine();
2402 if (ImGui::SmallButton(userKnownFolderDisplayName) && strcmp(userKnownFolder,I.currentFolder)!=0) {
2403 strcpy(I.currentFolder,userKnownFolder);
2406 I.forceRescan = true;
2407 //------------------------------------------------------------------------------------------------------------------------------
2408 }
2409 if (i!=sz-1 && (i>=*pNumberKnownUserDirectoriesExceptDrives || i%7!=6)) ImGui::SameLine();
2410 }
2411
2412 ImGui::PopStyleColor();
2413 ImGui::PopStyleColor();
2414 ImGui::PopStyleColor();
2415 ImGui::PopStyleColor();
2416
2417 ImGui::PopID();
2418 }
2419
2420 }
2421 // End User Known directories---------------------------------------------------------
2422 // Allow directory creation ----------------------------------------------------------
2423 if (allowDirectoryCreation && !isBrowsingInsideZipFile) {
2424 ImGui::Separator();
2425 bool mustCreate = false;
2426
2427 if (ImGui::CollapsingHeader("New Directory##imguifs_allowDirectoryCreation")) {
2428 static int id;
2429 ImGui::PushID(&id);
2430
2431 ImGui::InputText("##createNewFolderName",&I.newDirectoryName[0],MAX_FILENAME_BYTES);
2432 ImGui::SameLine();
2433 mustCreate = ImGui::Button("CREATE");
2434
2435 ImGui::PopID();
2436 }
2437
2438 if (mustCreate && strlen(I.newDirectoryName)>0) {
2439 char newDirPath[MAX_PATH_BYTES];
2440 Path::Combine(I.currentFolder,I.newDirectoryName,newDirPath,false);
2441 if (!Directory::Exists(newDirPath)) {
2442 //# define SIMULATING_ONLY
2443# ifdef SIMULATING_ONLY
2444 fprintf(stderr,"creating: \"%s\"\n",newDirPath);
2445# undef SIMULATING_ONLY
2446# else //SIMULATING_ONLY
2447 Directory::Create(newDirPath);
2448 if (!Directory::Exists(newDirPath)) fprintf(stderr,"Error creating new folder: \"%s\"\n",newDirPath);
2449 else I.forceRescan = true; // Just update
2450# endif //SIMULATING_ONLY
2451 }
2452 }
2453 }
2454 // End allow directory creation ------------------------------------------------------
2455 // Filtering entries -----------------------------------------------------------------
2456 if (I.allowFiltering) {
2457 ImGui::Separator();
2458 if (ImGui::CollapsingHeader("Filtering##imguifs_fileNameFiltering")) {
2459 static int id;
2460 ImGui::PushID(&id);
2461 I.filter.Draw();
2462 ImGui::PopID();
2463 }
2464
2465 }
2466 // End filtering entries -------------------------------------------------------------
2467 // End collapsable regions------------------------------------------------------------
2468
2469 // Reserve space for selection field------------------------------------------
2470 //----------------------------------------------------------------------------
2471
2472 ImGui::Separator();
2473 // sorting --------------------------------------------------------------------
2474 ImGui::Text("Sorting by: ");ImGui::SameLine();
2475 {
2476 const int oldSortingMode = I.sortingMode;
2477 const int oldSelectedTab = I.sortingMode/2;
2478 //-----------------------------------------------------
2479 // TAB LABELS
2480 //-----------------------------------------------------
2481 {
2482 static const int numTabs=(int)SORT_ORDER_COUNT/2;
2483 int newSortingMode = oldSortingMode;
2484 static const char* names[numTabs] = {"Name","Modified","Size","Type"};
2485 const int numUsedTabs = isSelectFolderDialog ? 2 : numTabs;
2486 for (int t=0;t<numUsedTabs;t++) {
2487 if (t>0) ImGui::SameLine();
2488 if (t==oldSelectedTab) {
2489 ImGui::PushStyleColor(ImGuiCol_Button,dummyButtonColor);
2490 }
2491 ImGui::PushID(&names[t]);
2492 const bool pressed = ImGui::SmallButton(names[t]);
2493 ImGui::PopID();
2494 if (pressed) {
2495 if (oldSelectedTab==t) {
2496 newSortingMode = oldSortingMode;
2497 if (newSortingMode%2==0) ++newSortingMode;// 0,2,4
2498 else --newSortingMode;
2499 }
2500 else newSortingMode = t*2;
2501 }
2502 if (t==oldSelectedTab) {
2503 ImGui::PopStyleColor();
2504 }
2505 }
2506
2507 if (newSortingMode!=oldSortingMode) {
2508 I.sortingMode = newSortingMode;
2509 //printf("sortingMode = %d\n",sortingMode);
2510 I.forceRescan = true;
2511 }
2512
2513 //-- Browsing per row -----------------------------------
2515 ImGui::SameLine();
2516 ImGui::Text(" Display by:");
2517 ImGui::SameLine();
2518 ImGui::PushStyleColor(ImGuiCol_Button,dummyButtonColor);
2519 if (ImGui::SmallButton(!Internal::BrowsingPerRow ? "Column##browsingPerRow" : "Row##browsingPerRow")) {
2521 }
2522 ImGui::PopStyleColor();
2523 }
2524 //-- End browsing per row -------------------------------
2525 }
2526 }
2527 //-----------------------------------------------------------------------------
2528 ImGui::Separator();
2529
2530 //-----------------------------------------------------------------------------
2531 // MAIN BROWSING FRAME:
2532 //-----------------------------------------------------------------------------
2533 {
2534 if (ImGui::BeginChild("BrowsingFrame",ImVec2(0,(isSaveFileDialog || isSelectFolderDialog)?(ImGui::GetContentRegionAvail().y-1.2f*ImGui::GetTextLineHeightWithSpacing()-ImGui::GetStyle().WindowPadding.y):0))) {
2535
2536 I.calculateBrowsingDataTableSizes(ImGui::GetWindowSize()); // (Actually we could save this call every frame, but it's difficult to detect whn we need it)
2537
2538 // ImGui::SetScrollPosHere(); // possible future ref: while drawing to place the scroll bar making a certain entry visible
2539 // Also we don't do any maual clipping here (for long directories using a clipper might be useful)
2540
2541 ImGui::Columns(I.numBrowsingColumns);
2542
2543 static int id;
2544 ImGui::PushID(&id);
2545 int cntEntries = 0;
2546 // Directories --------------------------------------------------------------
2547 if (I.dirs.size()>0) {
2548 ImGui::PushStyleColor(ImGuiCol_Text,ColorSet[Internal::ImGuiCol_Dialog_Directory_Text]);
2549 ImGui::PushStyleColor(ImGuiCol_Button,ColorSet[Internal::ImGuiCol_Dialog_Directory_Background]);
2550 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ColorSet[Internal::ImGuiCol_Dialog_Directory_Hover]);
2551 ImGui::PushStyleColor(ImGuiCol_ButtonActive,ColorSet[Internal::ImGuiCol_Dialog_Directory_Pressed]);
2552 const ImVec4& iconsColor = ColorSet[Internal::ImGuiCol_Dialog_Directory_Text];
2553
2554 for (int i=0,sz=(int)I.dirs.size();i<sz;i++) {
2555 const char* dirName = &I.dirNames[i][0];
2556 if (I.filter.PassFilter(dirName)) {
2557 if (Dialog::DrawFolderIconCallback && Dialog::DrawFolderIconCallback(false,&iconsColor)) ImGui::SameLine();
2558 if (ImGui::SmallButton(dirName)) {
2559 strcpy(I.currentFolder,I.dirs[i]);
2562 I.forceRescan = true;
2563 //------------------------------------------------------------------------------------------------------------------------------
2564 }
2565 ++cntEntries;
2566 if (Internal::BrowsingPerRow) ImGui::NextColumn();
2567 else if (cntEntries==I.numBrowsingEntriesPerColumn) {
2568 cntEntries = 0;
2569 ImGui::NextColumn();
2570 }
2571 }
2572 }
2573
2574 ImGui::PopStyleColor();
2575 ImGui::PopStyleColor();
2576 ImGui::PopStyleColor();
2577 ImGui::PopStyleColor();
2578 }
2579 // Files ----------------------------------------------------------------------
2580 if (!isSelectFolderDialog && I.files.size()>0) {
2581 ImGui::PushStyleColor(ImGuiCol_Text,ColorSet[Internal::ImGuiCol_Dialog_File_Text]);
2582 ImGui::PushStyleColor(ImGuiCol_Button,ColorSet[Internal::ImGuiCol_Dialog_File_Background]);
2583 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ColorSet[Internal::ImGuiCol_Dialog_File_Hover]);
2584 ImGui::PushStyleColor(ImGuiCol_ButtonActive,ColorSet[Internal::ImGuiCol_Dialog_File_Pressed]);
2585 const ImVec4* pIconsColor = &ColorSet[Internal::ImGuiCol_Dialog_File_Text];
2586
2587# ifdef IMGUI_USE_MINIZIP
2588 //const FolderInfo* fi = I.history.getCurrentFolderInfo();
2589 const bool acceptZipFilesForBrowsing = !isBrowsingInsideZipFile;//(fi && (fi->splitPathIndexOfZipFile<0 || fi->splitPathIndexOfZipFile>fi->splitPathIndex));
2590 bool isZipFile = false;bool hasZipExtension = false;
2591
2592 /* // DEBUG-- TO DELETE --
2593 static bool old = true;
2594 if (old!=acceptZipFilesForBrowsing) {
2595 old = acceptZipFilesForBrowsing;
2596 fprintf(stderr,"acceptZipFilesForBrowsing=%s %d %d\n",acceptZipFilesForBrowsing?"true":"false",fi->splitPathIndexOfZipFile,fi->splitPathIndex);
2597 }*/
2598# endif //IMGUI_USE_MINIZIP
2599
2600 for (int i=0,sz=(int)I.files.size();i<sz;i++) {
2601 const char* fileName = &I.fileNames[i][0];
2602 if (I.filter.PassFilter(fileName)) {
2603# ifdef IMGUI_USE_MINIZIP
2604 if (acceptZipFilesForBrowsing) {
2605 hasZipExtension = Path::HasZipExtension(fileName);
2606 if (hasZipExtension && !isZipFile) {
2607 ImGui::PopStyleColor(4);
2608 ImGui::PushStyleColor(ImGuiCol_Text,ColorSet[Internal::ImGuiCol_Dialog_ZipDirectory_Text]);
2609 ImGui::PushStyleColor(ImGuiCol_Button,ColorSet[Internal::ImGuiCol_Dialog_ZipDirectory_Background]);
2610 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ColorSet[Internal::ImGuiCol_Dialog_ZipDirectory_Hover]);
2611 ImGui::PushStyleColor(ImGuiCol_ButtonActive,ColorSet[Internal::ImGuiCol_Dialog_ZipDirectory_Pressed]);
2612 pIconsColor = &ColorSet[Internal::ImGuiCol_Dialog_ZipDirectory_Text];
2613 }
2614 else if (!hasZipExtension && isZipFile) {
2615 ImGui::PopStyleColor(4);
2616 ImGui::PushStyleColor(ImGuiCol_Text,ColorSet[Internal::ImGuiCol_Dialog_File_Text]);
2617 ImGui::PushStyleColor(ImGuiCol_Button,ColorSet[Internal::ImGuiCol_Dialog_File_Background]);
2618 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ColorSet[Internal::ImGuiCol_Dialog_File_Hover]);
2619 ImGui::PushStyleColor(ImGuiCol_ButtonActive,ColorSet[Internal::ImGuiCol_Dialog_File_Pressed]);
2620 pIconsColor = &ColorSet[Internal::ImGuiCol_Dialog_File_Text];
2621 }
2622 isZipFile = hasZipExtension;
2623 }
2624# endif //IMGUI_USE_MINIZIP
2625 if (Dialog::DrawFileIconCallback && Dialog::DrawFileIconCallback(I.fileExtensionTypes[i],pIconsColor)) ImGui::SameLine();
2626 if (ImGui::SmallButton(fileName)) {
2627 if (!isSaveFileDialog) {
2628 strcpy(rv,I.files[i]);
2629 I.open = true;
2630 }
2631 else {
2633 }
2634 }
2635# if (defined(__EMSCRIPTEN__) && defined(EMSCRIPTEN_SAVE_SHELL))
2636 static char tmpString[ImGuiFs::MAX_PATH_BYTES*2]="";
2637# ifndef IMGUI_USE_MINIZIP
2638 if (ImGui::IsItemHovered() && ImGui::GetIO().KeyCtrl) {
2639 ImGui::SetTooltip("%s","right click to download this file locally");
2640 if (ImGui::GetIO().MouseClicked[1]) {
2641 strcpy(tmpString,"saveFileFromMemoryFSToDisk('");
2642 strcat(tmpString,I.files[i]);strcat(tmpString,"','");
2643 strcat(tmpString,fileName);strcat(tmpString,"')");
2644
2645 emscripten_run_script(tmpString);
2646 }
2647 }
2648# else // IMGUI_USE_MINIZIP
2649 if (ImGui::IsItemHovered()) {
2650 if (!isBrowsingInsideZipFile && ImGui::GetIO().KeyCtrl) {
2651 ImGui::SetTooltip("%s","right click to download this file locally");
2652 if (ImGui::GetIO().MouseClicked[1]) {
2653 strcpy(tmpString,"saveFileFromMemoryFSToDisk('");
2654 strcat(tmpString,I.files[i]);strcat(tmpString,"','");
2655 strcat(tmpString,fileName);strcat(tmpString,"')");
2656
2657 emscripten_run_script(tmpString);
2658 }
2659 }
2660 else if (isZipFile) {
2661 ImGui::SetTooltip("right click to browse it");
2662 if (ImGui::GetIO().MouseClicked[1]) {
2663 strcpy(I.currentFolder,I.files[i]);
2666 I.forceRescan = true;
2667 //------------------------------------------------------------------------------------------------------------------------------
2668 }
2669 }
2670 }
2671# endif // IMGUI_USE_MINIZIP
2672# elif IMGUI_USE_MINIZIP // (defined(__EMSCRIPTEN__) && defined(EMSCRIPTEN_SAVE_SHELL))
2673 if (isZipFile) {
2674 if (ImGui::IsItemHovered()) ImGui::SetTooltip("right click to browse it");
2675 if (ImGui::GetIO().MouseClicked[1]) {
2676 strcpy(I.currentFolder,I.files[i]);
2679 I.forceRescan = true;
2680 //------------------------------------------------------------------------------------------------------------------------------
2681 }
2682 }
2683# endif //(defined(__EMSCRIPTEN__) && defined(EMSCRIPTEN_SAVE_SHELL))
2684 ++cntEntries;
2685 if (Internal::BrowsingPerRow) ImGui::NextColumn();
2686 else if (cntEntries==I.numBrowsingEntriesPerColumn) {
2687 cntEntries = 0;
2688 ImGui::NextColumn();
2689 }
2690 }
2691 }
2692
2693 ImGui::PopStyleColor();
2694 ImGui::PopStyleColor();
2695 ImGui::PopStyleColor();
2696 ImGui::PopStyleColor();
2697 }
2698 //-----------------------------------------------------------------------------
2699 ImGui::PopID();
2700 } // ImGui::BeginChild(...)
2701 ImGui::EndChild();
2702
2703 }
2704 //-----------------------------------------------------------------------------
2705 // Selection field -------------------------------------------------------------------
2706 if (isSaveFileDialog || isSelectFolderDialog) {
2707 ImGui::Separator();
2708 bool selectionButtonPressed = false;
2709
2710 static int id;
2711 float lastTwoButtonsWidth = 0;
2712 ImGui::PushID(&id);
2713 if (isSaveFileDialog) {
2714 ImGui::AlignTextToFramePadding();
2715 ImGui::Text("File:");ImGui::SameLine();
2716 lastTwoButtonsWidth = ImGui::CalcTextSize("Save Cancel").x+2.0f*(style.FramePadding.x+style.ItemSpacing.x)+style.WindowPadding.x;
2717 ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x-lastTwoButtonsWidth);
2718 ImGui::InputText("##saveFileNameDialog",&I.saveFileName[0],MAX_FILENAME_BYTES);
2719 if (ImGui::IsItemHovered() && I.mustFilterSaveFilePathWithFileFilterExtensionString && fileFilterExtensionString && strlen(fileFilterExtensionString)>0) ImGui::SetTooltip("%s",fileFilterExtensionString);
2720 ImGui::PopItemWidth();
2721 ImGui::SameLine();
2722 }
2723 else {
2724 ImGui::AlignTextToFramePadding();
2725
2726 static const ImVec4 sf(1.0,0.8,0.5,1); // selected folder color factor
2728 const ImVec4& r = style.Colors[ImGuiCol_Text];
2729 Internal::ColorCombine(c,r,sf);
2730 ImGui::TextColored(ColorSet[Internal::ImGuiCol_Dialog_SelectedFolder_Text],"Folder:");
2731
2732 //ImGui::Text("Folder:"); // Faster alternative to the 5 lines above
2733
2734 ImGui::SameLine();
2735 lastTwoButtonsWidth = ImGui::CalcTextSize("Select Cancel").x+2.0f*(style.FramePadding.x+style.ItemSpacing.x)+style.WindowPadding.x;
2736 ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x-lastTwoButtonsWidth);
2737 ImGui::InputText("##selectFileNameDialog",&I.saveFileName[0],MAX_FILENAME_BYTES,ImGuiInputTextFlags_ReadOnly);
2738 ImGui::PopItemWidth();
2739 ImGui::SameLine();
2740 }
2741
2742 bool mustCancel = false;
2743 if (ImGui::Button("Cancel")) {mustCancel = true;} ImGui::SameLine(); // Optional line
2744
2745 if (isSelectFolderDialog) selectionButtonPressed = ImGui::Button("Select");
2746 else selectionButtonPressed = ImGui::Button("Save");
2747
2748
2749 ImGui::PopID();
2750
2751 if (selectionButtonPressed) {
2752 if (isSelectFolderDialog) {
2753 strcpy(rv,I.currentFolder);
2754 I.open = true;
2755 }
2756 else if (isSaveFileDialog) {
2757 if (strlen(I.saveFileName)>0) {
2758 bool pathOk = true;
2759 if (I.mustFilterSaveFilePathWithFileFilterExtensionString && fileFilterExtensionString && strlen(fileFilterExtensionString)>0) {
2760 pathOk = false;
2761 char saveFileNameExtension[MAX_FILENAME_BYTES];Path::GetExtension(I.saveFileName,saveFileNameExtension);
2762 const bool saveFileNameHasExtension = strlen(saveFileNameExtension)>0;
2763 //-------------------------------------------------------------------
2764 FilenameStringVector wExts;String::Split(fileFilterExtensionString,wExts,';');
2765 const size_t wExtsSize = wExts.size();
2766 if (!saveFileNameHasExtension) {
2767 if (wExtsSize==0) pathOk = true; // Bad situation, better allow this case
2768 else strcat(I.saveFileName,wExts[0]);
2769 }
2770 else {
2771 // saveFileNameHasExtension
2772 for (size_t i = 0;i<wExtsSize;i++) {
2773 const char* ext = wExts[i];
2774 if (strcmp(ext,saveFileNameExtension)==0) {
2775 pathOk = true;
2776 break;
2777 }
2778 }
2779 if (!pathOk && wExtsSize>0) strcat(I.saveFileName,wExts[0]);
2780 }
2781 }
2782 if (pathOk) {
2783 char savePath[MAX_PATH_BYTES];
2784 Path::Combine(I.currentFolder,I.saveFileName,savePath,false);
2785 strcpy(rv,savePath);
2786 I.open = true;
2787 }
2788 }
2789 }
2790 }
2791 else if (mustCancel && rv[0]==0) {
2792 ImGui::CloseCurrentPopup();
2793# ifdef IMGUI_USE_MINIZIP
2794 I.unz.close();
2795# endif // IMGUI_USE_MINIZIP
2796 I.freeMemory();
2797
2799 }
2800 //ImGui::Separator();
2801 //ImGui::Spacing();
2802 }
2803 // End selection field----------------------------------------------------------------
2804
2805 if (rv[0]!=0 || !I.open) {
2806 ImGui::CloseCurrentPopup();
2807# ifdef IMGUI_USE_MINIZIP
2808 I.unz.close();
2809# endif // IMGUI_USE_MINIZIP
2810 I.freeMemory();
2811 }
2812 ImGui::EndPopup();
2813 return rv;
2814}
2815
2816const char* Dialog::chooseFileDialog(bool dialogTriggerButton,const char* directory,const char* fileFilterExtensionString,const char* windowTitle,const ImVec2& windowSize,const ImVec2& windowPos,const float windowAlpha) {
2818 if (dialogTriggerButton || (!internal->rescan && internal->open && strlen(getChosenPath())==0)) {
2819 const char* cp = ChooseFileMainMethod(*this,directory,false,false,"",fileFilterExtensionString,windowTitle,windowSize,windowPos,windowAlpha);
2820 if (!internal->open) { // AFAIK this should happen only when user clicks the close button (but not when he clicks cancel)
2822# ifdef IMGUI_USE_MINIZIP
2823 internal->unz.close();
2824# endif // IMGUI_USE_MINIZIP
2826 }
2827 return cp;
2828 }
2829 return "";
2830}
2831const char* Dialog::chooseFolderDialog(bool dialogTriggerButton,const char* directory,const char* windowTitle,const ImVec2& windowSize,const ImVec2& windowPos,const float windowAlpha) {
2833 if (dialogTriggerButton || (!internal->rescan && internal->open && strlen(getChosenPath())==0)) {
2834 const char* cp = ChooseFileMainMethod(*this,directory,true,false,"","",windowTitle,windowSize,windowPos,windowAlpha);
2835 if (!internal->open) {
2837# ifdef IMGUI_USE_MINIZIP
2838 internal->unz.close();
2839# endif // IMGUI_USE_MINIZIP
2841 }
2842 return cp;
2843 }
2844 return "";
2845}
2846const 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) {
2848 if (dialogTriggerButton || (!internal->rescan && internal->open && strlen(getChosenPath())==0)) {
2849 const char* cp = ChooseFileMainMethod(*this,directory,false,true,startingFileNameEntry,fileFilterExtensionString,windowTitle,windowSize,windowPos,windowAlpha);
2850 if (!internal->open) {
2852# ifdef IMGUI_USE_MINIZIP
2853 internal->unz.close();
2854# endif // IMGUI_USE_MINIZIP
2856 }
2857 return cp;
2858 }
2859 return "";
2860}
2861
2862
2863
2864#ifndef IMGUIFS_NO_EXTRA_METHODS
2865// just expose some methods
2866
2867void PathGetAbsolute(const char *path, char *rv) {Path::GetAbsolutePath(path,rv);}
2868void PathGetDirectoryName(const char *filePath, char *rv) {Path::GetDirectoryName(filePath,rv);}
2869void PathGetFileName(const char *filePath, char *rv) {Path::GetFileName(filePath,rv);}
2870void PathGetFileNameWithoutExtension(const char *filePath, char *rv) {Path::GetFileNameWithoutExtension(filePath,rv);}
2871void PathGetExtension(const char* filePath,char *rv) {Path::GetExtension(filePath,rv);}
2872void PathChangeExtension(const char* filePath,const char* newExtension,char *rv) {Path::ChangeExtension(filePath,newExtension,rv);}
2873void PathAppend(const char* directory,char* rv) {Path::Append(directory,rv);}
2874void PathSplit(const char* path,FilenameStringVector& rv,bool leaveIntermediateTrailingSlashes) {Path::Split(path,rv,leaveIntermediateTrailingSlashes);}
2875void DirectoryGetDirectories(const char* directoryName,PathStringVector& result,FilenameStringVector* pOptionalNamesOut,Sorting sorting) {Directory::GetDirectories(directoryName,result,pOptionalNamesOut,sorting);}
2876void DirectoryGetFiles(const char* directoryName,PathStringVector& result,FilenameStringVector* pOptionalNamesOut, Sorting sorting) {Directory::GetFiles(directoryName,result,pOptionalNamesOut,sorting);}
2877void DirectoryCreate(const char* directoryName) {Directory::Create(directoryName);}
2878bool PathExists(const char* path) {
2879 struct stat statbuf;return (stat(path, &statbuf) != -1 && (S_ISDIR(statbuf.st_mode) || S_ISREG(statbuf.st_mode)));
2880}
2881bool DirectoryExists(const char* path) {return Directory::Exists(path);}
2882bool FileExists(const char* path) {
2883 struct stat statbuf;return (stat(path, &statbuf) != -1 && S_ISREG(statbuf.st_mode));
2884}
2885#endif //IMGUIFS_NO_EXTRA_METHODS
2886
2887
2888} // namespace ImGuiFs
2889
2890
#define IM_ASSERT(_EXPR)
#define strcasecmp
#define pName
static void GetDirectoryName(const char *filePath, char *rv)
static bool HasZipExtension(const char *filePath)
static void GetAbsolutePath(const char *path, char *rv)
static void Split(const char *path, FilenameStringVector &rv, bool leaveIntermediateTrailingSlashes=true)
static void ChangeExtension(const char *filePath, const char *newExtension, char *rv)
static void GetFileName(const char *filePath, char *rv)
static void GetFileNameWithoutExtension(const char *filePath, char *rv)
static void GetExtension(const char *filePath, char *rv)
static void Combine(const char *directory, const char *fileName, char *rv, bool appendMode=true)
static void Append(const char *directory, char *rv)
static const SorterSignature Sorters[]
static int Lastmodsort(const struct dirent **e1, const struct dirent **e2)
static SorterSignature sorter
static struct stat stat1
static int Sizesort(const struct dirent **e1, const struct dirent **e2)
static int Lastmodsortinverse(const struct dirent **e1, const struct dirent **e2)
static int Typesort(const struct dirent **e1, const struct dirent **e2)
static int Alphasortinverse(const struct dirent **e1, const struct dirent **e2)
static int Alphasort(const struct dirent **e1, const struct dirent **e2)
static int Typesortinverse(const struct dirent **e1, const struct dirent **e2)
static const SorterSignature & SetSorter(Sorting sorting)
static struct stat stat2
int(* SorterSignature)(const struct dirent **e1, const struct dirent **e2)
static int Sizesortinverse(const struct dirent **e1, const struct dirent **e2)
static void Substr(const char *text, char *rv, int start, int count=-1)
static void PushBack(FilenameStringVector &rv, const char *s)
static void ReplaceInPlace(char *text, const char toReplace, const char replacement)
static void Split(const char *text, FilenameStringVector &rv, const char c=' ')
static void Replace(const char *baseText, const char textToReplace, const char replacement, char *rv)
static void ToLowerInPlace(char *text)
static int Find(const char *text, const char toFind, int beg=0)
static void ToLower(const char *text, char *rv)
static int FindLastOf(const char *text, const char toFind)
#define IMGUIFS_CDECL
#define FILENAME_MAX
#define DIRENT_MAX_PATH
unsigned long long int ZPOS64_T
Definition: ioapi.h:95
bool load() override
const int MAX_FILENAME_BYTES
void PathGetAbsolute(const char *path, char *rv)
void PathGetFileNameWithoutExtension(const char *filePath, char *rv)
ImVector< FilenameString > FilenameStringVector
void FileGetExtensionTypesFromFilenames(ImVector< int > &fileExtensionTypesOut, const FilenameStringVector &fileNames)
char PathString[MAX_PATH_BYTES]
void PathGetFileName(const char *filePath, char *rv)
const int MAX_PATH_BYTES
void PathChangeExtension(const char *filePath, const char *newExtension, char *rv)
int FileGetExtensionType(const char *path)
@ SORT_ORDER_TYPE_INVERSE
@ SORT_ORDER_ALPHABETIC
@ SORT_ORDER_LAST_MODIFICATION
@ SORT_ORDER_ALPHABETIC_INVERSE
@ SORT_ORDER_LAST_MODIFICATION_INVERSE
@ SORT_ORDER_SIZE_INVERSE
void DirectoryCreate(const char *directoryName)
void PathGetExtension(const char *filePath, char *rv)
bool PathExists(const char *path)
void DirectoryGetDirectories(const char *directoryName, PathStringVector &result, FilenameStringVector *pOptionalNamesOut, Sorting sorting)
void DirectoryGetFiles(const char *directoryName, PathStringVector &result, FilenameStringVector *pOptionalNamesOut, Sorting sorting)
bool FileGetContentBase(const char *path, ImVector< CharType > &bufferOut, bool openInTextMode, const char *password)
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)
static size_t ImFormatString(char *buf, size_t buf_size, const char *fmt,...)
ImVector< PathString > PathStringVector
bool FileGetContent(const char *path, ImVector< unsigned char > &bufferOut, bool openInTextMode, const char *password)
bool DirectoryExists(const char *path)
void PathGetDirectoryName(const char *filePath, char *rv)
void PathAppend(const char *directory, char *rv)
void PathSplit(const char *path, FilenameStringVector &rv, bool leaveIntermediateTrailingSlashes)
char FilenameString[MAX_FILENAME_BYTES]
bool FileExists(const char *path)
IMGUI_API const char * getLastDirectory() const
static ImVec2 WindowSize
IMGUI_API 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)
static DrawFileIconDelegate DrawFileIconCallback
bool(* DrawFolderIconDelegate)(bool useOpenFolderIconIfAvailable, const ImVec4 *pOptionalColorOverride)
IMGUI_API bool hasUserJustCancelledDialog() const
static ImGuiWindowFlags ExtraWindowFlags
IMGUI_API Dialog(bool noKnownDirectoriesSection=false, bool noCreateDirectorySection=false, bool noFilteringSection=false, bool detectKnownDirectoriesAtEachOpening=false, bool addDisplayByOption=false, bool dontFilterSaveFilePathsEnteredByTheUser=false)
IMGUI_API const char * getChosenPath() const
static DrawFolderIconDelegate DrawFolderIconCallback
static ImVec4 WindowLTRBOffsets
IMGUI_API 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)
bool(* DrawFileIconDelegate)(int fileExtensionType, const ImVec4 *pOptionalColorOverride)
IMGUI_API 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)
struct Internal * internal
static bool WrapMode
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)
static void GetFiles(const char *path, PathStringVector &files, const char *wantedExtensions, const char *unwantedExtensions=NULL, FilenameStringVector *pOptionalNamesOut=NULL, Sorting sorting=SORT_ORDER_ALPHABETIC)
static int DirentGetFiles(const struct dirent *de)
static int DirentGetDirectories(const struct dirent *de)
static const PathStringVector & GetUserKnownDirectories(const FilenameStringVector **pOptionalUserKnownDirectoryDisplayNamesOut, const int **pOptionalNumberKnownUserDirectoriesExceptDrives=NULL, bool forceUpdate=false)
static void GetFiles(const char *directoryName, PathStringVector &result, FilenameStringVector *pOptionalNamesOut=NULL, Sorting sorting=SORT_ORDER_ALPHABETIC)
static bool Exists(const char *path)
static void GetDirectories(const char *directoryName, PathStringVector &result, FilenameStringVector *pOptionalNamesOut=NULL, Sorting sorting=SORT_ORDER_ALPHABETIC)
static void Create(const char *directoryName)
bool getFolderInfoForSplitPathIndex(int _splitPathIndex, FolderInfo &rv) const
static FilenameStringVector SplitPath
static int GetSplitPathIndexOfZipFile(const FilenameStringVector &SplitPath)
void fromCurrentFolder(const char *path)
const FolderInfo & operator=(const FolderInfo &o)
char currentFolder[MAX_PATH_BYTES]
int getSplitPathIndexFor(const char *path) const
bool isEqual(const FolderInfo &fi) const
char fullFolder[MAX_PATH_BYTES]
void getSplitPath(FilenameStringVector &splitPath) const
bool isEqual(const char *path) const
FolderInfo(const FolderInfo &o)
ImVector< FolderInfo > info
bool getCurrentSplitPath(FilenameStringVector &rv) const
bool switchTo(const char *currentFolder)
const char * getCurrentFolder() const
size_t getInfoSize() const
bool switchTo(const FolderInfo &fi)
const FolderInfo * getCurrentFolderInfo() const
const int * getCurrentSplitPathIndex() const
int getExtensionType(const char *ext, bool caseSensitiveMatch=false) const
bool drawIcon(int extensionType, const ImVec4 *pOptionalColorOverride=NULL) const
bool drawIcon(const char *ext, bool caseSensitiveMatch=false, const ImVec4 *pOptionalColorOverride=NULL) const
void fillExtensionTypesFromFilenames(ImVector< int > &fileExtensionTypes, const FilenameStringVector &fileNames)
static ImGuiFsDrawIconStruct & Get()
FilenameStringVector currentSplitPath
static bool BrowsingPerRow
bool detectKnownDirectoriesAtEveryOpening
char editLocationInputText[MAX_PATH_BYTES]
ImVector< int > fileExtensionTypes
static void FreeMemory(PathStringVector &v)
PathStringVector files
char newDirectoryName[MAX_FILENAME_BYTES]
ImGuiTextFilter filter
char chosenPath[MAX_PATH_BYTES]
FilenameStringVector fileNames
void calculateBrowsingDataTableSizes(const ImVec2 &childWindowSize=ImVec2(-1,-1))
char wndTitle[MAX_PATH_BYTES]
static void ColorCombine(ImVec4 &c, const ImVec4 &r, const ImVec4 &factor)
char currentFolder[MAX_PATH_BYTES]
PathStringVector dirs
FilenameStringVector dirNames
bool mustFilterSaveFilePathWithFileFilterExtensionString
char saveFileName[MAX_FILENAME_BYTES]
ZPOS64_T uncompressed_size
Definition: unzip.h:120
uLong dosDate
Definition: unzip.h:117
ZPOS64_T compressed_size
Definition: unzip.h:119
ZPOS64_T number_entry
Definition: unzip.h:98
int ZEXPORT unzOpenCurrentFilePassword(unzFile file, const char *password)
Definition: unzip.c:1646
int ZEXPORT unzLocateFile(unzFile file, const char *szFileName, int iCaseSensitivity)
Definition: unzip.c:1232
int ZEXPORT unzGetCurrentFileInfo64(unzFile file, unz_file_info64 *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)
Definition: unzip.c:1124
int ZEXPORT unzGoToFirstFile(unzFile file)
Definition: unzip.c:1178
int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, unsigned len)
Definition: unzip.c:1684
int ZEXPORT unzGetGlobalInfo64(unzFile file, unz_global_info64 *pglobal_info)
Definition: unzip.c:828
unzFile ZEXPORT unzOpen64(const void *path)
Definition: unzip.c:798
int ZEXPORT unzCloseCurrentFile(unzFile file)
Definition: unzip.c:2004
int ZEXPORT unzGoToNextFile(unzFile file)
Definition: unzip.c:1199
int ZEXPORT unzClose(unzFile file)
Definition: unzip.c:808
voidp unzFile
Definition: unzip.h:70
#define UNZ_OK
Definition: unzip.h:74
#define SEEK_SET
Definition: zip.c:88
#define SEEK_END
Definition: zip.c:84