openMSX
ImGuiFileDialog.cc
Go to the documentation of this file.
1//#pragma region PVS STUDIO
2
3// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
4// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
5
6//#pragma endregion
7
8//#pragma region IGFD LICENSE
9
10/*
11MIT License
12
13Copyright (c) 2019-2020 Stephane Cuillerdier (aka aiekick)
14
15Permission is hereby granted, free of charge, to any person obtaining a copy
16of this software and associated documentation files (the "Software"), to deal
17in the Software without restriction, including without limitation the rights
18to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19copies of the Software, and to permit persons to whom the Software is
20furnished to do so, subject to the following conditions:
21
22The above copyright notice and this permission notice shall be included in all
23copies or substantial portions of the Software.
24
25THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31SOFTWARE.
32*/
33
34//#pragma endregion
35
36#include "ImGuiFileDialog.h"
37
38#ifdef __cplusplus
39
40//#pragma region Includes
41
42#include <cfloat>
43#include <cstring> // stricmp / strcasecmp
44#include <cstdarg> // variadic
45#include <sstream>
46#include <iomanip>
47#include <ctime>
48#include <memory>
49#include <sys/stat.h>
50#include <cstdio>
51#include <cerrno>
52
54static void RightAlignText(const char* text, const char* maxWidthText)
55{
56 auto maxWidth = ImGui::CalcTextSize(maxWidthText).x;
57 auto actualWidth = ImGui::CalcTextSize(text).x;
58 if (auto spacing = maxWidth - actualWidth; spacing > 0.0f) {
59 auto pos = ImGui::GetCursorPosX();
60 ImGui::SetCursorPosX(pos + spacing);
61 }
63
64}
66
67
68
69// this option need c++17
70#ifdef USE_STD_FILESYSTEM
71#include <filesystem>
72#include <exception>
73#endif // USE_STD_FILESYSTEM
74
75#ifdef __EMSCRIPTEN__
76#include <emscripten.h>
77#endif // __EMSCRIPTEN__
78
79#ifdef _MSC_VER
80
81#define IGFD_DEBUG_BREAK \
82 if (IsDebuggerPresent()) __debugbreak()
83#else
84#define IGFD_DEBUG_BREAK
85#endif
86
87#if defined(__WIN32__) || defined(WIN32) || defined(_WIN32) || defined(__WIN64__) || defined(WIN64) || defined(_WIN64) || defined(_MSC_VER)
88#define _IGFD_WIN_
89#define stat _stat
90#define stricmp _stricmp
91#include <cctype>
92// this option need c++17
93#ifdef USE_STD_FILESYSTEM
94#include <windows.h>
95#else
96#include "dirent/dirent.h" // directly open the dirent file attached to this lib
97#endif // USE_STD_FILESYSTEM
98#define PATH_SEP '\\'
99#ifndef PATH_MAX
100#define PATH_MAX 260
101#endif // PATH_MAX
102#elif defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__APPLE__) || defined(__EMSCRIPTEN__)
103#define _IGFD_UNIX_
104#define stricmp strcasecmp
105#include <sys/types.h>
106// this option need c++17
107#ifndef USE_STD_FILESYSTEM
108#include <dirent.h>
109#endif // USE_STD_FILESYSTEM
110#define PATH_SEP '/'
111#endif // _IGFD_UNIX_
112
113#include "imgui.h"
114#include "imgui_internal.h"
115
116// legacy compatibility 1.89
117#ifndef IM_TRUNC
118#define IM_TRUNC IM_FLOOR
119#endif
120
121#include <cstdlib>
122#include <algorithm>
123#include <iostream>
124
125//#pragma endregion
126
127//#pragma region Common defines
128
129#ifdef USE_THUMBNAILS
130#ifndef DONT_DEFINE_AGAIN__STB_IMAGE_IMPLEMENTATION
131#ifndef STB_IMAGE_IMPLEMENTATION
132#define STB_IMAGE_IMPLEMENTATION
133#endif // STB_IMAGE_IMPLEMENTATION
134#endif // DONT_DEFINE_AGAIN__STB_IMAGE_IMPLEMENTATION
135#include "stb/stb_image.h"
136#ifndef DONT_DEFINE_AGAIN__STB_IMAGE_RESIZE_IMPLEMENTATION
137#ifndef STB_IMAGE_RESIZE_IMPLEMENTATION
138#define STB_IMAGE_RESIZE_IMPLEMENTATION
139#endif // STB_IMAGE_RESIZE_IMPLEMENTATION
140#endif // DONT_DEFINE_AGAIN__STB_IMAGE_RESIZE_IMPLEMENTATION
141#include "stb/stb_image_resize.h"
142#endif // USE_THUMBNAILS
143
144// float comparisons
145#ifndef IS_FLOAT_DIFFERENT
146#define IS_FLOAT_DIFFERENT(a, b) (fabs((a) - (b)) > FLT_EPSILON)
147#endif // IS_FLOAT_DIFFERENT
148#ifndef IS_FLOAT_EQUAL
149#define IS_FLOAT_EQUAL(a, b) (fabs((a) - (b)) < FLT_EPSILON)
150#endif // IS_FLOAT_EQUAL
151
152//#pragma endregion
153
154//#pragma region IGFD NAMESPACE
155
156//#pragma region CUSTOMIZATION DEFINES
157
159// COMBOBOX
161#ifndef FILTER_COMBO_AUTO_SIZE
162#define FILTER_COMBO_AUTO_SIZE 1
163#endif // FILTER_COMBO_AUTO_SIZE
164#ifndef FILTER_COMBO_MIN_WIDTH
165#define FILTER_COMBO_MIN_WIDTH 150.0f
166#endif // FILTER_COMBO_MIN_WIDTH
167#ifndef IMGUI_BEGIN_COMBO
168#define IMGUI_BEGIN_COMBO ImGui::BeginCombo
169#endif // IMGUI_BEGIN_COMBO
171// BUTTON
173// for lets you define your button widget
174// if you have like me a special bi-color button
175#ifndef IMGUI_PATH_BUTTON
176#define IMGUI_PATH_BUTTON ImGui::Button
177#endif // IMGUI_PATH_BUTTON
178#ifndef IMGUI_BUTTON
179#define IMGUI_BUTTON ImGui::Button
180#endif // IMGUI_BUTTON
182// locales
184#ifndef createDirButtonString
185#define createDirButtonString "+"
186#endif // createDirButtonString
187#ifndef okButtonString
188#define okButtonString "OK"
189#endif // okButtonString
190#ifndef okButtonWidth
191#define okButtonWidth 0.0f
192#endif // okButtonWidth
193#ifndef cancelButtonString
194#define cancelButtonString "Cancel"
195#endif // cancelButtonString
196#ifndef cancelButtonWidth
197#define cancelButtonWidth 0.0f
198#endif // cancelButtonWidth
199#ifndef okCancelButtonAlignement
200#define okCancelButtonAlignement 0.0f
201#endif // okCancelButtonAlignement
202#ifndef invertOkAndCancelButtons
203// 0 => disabled, 1 => enabled
204#define invertOkAndCancelButtons 0
205#endif // invertOkAndCancelButtons
206#ifndef resetButtonString
207#define resetButtonString "R"
208#endif // resetButtonString
209#ifndef drivesButtonString
210#define drivesButtonString "Drives"
211#endif // drivesButtonString
212#ifndef editPathButtonString
213#define editPathButtonString "E"
214#endif // editPathButtonString
215#ifndef searchString
216#define searchString "Filter:"
217#endif // searchString
218#ifndef dirEntryString
219#define dirEntryString "[Dir]"
220#endif // dirEntryString
221#ifndef linkEntryString
222#define linkEntryString "[Link]"
223#endif // linkEntryString
224#ifndef fileEntryString
225#define fileEntryString "[File]"
226#endif // fileEntryString
227#ifndef fileNameString
228#define fileNameString "File Name:"
229#endif // fileNameString
230#ifndef dirNameString
231#define dirNameString "Directory Path:"
232#endif // dirNameString
233#ifndef buttonResetSearchString
234#define buttonResetSearchString "Reset filter"
235#endif // buttonResetSearchString
236#ifndef buttonDriveString
237#define buttonDriveString "Drives"
238#endif // buttonDriveString
239#ifndef buttonEditPathString
240#define buttonEditPathString "Edit path\nYou can also right click on path buttons"
241#endif // buttonEditPathString
242#ifndef buttonResetPathString
243#define buttonResetPathString "Reset to current directory"
244#endif // buttonResetPathString
245#ifndef buttonCreateDirString
246#define buttonCreateDirString "Create Directory"
247#endif // buttonCreateDirString
248#ifndef tableHeaderAscendingIcon
249#define tableHeaderAscendingIcon "A|"
250#endif // tableHeaderAscendingIcon
251#ifndef tableHeaderDescendingIcon
252#define tableHeaderDescendingIcon "D|"
253#endif // tableHeaderDescendingIcon
254#ifndef tableHeaderFileNameString
255#define tableHeaderFileNameString "File name"
256#endif // tableHeaderFileNameString
257#ifndef tableHeaderFileTypeString
258#define tableHeaderFileTypeString "Type"
259#endif // tableHeaderFileTypeString
260#ifndef tableHeaderFileSizeString
261#define tableHeaderFileSizeString "Size"
262#endif // tableHeaderFileSizeString
263#ifndef tableHeaderFileDateString
264#define tableHeaderFileDateString "Date"
265#endif // tableHeaderFileDateString
266#ifndef fileSizeBytes
267#define fileSizeBytes "o"
268#endif // fileSizeBytes
269#ifndef fileSizeKiloBytes
270#define fileSizeKiloBytes "Ko"
271#endif // fileSizeKiloBytes
272#ifndef fileSizeMegaBytes
273#define fileSizeMegaBytes "Mo"
274#endif // fileSizeMegaBytes
275#ifndef fileSizeGigaBytes
276#define fileSizeGigaBytes "Go"
277#endif // fileSizeGigaBytes
278#ifndef OverWriteDialogTitleString
279#define OverWriteDialogTitleString "The selected file already exists!"
280#endif // OverWriteDialogTitleString
281#ifndef OverWriteDialogMessageString
282#define OverWriteDialogMessageString "Are you sure you want to overwrite it?"
283#endif // OverWriteDialogMessageString
284#ifndef OverWriteDialogConfirmButtonString
285#define OverWriteDialogConfirmButtonString "Confirm"
286#endif // OverWriteDialogConfirmButtonString
287#ifndef OverWriteDialogCancelButtonString
288#define OverWriteDialogCancelButtonString "Cancel"
289#endif // OverWriteDialogCancelButtonString
290#ifndef DateTimeFormat
291// see strftime functionin <ctime> for customize
292#define DateTimeFormat "%Y/%m/%d %H:%M"
293#endif // DateTimeFormat
295// THUMBNAILS
297#ifdef USE_THUMBNAILS
298#ifndef tableHeaderFileThumbnailsString
299#define tableHeaderFileThumbnailsString "Thumbnails"
300#endif // tableHeaderFileThumbnailsString
301#ifndef DisplayMode_FilesList_ButtonString
302#define DisplayMode_FilesList_ButtonString "FL"
303#endif // DisplayMode_FilesList_ButtonString
304#ifndef DisplayMode_FilesList_ButtonHelp
305#define DisplayMode_FilesList_ButtonHelp "File List"
306#endif // DisplayMode_FilesList_ButtonHelp
307#ifndef DisplayMode_ThumbailsList_ButtonString
308#define DisplayMode_ThumbailsList_ButtonString "TL"
309#endif // DisplayMode_ThumbailsList_ButtonString
310#ifndef DisplayMode_ThumbailsList_ButtonHelp
311#define DisplayMode_ThumbailsList_ButtonHelp "Thumbnails List"
312#endif // DisplayMode_ThumbailsList_ButtonHelp
313#ifndef DisplayMode_ThumbailsGrid_ButtonString
314#define DisplayMode_ThumbailsGrid_ButtonString "TG"
315#endif // DisplayMode_ThumbailsGrid_ButtonString
316#ifndef DisplayMode_ThumbailsGrid_ButtonHelp
317#define DisplayMode_ThumbailsGrid_ButtonHelp "Thumbnails Grid"
318#endif // DisplayMode_ThumbailsGrid_ButtonHelp
319#ifndef DisplayMode_ThumbailsList_ImageHeight
320#define DisplayMode_ThumbailsList_ImageHeight 32.0f
321#endif // DisplayMode_ThumbailsList_ImageHeight
322#ifndef IMGUI_RADIO_BUTTON
323inline bool inRadioButton(const char* vLabel, bool vToggled) {
324 bool pressed = false;
325 if (vToggled) {
326 ImVec4 bua = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);
327 ImVec4 te = ImGui::GetStyleColorVec4(ImGuiCol_Text);
328 ImGui::PushStyleColor(ImGuiCol_Button, te);
329 ImGui::PushStyleColor(ImGuiCol_ButtonActive, te);
330 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, te);
331 ImGui::PushStyleColor(ImGuiCol_Text, bua);
332 }
333 pressed = IMGUI_BUTTON(vLabel);
334 if (vToggled) {
335 ImGui::PopStyleColor(4); //-V112
336 }
337 return pressed;
338}
339#define IMGUI_RADIO_BUTTON inRadioButton
340#endif // IMGUI_RADIO_BUTTON
341#endif // USE_THUMBNAILS
343// BOOKMARKS
345#ifdef USE_BOOKMARK
346#ifndef defaultBookmarkPaneWith
347#define defaultBookmarkPaneWith 150.0f
348#endif // defaultBookmarkPaneWith
349#ifndef bookmarksButtonString
350#define bookmarksButtonString "Bookmark"
351#endif // bookmarksButtonString
352#ifndef bookmarksButtonHelpString
353#define bookmarksButtonHelpString "Bookmark"
354#endif // bookmarksButtonHelpString
355#ifndef addBookmarkButtonString
356#define addBookmarkButtonString "+"
357#endif // addBookmarkButtonString
358#ifndef removeBookmarkButtonString
359#define removeBookmarkButtonString "-"
360#endif // removeBookmarkButtonString
361#ifndef IMGUI_TOGGLE_BUTTON
362inline bool inToggleButton(const char* vLabel, bool* vToggled) {
363 bool pressed = false;
364
365 if (vToggled && *vToggled) {
366 ImVec4 bua = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);
367 // ImVec4 buh = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered);
368 // ImVec4 bu = ImGui::GetStyleColorVec4(ImGuiCol_Button);
369 ImVec4 te = ImGui::GetStyleColorVec4(ImGuiCol_Text);
370 ImGui::PushStyleColor(ImGuiCol_Button, te);
371 ImGui::PushStyleColor(ImGuiCol_ButtonActive, te);
372 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, te);
373 ImGui::PushStyleColor(ImGuiCol_Text, bua);
374 }
375
376 pressed = IMGUI_BUTTON(vLabel);
377
378 if (vToggled && *vToggled) {
379 ImGui::PopStyleColor(4); //-V112
380 }
381
382 if (vToggled && pressed) *vToggled = !*vToggled;
383
384 return pressed;
385}
386#define IMGUI_TOGGLE_BUTTON inToggleButton
387#endif // IMGUI_TOGGLE_BUTTON
388#endif // USE_BOOKMARK
389
390//#pragma endregion
391
392//#pragma region INTERNAL
393
394//#pragma region EXCEPTION
395
396class IGFDException : public std::exception {
397private:
398 std::string m_Message;
399
400public:
401 IGFDException(const std::string& vMessage) : m_Message(vMessage) {
402 }
403 const char* what() {
404 return m_Message.c_str();
405 }
406};
407
408//#pragma endregion
409
410//#pragma region FILE SYSTEM INTERFACE
411
412#ifndef CUSTOM_FILESYSTEM_INCLUDE
413#ifdef USE_STD_FILESYSTEM
414
415static std::filesystem::path stringToPath(const std::string& str)
416{
417#ifdef _IGFD_WIN_
418 return std::filesystem::path(IGFD::Utils::UTF8Decode(str));
419#else
420 return std::filesystem::path(str);
421#endif
422}
423
424static std::string pathToString(const std::filesystem::path& path)
425{
426#ifdef _IGFD_WIN_
427 return IGFD::Utils::UTF8Encode(path.wstring());
428#else
429 return path.string();
430#endif
431}
432
433class FileSystemStd : public IGFD::IFileSystem {
434public:
435 bool IsDirectoryCanBeOpened(const std::string& vName) override {
436 bool bExists = false;
437 if (!vName.empty()) {
438 namespace fs = std::filesystem;
439 auto pathName = stringToPath(vName);
440 try {
441 // interesting, in the case of a protected dir or for any reason the dir cant be opened
442 // this func will work but will say nothing more . not like the dirent version
443 bExists = fs::is_directory(pathName);
444 // test if can be opened, this function can thrown an exception if there is an issue with this dir
445 // here, the dir_iter is need else not exception is thrown..
446 const auto dir_iter = fs::directory_iterator(pathName);
447 (void)dir_iter; // for avoid unused warnings
448 } catch (const std::exception& /*ex*/) {
449 // fail so this dir cant be opened
450 bExists = false;
451 }
452 }
453 return bExists; // this is not a directory!
454 }
455 bool IsDirectoryExist(const std::string& vName) override {
456 if (!vName.empty()) {
457 namespace fs = std::filesystem;
458 return fs::is_directory(stringToPath(vName));
459 }
460 return false; // this is not a directory!
461 }
462 bool IsFileExist(const std::string& vName) override {
463 namespace fs = std::filesystem;
464 return fs::is_regular_file(stringToPath(vName));
465 }
466 bool CreateDirectoryIfNotExist(const std::string& vName) override {
467 if (vName.empty()) return false;
468 if (IsDirectoryExist(vName)) return false; // TODO is this really an error? Should this be checked at all? (creating an existing directory is not an error)
469
470#if defined(__EMSCRIPTEN__)
471 std::string str = std::string("FS.mkdir('") + vName + "');";
472 emscripten_run_script(str.c_str());
473 bool res = true;
474#else
475 namespace fs = std::filesystem;
476 bool res = fs::create_directories(stringToPath(vName));
477#endif // _IGFD_WIN_
478 if (!res) {
479 std::cout << "Error creating directory " << vName << std::endl;
480 }
481 return res;
482 }
483 std::vector<std::string> GetDrivesList() override {
484 std::vector<std::string> res;
485#ifdef _IGFD_WIN_
486 const DWORD mydrives = 2048;
487 char lpBuffer[2048];
488#define mini(a, b) (((a) < (b)) ? (a) : (b))
489 const DWORD countChars = mini(GetLogicalDriveStringsA(mydrives, lpBuffer), 2047);
490#undef mini
491 if (countChars > 0U && countChars < 2049U) {
492 std::string var = std::string(lpBuffer, (size_t)countChars);
493 IGFD::Utils::ReplaceString(var, "\\", "");
494 res = IGFD::Utils::SplitStringToVector(var, '\0', false);
495 }
496#endif // _IGFD_WIN_
497 return res;
498 }
499
500 IGFD::Utils::PathStruct ParsePathFileName(const std::string& vPathFileName) override {
501 // https://github.com/aiekick/ImGuiFileDialog/issues/54
502 namespace fs = std::filesystem;
503 IGFD::Utils::PathStruct res;
504 if (vPathFileName.empty()) return res;
505 auto fsPath = stringToPath(vPathFileName);
506 if (fs::is_directory(fsPath)) {
507 res.name = "";
508 res.path = pathToString(fsPath);
509 res.isOk = true;
510 } else if (fs::is_regular_file(fsPath)) {
511 res.name = pathToString(fsPath.filename());
512 res.path = pathToString(fsPath.parent_path());
513 res.isOk = true;
514 }
515 return res;
516 }
517
518 std::vector<IGFD::FileInfos> ScanDirectory(const std::string& vPath) override {
519 std::vector<IGFD::FileInfos> res;
520 try {
521 namespace fs = std::filesystem;
522 auto fspath = stringToPath(vPath);
523 const auto dir_iter = fs::directory_iterator(fspath);
524 IGFD::FileType fstype = IGFD::FileType(IGFD::FileType::ContentType::Directory, fs::is_symlink(fs::status(fspath)));
525 {
526 IGFD::FileInfos file_two_dot;
527 file_two_dot.filePath = vPath;
528 file_two_dot.fileNameExt = "..";
529 file_two_dot.fileType = fstype;
530 res.push_back(file_two_dot);
531 }
532 for (const auto& file : dir_iter) {
533 IGFD::FileType fileType;
534 if (file.is_symlink()) {
535 fileType.SetSymLink(file.is_symlink());
536 fileType.SetContent(IGFD::FileType::ContentType::LinkToUnknown);
537 }
538 if (file.is_directory()) {
539 fileType.SetContent(IGFD::FileType::ContentType::Directory);
540 } // directory or symlink to directory
541 else if (file.is_regular_file()) {
542 fileType.SetContent(IGFD::FileType::ContentType::File);
543 }
544 if (fileType.isValid()) {
545 auto fileNameExt = pathToString(file.path().filename());
546 {
547 IGFD::FileInfos _file;
548 _file.filePath = vPath;
549 _file.fileNameExt = fileNameExt;
550 _file.fileType = fileType;
551 res.push_back(_file);
552 }
553 }
554 }
555 } catch (const std::exception& ex) {
556 printf("%s", ex.what());
557 }
558 return res;
559 }
560 bool IsDirectory(const std::string& vFilePathName) override {
561 namespace fs = std::filesystem;
562 return fs::is_directory(stringToPath(vFilePathName));
563 }
564};
565#define FILE_SYSTEM_OVERRIDE FileSystemStd
566#else
567class FileSystemDirent : public IGFD::IFileSystem {
568public:
569 bool IsDirectoryCanBeOpened(const std::string& vName) override {
570 if (!vName.empty()) {
571 DIR* pDir = nullptr;
572 // interesting, in the case of a protected dir or for any reason the dir cant be opened
573 // this func will fail
574 pDir = opendir(vName.c_str());
575 if (pDir != nullptr) {
576 (void)closedir(pDir);
577 return true;
578 }
579 }
580 return false;
581 }
582 bool IsDirectoryExist(const std::string& vName) override {
583 bool bExists = false;
584 if (!vName.empty()) {
585 DIR* pDir = nullptr;
586 pDir = opendir(vName.c_str());
587 if (pDir) {
588 bExists = true;
589 closedir(pDir);
590 } else if (ENOENT == errno) {
591 /* Directory does not exist. */
592 // bExists = false;
593 } else {
594 /* opendir() failed for some other reason.
595 like if a dir is protected, or not accessable with user right
596 */
597 bExists = true;
598 }
599 }
600 return bExists;
601 }
602 bool IsFileExist(const std::string& vName) override {
603 std::ifstream docFile(vName, std::ios::in);
604 if (docFile.is_open()) {
605 docFile.close();
606 return true;
607 }
608 return false;
609 }
610 bool CreateDirectoryIfNotExist(const std::string& vName) override {
611 bool res = false;
612 if (!vName.empty()) {
613 if (!IsDirectoryExist(vName)) {
614#ifdef _IGFD_WIN_
615 std::wstring wname = IGFD::Utils::UTF8Decode(vName);
616 if (CreateDirectoryW(wname.c_str(), nullptr)) {
617 res = true;
618 }
619#elif defined(__EMSCRIPTEN__) // _IGFD_WIN_
620 std::string str = std::string("FS.mkdir('") + vName + "');";
621 emscripten_run_script(str.c_str());
622 res = true;
623#elif defined(_IGFD_UNIX_)
624 char buffer[PATH_MAX] = {};
625 snprintf(buffer, PATH_MAX, "mkdir -p \"%s\"", vName.c_str());
626 const int dir_err = std::system(buffer);
627 if (dir_err != -1) {
628 res = true;
629 }
630#endif // _IGFD_WIN_
631 if (!res) {
632 std::cout << "Error creating directory " << vName << std::endl;
633 }
634 }
635 }
636
637 return res;
638 }
639
640 std::vector<std::string> GetDrivesList() override {
641 std::vector<std::string> res;
642#ifdef _IGFD_WIN_
643 const DWORD mydrives = 2048;
644 char lpBuffer[2048];
645#define mini(a, b) (((a) < (b)) ? (a) : (b))
646 const DWORD countChars = mini(GetLogicalDriveStringsA(mydrives, lpBuffer), 2047);
647#undef mini
648 if (countChars > 0U && countChars < 2049U) {
649 std::string var = std::string(lpBuffer, (size_t)countChars);
650 IGFD::Utils::ReplaceString(var, "\\", "");
651 res = IGFD::Utils::SplitStringToVector(var, '\0', false);
652 }
653#endif // _IGFD_WIN_
654 return res;
655 }
656
657 IGFD::Utils::PathStruct ParsePathFileName(const std::string& vPathFileName) override {
658 IGFD::Utils::PathStruct res;
659 if (!vPathFileName.empty()) {
660 std::string pfn = vPathFileName;
661 std::string separator(1u, PATH_SEP);
662 IGFD::Utils::ReplaceString(pfn, "\\", separator);
663 IGFD::Utils::ReplaceString(pfn, "/", separator);
664 size_t lastSlash = pfn.find_last_of(separator);
665 if (lastSlash != std::string::npos) {
666 res.name = pfn.substr(lastSlash + 1);
667 res.path = pfn.substr(0, lastSlash);
668 res.isOk = true;
669 }
670 size_t lastPoint = pfn.find_last_of('.');
671 if (lastPoint != std::string::npos) {
672 if (!res.isOk) {
673 res.name = pfn;
674 res.isOk = true;
675 }
676 res.ext = pfn.substr(lastPoint + 1);
677 IGFD::Utils::ReplaceString(res.name, "." + res.ext, "");
678 }
679 if (!res.isOk) {
680 res.name = std::move(pfn);
681 res.isOk = true;
682 }
683 }
684 return res;
685 }
686
687 std::vector<IGFD::FileInfos> ScanDirectory(const std::string& vPath) override {
688 std::vector<IGFD::FileInfos> res;
689 struct dirent** files = nullptr;
690 size_t n = scandir(vPath.c_str(), &files, nullptr, //
691 [](const struct dirent** a, const struct dirent** b) { //
692 return strcoll((*a)->d_name, (*b)->d_name);
693 });
694 if (n && files) {
695 for (size_t i = 0; i < n; ++i) {
696 struct dirent* ent = files[i];
697 IGFD::FileType fileType;
698 switch (ent->d_type) {
699 case DT_DIR: fileType.SetContent(IGFD::FileType::ContentType::Directory); break;
700 case DT_REG: fileType.SetContent(IGFD::FileType::ContentType::File); break;
701#if defined(_IGFD_UNIX_) || (DT_LNK != DT_UNKNOWN)
702 case DT_LNK:
703#endif
704 case DT_UNKNOWN: {
705 struct stat sb = {};
706#ifdef _IGFD_WIN_
707 auto filePath = vPath + ent->d_name;
708#else
709 auto filePath = vPath + IGFD::Utils::GetPathSeparator() + ent->d_name;
710#endif
711 if (!stat(filePath.c_str(), &sb)) {
712 if (sb.st_mode & S_IFLNK) {
713 fileType.SetSymLink(true);
714 // by default if we can't figure out the target type.
715 fileType.SetContent(IGFD::FileType::ContentType::LinkToUnknown);
716 }
717 if (sb.st_mode & S_IFREG) {
718 fileType.SetContent(IGFD::FileType::ContentType::File);
719 break;
720 } else if (sb.st_mode & S_IFDIR) {
721 fileType.SetContent(IGFD::FileType::ContentType::Directory);
722 break;
723 }
724 }
725 break;
726 }
727 default: break; // leave it invalid (devices, etc.)
728 }
729 if (fileType.isValid()) {
730 IGFD::FileInfos _file;
731 _file.filePath = vPath;
732 _file.fileNameExt = ent->d_name;
733 _file.fileType = fileType;
734 res.push_back(_file);
735 }
736 }
737 for (size_t i = 0; i < n; ++i) {
738 free(files[i]);
739 }
740 free(files);
741 }
742 return res;
743 }
744 bool IsDirectory(const std::string& vFilePathName) override {
745 DIR *pDir = opendir(vFilePathName.c_str());
746 if (pDir) {
747 (void)closedir(pDir);
748 return true;
749 }
750 return false;
751 }
752};
753#define FILE_SYSTEM_OVERRIDE FileSystemDirent
754#endif // USE_STD_FILESYSTEM
755#else
756#include CUSTOM_FILESYSTEM_INCLUDE
757#endif // USE_CUSTOM_FILESYSTEM
758
759//#pragma endregion
760
761//#pragma region Utils
762
763// https://github.com/ocornut/imgui/issues/1720
764bool IGFD::Utils::ImSplitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size) {
765 using namespace ImGui;
766 ImGuiContext& g = *GImGui;
767 ImGuiWindow* window = g.CurrentWindow;
768 ImGuiID id = window->GetID("##Splitter");
769 ImRect bb;
770 bb.Min = window->DC.CursorPos + (split_vertically ? ImVec2(*size1, 0.0f) : ImVec2(0.0f, *size1));
771 bb.Max = bb.Min + CalcItemSize(split_vertically ? ImVec2(thickness, splitter_long_axis_size) : ImVec2(splitter_long_axis_size, thickness), 0.0f, 0.0f);
772 return SplitterBehavior(bb, id, split_vertically ? ImGuiAxis_X : ImGuiAxis_Y, size1, size2, min_size1, min_size2, 1.0f);
773}
774
775// Convert a wide Unicode string to an UTF8 string
776std::string IGFD::Utils::UTF8Encode(const std::wstring& wstr) {
777 std::string res;
778#ifdef _IGFD_WIN_
779 if (!wstr.empty()) {
780 int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
781 if (size_needed) {
782 res = std::string(size_needed, 0);
783 WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &res[0], size_needed, NULL, NULL);
784 }
785 }
786#else
787 // Suppress warnings from the compiler.
788 (void)wstr;
789#endif // _IGFD_WIN_
790 return res;
791}
792
793// Convert an UTF8 string to a wide Unicode String
794std::wstring IGFD::Utils::UTF8Decode(const std::string& str) {
795 std::wstring res;
796#ifdef _IGFD_WIN_
797 if (!str.empty()) {
798 int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
799 if (size_needed) {
800 res = std::wstring(size_needed, 0);
801 MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &res[0], size_needed);
802 }
803 }
804#else
805 // Suppress warnings from the compiler.
806 (void)str;
807#endif // _IGFD_WIN_
808 return res;
809}
810
811bool IGFD::Utils::ReplaceString(std::string& str, const ::std::string& oldStr, const ::std::string& newStr, const size_t& vMaxRecursion) {
812 if (!str.empty() && oldStr != newStr) {
813 bool res = false;
814 size_t pos = 0;
815 bool found = false;
816 size_t max_recursion = vMaxRecursion;
817 do {
818 pos = str.find(oldStr, pos);
819 if (pos != std::string::npos) {
820 found = res = true;
821 str.replace(pos, oldStr.length(), newStr);
822 pos += newStr.length();
823 } else if (found && max_recursion > 0) { // recursion loop
824 found = false;
825 pos = 0;
826 --max_recursion;
827 }
828 } while (pos != std::string::npos);
829 return res;
830 }
831 return false;
832}
833
834std::vector<std::string> IGFD::Utils::SplitStringToVector(const std::string& vText, const char& vDelimiter, const bool& vPushEmpty) {
835 std::vector<std::string> arr;
836 if (!vText.empty()) {
837 size_t start = 0;
838 size_t end = vText.find(vDelimiter, start);
839 while (end != std::string::npos) {
840 auto token = vText.substr(start, end - start);
841 if (!token.empty() || (token.empty() && vPushEmpty)) { //-V728
842 arr.push_back(token);
843 }
844 start = end + 1;
845 end = vText.find(vDelimiter, start);
846 }
847 auto token = vText.substr(start);
848 if (!token.empty() || (token.empty() && vPushEmpty)) { //-V728
849 arr.push_back(token);
850 }
851 }
852 return arr;
853}
854
855void IGFD::Utils::AppendToBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr) {
856 std::string st = vStr;
857 size_t len = vBufferLen - 1u;
858 size_t slen = strlen(vBuffer);
859
860 if (!st.empty() && st != "\n") {
861 IGFD::Utils::ReplaceString(st, "\n", "");
862 IGFD::Utils::ReplaceString(st, "\r", "");
863 }
864 vBuffer[slen] = '\0';
865 std::string str = std::string(vBuffer);
866 // if (!str.empty()) str += "\n";
867 str += vStr;
868 if (len > str.size()) {
869 len = str.size();
870 }
871#ifdef _MSC_VER
872 strncpy_s(vBuffer, vBufferLen, str.c_str(), len);
873#else // _MSC_VER
874 strncpy(vBuffer, str.c_str(), len);
875#endif // _MSC_VER
876 vBuffer[len] = '\0';
877}
878
879void IGFD::Utils::ResetBuffer(char* vBuffer) {
880 vBuffer[0] = '\0';
881}
882
883void IGFD::Utils::SetBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr) {
884 ResetBuffer(vBuffer);
885 AppendToBuffer(vBuffer, vBufferLen, vStr);
886}
887
888std::string IGFD::Utils::LowerCaseString(const std::string& vString) {
889 auto str = vString;
890
891 // convert to lower case
892 for (char& c : str) {
893 c = (char)std::tolower(c);
894 }
895
896 return str;
897}
898
899size_t IGFD::Utils::GetCharCountInString(const std::string& vString, const char& vChar) {
900 size_t res = 0U;
901 for (const auto& c : vString) {
902 if (c == vChar) {
903 ++res;
904 }
905 }
906 return res;
907}
908
909size_t IGFD::Utils::GetLastCharPosWithMinCharCount(const std::string& vString, const char& vChar, const size_t& vMinCharCount) {
910 if (vMinCharCount) {
911 size_t last_dot_pos = vString.size() + 1U;
912 size_t count_dots = vMinCharCount;
913 while (count_dots > 0U && last_dot_pos > 0U && last_dot_pos != std::string::npos) {
914 auto new_dot = vString.rfind(vChar, last_dot_pos - 1U);
915 if (new_dot != std::string::npos) {
916 last_dot_pos = new_dot;
917 --count_dots;
918 } else {
919 break;
920 }
921 }
922 return last_dot_pos;
923 }
924 return std::string::npos;
925}
926
927std::string IGFD::Utils::GetPathSeparator() {
928 return std::string(1U, PATH_SEP);
929}
930
931std::string IGFD::Utils::RoundNumber(double vvalue, int n) {
932 std::stringstream tmp;
933 tmp << std::setprecision(n) << std::fixed << vvalue;
934 return tmp.str();
935}
936
937std::pair<std::string, std::string> IGFD::Utils::FormatFileSize(size_t vByteSize) {
938 if (vByteSize != 0) {
939 static double lo = 1024.0;
940 static double ko = 1024.0 * 1024.0;
941 static double mo = 1024.0 * 1024.0 * 1024.0;
942
943 auto v = (double)vByteSize;
944
945 if (v < lo)
946 return {RoundNumber(v, 0), fileSizeBytes}; // octet
947 else if (v < ko)
948 return {RoundNumber(v / lo, 2), fileSizeKiloBytes}; // ko
949 else if (v < mo)
950 return {RoundNumber(v / ko, 2), fileSizeMegaBytes}; // Mo
951 else
952 return {RoundNumber(v / mo, 2), fileSizeGigaBytes}; // Go
953 }
954
955 return {"0", fileSizeBytes};
956}
957
958//#pragma endregion
959
960//#pragma region FileStyle
961
962IGFD::FileStyle::FileStyle() : color(0, 0, 0, 0) {
963}
964
965IGFD::FileStyle::FileStyle(const FileStyle& vStyle) {
966 color = vStyle.color;
967 icon = vStyle.icon;
968 font = vStyle.font;
969 flags = vStyle.flags;
970}
971
972IGFD::FileStyle::FileStyle(const ImVec4& vColor, const std::string& vIcon, ImFont* vFont) : color(vColor), icon(vIcon), font(vFont) {
973}
974
975//#pragma endregion
976
977//#pragma region SearchManager
978
979void IGFD::SearchManager::Clear() {
980 searchTag.clear();
981 IGFD::Utils::ResetBuffer(searchBuffer);
982}
983
984void IGFD::SearchManager::DrawSearchBar(FileDialogInternal& vFileDialogInternal) {
985 // search field
986 if (IMGUI_BUTTON(resetButtonString "##BtnImGuiFileDialogSearchField")) {
987 Clear();
988 vFileDialogInternal.fileManager.ApplyFilteringOnFileList(vFileDialogInternal);
989 }
990 if (ImGui::IsItemHovered()) ImGui::SetTooltip(buttonResetSearchString);
991 ImGui::SameLine();
992 ImGui::Text(searchString);
993 ImGui::SameLine();
994 ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
995 bool edited = ImGui::InputText("##InputImGuiFileDialogSearchField", searchBuffer, MAX_FILE_DIALOG_NAME_BUFFER);
996 if (ImGui::GetItemID() == ImGui::GetActiveID()) searchInputIsActive = true;
997 ImGui::PopItemWidth();
998 if (edited) {
999 searchTag = searchBuffer;
1000 vFileDialogInternal.fileManager.ApplyFilteringOnFileList(vFileDialogInternal);
1001 }
1002}
1003
1004//#pragma endregion
1005
1006//#pragma region FilterInfos
1007
1008void IGFD::FilterInfos::setCollectionTitle(const std::string& vTitle) {
1009 title = vTitle;
1010}
1011
1012void IGFD::FilterInfos::addFilter(const std::string& vFilter, const bool& vIsRegex) {
1013 setCollectionTitle(vFilter);
1014 addCollectionFilter(vFilter, vIsRegex);
1015}
1016
1017void IGFD::FilterInfos::addCollectionFilter(const std::string& vFilter, const bool& vIsRegex) {
1018 if (!vIsRegex) {
1019 if (vFilter.find('*') != std::string::npos) {
1020 const auto& regex_string = transformAsteriskBasedFilterToRegex(vFilter);
1021 addCollectionFilter(regex_string, true);
1022 return;
1023 }
1024 filters.try_add(vFilter);
1025 filters_optimized.try_add(Utils::LowerCaseString(vFilter));
1026 auto _count_dots = Utils::GetCharCountInString(vFilter, '.');
1027 if (_count_dots > count_dots) {
1028 count_dots = _count_dots;
1029 }
1030 } else {
1031 try {
1032 auto rx = std::regex(vFilter);
1033 filters.try_add(vFilter);
1034 filters_regex.emplace_back(rx);
1035 } catch (std::exception&) {
1036 assert(0); // YOUR REGEX FILTER IS INVALID
1037 }
1038 }
1039}
1040
1041void IGFD::FilterInfos::clear() {
1042 title.clear();
1043 filters.clear();
1044 filters_optimized.clear();
1045 filters_regex.clear();
1046}
1047
1048bool IGFD::FilterInfos::empty() const {
1049 return filters.empty() || filters.begin()->empty();
1050}
1051
1052const std::string& IGFD::FilterInfos::getFirstFilter() const {
1053 if (!filters.empty()) {
1054 return *filters.begin();
1055 }
1056 return empty_string;
1057}
1058
1059bool IGFD::FilterInfos::exist(const FileInfos& vFileInfos, bool vIsCaseInsensitive) const {
1060 for (const auto& filter : filters) {
1061 if (vFileInfos.SearchForExt(filter, vIsCaseInsensitive, count_dots)) {
1062 return true;
1063 }
1064 }
1065 return false;
1066}
1067
1068bool IGFD::FilterInfos::regexExist(const std::string& vFilter) const {
1069 for (auto regex : filters_regex) {
1070 if (std::regex_search(vFilter, regex)) {
1071 return true;
1072 }
1073 }
1074 return false;
1075}
1076
1077std::string IGFD::FilterInfos::transformAsteriskBasedFilterToRegex(const std::string& vFilter) {
1078 std::string res;
1079 if (!vFilter.empty() && vFilter.find('*') != std::string::npos) {
1080 res = "((";
1081 for (const auto& c : vFilter) {
1082 if (c == '.') {
1083 res += "[.]"; // [.] => a dot
1084 } else if (c == '*') {
1085 res += ".*"; // .* => any char zero or many
1086 } else {
1087 res += c; // other chars
1088 }
1089 }
1090 res += "$))"; // $ => end fo the string
1091 }
1092 return res;
1093}
1094
1095//#pragma endregion
1096
1097//#pragma region FilterManager
1098
1099const IGFD::FilterInfos& IGFD::FilterManager::GetSelectedFilter() const {
1100 return m_SelectedFilter;
1101}
1102
1103void IGFD::FilterManager::ParseFilters(const char* vFilters) {
1104 m_ParsedFilters.clear();
1105
1106 if (vFilters) {
1107 dLGFilters = vFilters; // file mode
1108 } else {
1109 dLGFilters.clear(); // directory mode
1110 }
1111
1112 if (!dLGFilters.empty()) {
1113 /* Rules
1114 0) a filter must have 2 chars mini and the first must be a .
1115 1) a regex must be in (( and ))
1116 2) a , will separate filters except if between a ( and )
1117 3) name{filter1, filter2} is a spetial form for collection filters
1118 3.1) the name can be composed of what you want except { and }
1119 3.2) the filter can be a regex
1120 4) the filters cannot integrate these chars '(' ')' '{' '}' ' ' except for a regex with respect to rule 1)
1121 5) the filters cannot integrate a ','
1122 */
1123
1124 bool current_filter_found = false;
1125 bool started = false;
1126 bool regex_started = false;
1127 bool parenthesis_started = false;
1128
1129 std::string word;
1130 std::string filter_name;
1131
1132 char last_split_char = 0;
1133 for (char c : dLGFilters) {
1134 if (c == '{') {
1135 if (regex_started) {
1136 word += c;
1137 } else {
1138 started = true;
1139 m_ParsedFilters.emplace_back();
1140 m_ParsedFilters.back().setCollectionTitle(filter_name);
1141 filter_name.clear();
1142 word.clear();
1143 }
1144 last_split_char = c;
1145 } else if (c == '}') {
1146 if (regex_started) {
1147 word += c;
1148 } else {
1149 if (started) {
1150 if (word.size() > 1U && word[0] == '.') {
1151 if (m_ParsedFilters.empty()) {
1152 m_ParsedFilters.emplace_back();
1153 }
1154 m_ParsedFilters.back().addCollectionFilter(word, false);
1155 }
1156 word.clear();
1157 filter_name.clear();
1158 started = false;
1159 }
1160 }
1161 last_split_char = c;
1162 } else if (c == '(') {
1163 word += c;
1164 if (last_split_char == '(') {
1165 regex_started = true;
1166 }
1167 parenthesis_started = true;
1168 if (!started) {
1169 filter_name += c;
1170 }
1171 last_split_char = c;
1172 } else if (c == ')') {
1173 word += c;
1174 if (last_split_char == ')') {
1175 if (regex_started) {
1176 if (started) {
1177 m_ParsedFilters.back().addCollectionFilter(word, true);
1178 } else {
1179 m_ParsedFilters.emplace_back();
1180 m_ParsedFilters.back().addFilter(word, true);
1181 }
1182 word.clear();
1183 filter_name.clear();
1184 regex_started = false;
1185 } else {
1186 if (!started) {
1187 if (!m_ParsedFilters.empty()) {
1188 m_ParsedFilters.erase(m_ParsedFilters.begin() + m_ParsedFilters.size() - 1U);
1189 } else {
1190 m_ParsedFilters.clear();
1191 }
1192 }
1193 word.clear();
1194 filter_name.clear();
1195 }
1196 }
1197 parenthesis_started = false;
1198 if (!started) {
1199 filter_name += c;
1200 }
1201 last_split_char = c;
1202 } else if (c == '.') {
1203 word += c;
1204 if (!started) {
1205 filter_name += c;
1206 }
1207 last_split_char = c;
1208 } else if (c == ',') {
1209 if (regex_started) {
1210 regex_started = false;
1211 word.clear();
1212 filter_name.clear();
1213 } else {
1214 if (started) {
1215 if (word.size() > 1U && word[0] == '.') {
1216 m_ParsedFilters.back().addCollectionFilter(word, false);
1217 word.clear();
1218 filter_name.clear();
1219 }
1220 } else {
1221 if (word.size() > 1U && word[0] == '.') {
1222 m_ParsedFilters.emplace_back();
1223 m_ParsedFilters.back().addFilter(word, false);
1224 word.clear();
1225 filter_name.clear();
1226 }
1227 if (parenthesis_started) {
1228 filter_name += c;
1229 }
1230 }
1231 }
1232 } else {
1233 if (c != ' ') {
1234 word += c;
1235 }
1236 if (!started) {
1237 filter_name += c;
1238 }
1239 }
1240 }
1241
1242 if (started) {
1243 if (!m_ParsedFilters.empty()) {
1244 m_ParsedFilters.erase(m_ParsedFilters.begin() + m_ParsedFilters.size() - 1U);
1245 } else {
1246 m_ParsedFilters.clear();
1247 }
1248 } else if (word.size() > 1U && word[0] == '.') {
1249 m_ParsedFilters.emplace_back();
1250 m_ParsedFilters.back().addFilter(word, false);
1251 word.clear();
1252 }
1253
1254 for (const auto& it : m_ParsedFilters) {
1255 if (it.title == m_SelectedFilter.title) {
1256 m_SelectedFilter = it;
1257 current_filter_found = true;
1258 break;
1259 }
1260 }
1261
1262 if (!current_filter_found) {
1263 if (!m_ParsedFilters.empty()) {
1264 m_SelectedFilter = *m_ParsedFilters.begin();
1265 }
1266 }
1267 }
1268}
1269
1270void IGFD::FilterManager::SetSelectedFilterWithExt(const std::string& vFilter) {
1271 if (!m_ParsedFilters.empty()) {
1272 if (!vFilter.empty()) {
1273 for (const auto& infos : m_ParsedFilters) {
1274 for (const auto& filter : infos.filters) {
1275 if (vFilter == filter) {
1276 m_SelectedFilter = infos;
1277 }
1278 }
1279 }
1280 }
1281
1282 if (m_SelectedFilter.empty()) {
1283 m_SelectedFilter = *m_ParsedFilters.begin();
1284 }
1285 }
1286}
1287
1288void IGFD::FilterManager::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const FileStyle& vInfos) {
1289 std::string _criteria = (vCriteria != nullptr) ? std::string(vCriteria) : "";
1290 m_FilesStyle[vFlags][_criteria] = std::make_shared<FileStyle>(vInfos);
1291 m_FilesStyle[vFlags][_criteria]->flags = vFlags;
1292}
1293
1294// will be called internally
1295// will not been exposed to IGFD API
1296bool IGFD::FilterManager::m_FillFileStyle(std::shared_ptr<FileInfos> vFileInfos) const {
1297 // todo : better system to found regarding what style to priorize regarding other
1298 // maybe with a lambda fucntion for let the user use his style
1299 // according to his use case
1300 if (vFileInfos.use_count() && !m_FilesStyle.empty()) {
1301 for (const auto& _flag : m_FilesStyle) {
1302 for (const auto& _file : _flag.second) {
1303 if ((_flag.first & IGFD_FileStyleByTypeDir && _flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType.isDir() && vFileInfos->fileType.isSymLink()) ||
1304 (_flag.first & IGFD_FileStyleByTypeFile && _flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType.isFile() && vFileInfos->fileType.isSymLink()) ||
1305 (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType.isSymLink()) || (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType.isDir()) ||
1306 (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType.isFile())) {
1307 if (_file.first.empty()) { // for all links
1308 vFileInfos->fileStyle = _file.second;
1309 } else if (_file.first.find("((") != std::string::npos && std::regex_search(vFileInfos->fileNameExt,
1310 std::regex(_file.first))) { // for links who are equal to style criteria
1311 vFileInfos->fileStyle = _file.second;
1312 } else if (_file.first == vFileInfos->fileNameExt) { // for links who are equal to style criteria
1313 vFileInfos->fileStyle = _file.second;
1314 }
1315 }
1316
1317 if (_flag.first & IGFD_FileStyleByExtention) {
1318 if (_file.first.find("((") != std::string::npos && std::regex_search(vFileInfos->fileExtLevels[0], std::regex(_file.first))) {
1319 vFileInfos->fileStyle = _file.second;
1320 } else if (vFileInfos->SearchForExt(_file.first, false)) {
1321 vFileInfos->fileStyle = _file.second;
1322 }
1323 }
1324
1325 if (_flag.first & IGFD_FileStyleByFullName) {
1326 if (_file.first.find("((") != std::string::npos && std::regex_search(vFileInfos->fileNameExt, std::regex(_file.first))) {
1327 vFileInfos->fileStyle = _file.second;
1328 } else if (_file.first == vFileInfos->fileNameExt) {
1329 vFileInfos->fileStyle = _file.second;
1330 }
1331 }
1332
1333 if (_flag.first & IGFD_FileStyleByContainedInFullName) {
1334 if (_file.first.find("((") != std::string::npos && std::regex_search(vFileInfos->fileNameExt, std::regex(_file.first))) {
1335 vFileInfos->fileStyle = _file.second;
1336 } else if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos) {
1337 vFileInfos->fileStyle = _file.second;
1338 }
1339 }
1340
1341 for (auto& functor : m_FilesStyleFunctors) {
1342 if (functor) {
1343 FileStyle result;
1344 if (functor(*(vFileInfos.get()), result)) {
1345 vFileInfos->fileStyle = std::make_shared<FileStyle>(std::move(result));
1346 }
1347 }
1348 }
1349
1350 if (vFileInfos->fileStyle.use_count()) {
1351 return true;
1352 }
1353 }
1354 }
1355 }
1356
1357 return false;
1358}
1359
1360void IGFD::FilterManager::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const ImVec4& vColor, const std::string& vIcon, ImFont* vFont) {
1361 std::string _criteria;
1362 if (vCriteria) _criteria = std::string(vCriteria);
1363 m_FilesStyle[vFlags][_criteria] = std::make_shared<FileStyle>(vColor, vIcon, vFont);
1364 m_FilesStyle[vFlags][_criteria]->flags = vFlags;
1365}
1366
1367void IGFD::FilterManager::SetFileStyle(FileStyle::FileStyleFunctor vFunctor) {
1368 if (vFunctor) {
1369 m_FilesStyleFunctors.push_back(vFunctor);
1370 }
1371}
1372
1373// todo : refactor this fucking function
1374bool IGFD::FilterManager::GetFileStyle(const IGFD_FileStyleFlags& vFlags, const std::string& vCriteria, ImVec4* vOutColor, std::string* vOutIcon, ImFont** vOutFont) {
1375 if (vOutColor) {
1376 if (!m_FilesStyle.empty()) {
1377 if (m_FilesStyle.find(vFlags) != m_FilesStyle.end()) { // found
1378 if (vFlags & IGFD_FileStyleByContainedInFullName) {
1379 // search for vCriteria who are containing the criteria
1380 for (const auto& _file : m_FilesStyle.at(vFlags)) {
1381 if (vCriteria.find(_file.first) != std::string::npos) {
1382 if (_file.second.use_count()) {
1383 *vOutColor = _file.second->color;
1384 if (vOutIcon) *vOutIcon = _file.second->icon;
1385 if (vOutFont) *vOutFont = _file.second->font;
1386 return true;
1387 }
1388 }
1389 }
1390 } else {
1391 if (m_FilesStyle.at(vFlags).find(vCriteria) != m_FilesStyle.at(vFlags).end()) { // found
1392 *vOutColor = m_FilesStyle[vFlags][vCriteria]->color;
1393 if (vOutIcon) *vOutIcon = m_FilesStyle[vFlags][vCriteria]->icon;
1394 if (vOutFont) *vOutFont = m_FilesStyle[vFlags][vCriteria]->font;
1395 return true;
1396 }
1397 }
1398 } else {
1399 // search for flag composition
1400 for (const auto& _flag : m_FilesStyle) {
1401 if (_flag.first & vFlags) {
1402 if (_flag.first & IGFD_FileStyleByContainedInFullName) {
1403 // search for vCriteria who are containing the criteria
1404 for (const auto& _file : m_FilesStyle.at(_flag.first)) {
1405 if (vCriteria.find(_file.first) != std::string::npos) {
1406 if (_file.second.use_count()) {
1407 *vOutColor = _file.second->color;
1408 if (vOutIcon) *vOutIcon = _file.second->icon;
1409 if (vOutFont) *vOutFont = _file.second->font;
1410 return true;
1411 }
1412 }
1413 }
1414 } else {
1415 if (m_FilesStyle.at(_flag.first).find(vCriteria) != m_FilesStyle.at(_flag.first).end()) { // found
1416 *vOutColor = m_FilesStyle[_flag.first][vCriteria]->color;
1417 if (vOutIcon) *vOutIcon = m_FilesStyle[_flag.first][vCriteria]->icon;
1418 if (vOutFont) *vOutFont = m_FilesStyle[_flag.first][vCriteria]->font;
1419 return true;
1420 }
1421 }
1422 }
1423 }
1424 }
1425 }
1426 }
1427 return false;
1428}
1429
1430void IGFD::FilterManager::ClearFilesStyle() {
1431 m_FilesStyle.clear();
1432}
1433
1434bool IGFD::FilterManager::IsCoveredByFilters(const FileInfos& vFileInfos, bool vIsCaseInsensitive) const {
1435 if (!dLGFilters.empty() && !m_SelectedFilter.empty()) {
1436 return (m_SelectedFilter.exist(vFileInfos, vIsCaseInsensitive) || m_SelectedFilter.regexExist(vFileInfos.fileNameExt));
1437 }
1438
1439 return false;
1440}
1441
1442float IGFD::FilterManager::GetFilterComboBoxWidth() const {
1443#if FILTER_COMBO_AUTO_SIZE
1444 const auto& combo_width = ImGui::CalcTextSize(m_SelectedFilter.title.c_str()).x + ImGui::GetFrameHeight() + ImGui::GetStyle().ItemInnerSpacing.x;
1445 return ImMax(combo_width, FILTER_COMBO_MIN_WIDTH);
1446#else
1447 return FILTER_COMBO_MIN_WIDTH;
1448#endif
1449}
1450
1451bool IGFD::FilterManager::DrawFilterComboBox(FileDialogInternal& vFileDialogInternal) {
1452 if (!dLGFilters.empty()) {
1453 ImGui::SameLine();
1454 bool needToApllyNewFilter = false;
1455 ImGui::PushItemWidth(GetFilterComboBoxWidth());
1456 if (IMGUI_BEGIN_COMBO("##Filters", m_SelectedFilter.title.c_str(), ImGuiComboFlags_None)) {
1457 intptr_t i = 0;
1458 for (const auto& filter : m_ParsedFilters) {
1459 const bool item_selected = (filter.title == m_SelectedFilter.title);
1460 ImGui::PushID((void*)(intptr_t)i++);
1461 if (ImGui::Selectable(filter.title.c_str(), item_selected)) {
1462 m_SelectedFilter = filter;
1463 needToApllyNewFilter = true;
1464 }
1465 ImGui::PopID();
1466 }
1467 ImGui::EndCombo();
1468 }
1469 ImGui::PopItemWidth();
1470 if (needToApllyNewFilter) {
1471 vFileDialogInternal.fileManager.OpenCurrentPath(vFileDialogInternal);
1472 }
1473 return needToApllyNewFilter;
1474 }
1475 return false;
1476}
1477
1478std::string IGFD::FilterManager::ReplaceExtentionWithCurrentFilterIfNeeded(const std::string& vFileName, IGFD_ResultMode vFlag) const {
1479 auto result = vFileName;
1480 if (!result.empty()) {
1481 const auto& current_filter = m_SelectedFilter.getFirstFilter();
1482 if (!current_filter.empty()) {
1483 Utils::ReplaceString(result, "..", ".");
1484
1485 // is a regex => no change
1486 if (current_filter.find("((") != std::string::npos) {
1487 return result;
1488 }
1489
1490 // contain .* => no change
1491 if (current_filter.find(".*") != std::string::npos) {
1492 return result;
1493 }
1494
1495 switch (vFlag) {
1496 case IGFD_ResultMode_KeepInputFile: {
1497 return vFileName;
1498 }
1499 case IGFD_ResultMode_OverwriteFileExt: {
1500 const auto& count_dots = Utils::GetCharCountInString(vFileName, '.');
1501 const auto& min_dots = ImMin<size_t>(count_dots, m_SelectedFilter.count_dots);
1502 const auto& lp = Utils::GetLastCharPosWithMinCharCount(vFileName, '.', min_dots);
1503 if (lp != std::string::npos) { // there is a user extention
1504 const auto& file_name_without_user_ext = vFileName.substr(0, lp);
1505 result = file_name_without_user_ext + current_filter;
1506 } else { // add extention
1507 result = vFileName + current_filter;
1508 }
1509 break;
1510 }
1511 case IGFD_ResultMode_AddIfNoFileExt: {
1512 const auto& count_dots = Utils::GetCharCountInString(vFileName, '.');
1513 const auto& min_dots = ImMin<size_t>(count_dots, m_SelectedFilter.count_dots);
1514 const auto& lp = Utils::GetLastCharPosWithMinCharCount(vFileName, '.', min_dots);
1515 if (lp == std::string::npos || // there is no user extention
1516 lp == (vFileName.size() - 1U)) { // or this pos is also the last char => considered like no user extention
1517 const auto& file_name_without_user_ext = vFileName.substr(0, lp);
1518 result = file_name_without_user_ext + current_filter;
1519 }
1520 break;
1521 }
1522 }
1523
1524 Utils::ReplaceString(result, "..", ".");
1525 }
1526 }
1527 return result;
1528}
1529
1530void IGFD::FilterManager::SetDefaultFilterIfNotDefined() {
1531 if (m_SelectedFilter.empty() && // no filter selected
1532 !m_ParsedFilters.empty()) { // filter exist
1533 m_SelectedFilter = *m_ParsedFilters.begin(); // we take the first filter
1534 }
1535}
1536
1537//#pragma endregion
1538
1539//#pragma region FileType
1540
1541IGFD::FileType::FileType() = default;
1542IGFD::FileType::FileType(const ContentType& vContentType, const bool& vIsSymlink) : m_Content(vContentType), m_Symlink(vIsSymlink) {
1543}
1544void IGFD::FileType::SetContent(const ContentType& vContentType) {
1545 m_Content = vContentType;
1546}
1547void IGFD::FileType::SetSymLink(const bool& vIsSymlink) {
1548 m_Symlink = vIsSymlink;
1549}
1550bool IGFD::FileType::isValid() const {
1551 return m_Content != ContentType::Invalid;
1552}
1553bool IGFD::FileType::isDir() const {
1554 return m_Content == ContentType::Directory;
1555}
1556bool IGFD::FileType::isFile() const {
1557 return m_Content == ContentType::File;
1558}
1559bool IGFD::FileType::isLinkToUnknown() const {
1560 return m_Content == ContentType::LinkToUnknown;
1561}
1562bool IGFD::FileType::isSymLink() const {
1563 return m_Symlink;
1564}
1565// Comparisons only care about the content type, ignoring whether it's a symlink or not.
1566bool IGFD::FileType::operator==(const FileType& rhs) const {
1567 return m_Content == rhs.m_Content;
1568}
1569bool IGFD::FileType::operator!=(const FileType& rhs) const {
1570 return m_Content != rhs.m_Content;
1571}
1572bool IGFD::FileType::operator<(const FileType& rhs) const {
1573 return m_Content < rhs.m_Content;
1574}
1575bool IGFD::FileType::operator>(const FileType& rhs) const {
1576 return m_Content > rhs.m_Content;
1577}
1578
1579//#pragma endregion
1580
1581//#pragma region FileInfos
1582
1583bool IGFD::FileInfos::SearchForTag(const std::string& vTag) const {
1584 if (!vTag.empty()) {
1585 if (fileNameExt_optimized == "..") return true;
1586 return fileNameExt_optimized.find(vTag) != std::string::npos || // first try without case and accents
1587 fileNameExt.find(vTag) != std::string::npos; // second if searched with case and accents
1588 }
1589
1590 // if tag is empty => its a special case but all is found
1591 return true;
1592}
1593
1594bool IGFD::FileInfos::SearchForExt(const std::string& vExt, const bool& vIsCaseInsensitive, const size_t& vMaxLevel) const {
1595 if (!vExt.empty()) {
1596 const auto& ext_to_check = vIsCaseInsensitive ? Utils::LowerCaseString(vExt) : vExt;
1597 const auto& ext_levels = vIsCaseInsensitive ? fileExtLevels_optimized : fileExtLevels;
1598 if (vMaxLevel >= 1 && countExtDot >= vMaxLevel) {
1599 for (const auto& ext : ext_levels) {
1600 if (!ext.empty() && ext == ext_to_check) {
1601 return true;
1602 }
1603 }
1604 } else {
1605 return (fileExtLevels[0] == vExt);
1606 }
1607 }
1608 return false;
1609}
1610
1611bool IGFD::FileInfos::SearchForExts(const std::string& vComaSepExts, const bool& vIsCaseInsensitive, const size_t& vMaxLevel) const {
1612 if (!vComaSepExts.empty()) {
1613 const auto& arr = Utils::SplitStringToVector(vComaSepExts, ',', false);
1614 for (const auto& a : arr) {
1615 if (SearchForExt(a, vIsCaseInsensitive, vMaxLevel)) {
1616 return true;
1617 }
1618 }
1619 }
1620 return false;
1621}
1622
1623bool IGFD::FileInfos::FinalizeFileTypeParsing(const size_t& vMaxDotToExtract) {
1624 if (fileType.isFile() || fileType.isLinkToUnknown()) { // link can have the same extention of a file
1625 countExtDot = Utils::GetCharCountInString(fileNameExt, '.');
1626 size_t lpt = 0U;
1627 if (countExtDot > 1U) { // multi layer ext
1628 size_t max_dot_to_extract = vMaxDotToExtract;
1629 if (max_dot_to_extract > countExtDot) {
1630 max_dot_to_extract = countExtDot;
1631 }
1632 lpt = Utils::GetLastCharPosWithMinCharCount(fileNameExt, '.', max_dot_to_extract);
1633 } else {
1634 lpt = fileNameExt.find_first_of('.');
1635 }
1636 if (lpt != std::string::npos) {
1637 size_t lvl = 0U;
1638 fileNameLevels[lvl] = fileNameExt.substr(0, lpt);
1639 fileNameLevels[lvl] = Utils::LowerCaseString(fileNameLevels[lvl]);
1640 fileExtLevels[lvl] = fileNameExt.substr(lpt);
1641 fileExtLevels_optimized[lvl] = Utils::LowerCaseString(fileExtLevels[lvl]);
1642 if (countExtDot > 1U) { // multi layer ext
1643 auto count = countExtDot;
1644 while (count > 0 && lpt != std::string::npos && lvl < fileExtLevels.size()) {
1645 ++lpt, ++lvl;
1646 if (fileNameExt.size() > lpt) {
1647 lpt = fileNameExt.find_first_of('.', lpt);
1648 if (lpt != std::string::npos) {
1649 fileNameLevels[lvl] = fileNameExt.substr(0, lpt);
1650 fileNameLevels[lvl] = Utils::LowerCaseString(fileNameLevels[lvl]);
1651 fileExtLevels[lvl] = fileNameExt.substr(lpt);
1652 fileExtLevels_optimized[lvl] = Utils::LowerCaseString(fileExtLevels[lvl]);
1653 }
1654 }
1655 }
1656 }
1657 }
1658 return true;
1659 }
1660 return false;
1661}
1662
1663//#pragma endregion
1664
1665//#pragma region FileManager
1666
1667IGFD::FileManager::FileManager() {
1668 fsRoot = IGFD::Utils::GetPathSeparator();
1669 m_FileSystemName = typeid(FILE_SYSTEM_OVERRIDE).name();
1670 // std::make_unique is not available un cpp11
1671 m_FileSystemPtr = std::unique_ptr<FILE_SYSTEM_OVERRIDE>(new FILE_SYSTEM_OVERRIDE());
1672 // m_FileSystemPtr = std::make_unique<FILE_SYSTEM_OVERRIDE>();
1673}
1674
1675void IGFD::FileManager::OpenCurrentPath(const FileDialogInternal& vFileDialogInternal) {
1676 showDrives = false;
1677 ClearComposer();
1678 ClearFileLists();
1679 if (dLGDirectoryMode) { // directory mode
1680 SetDefaultFileName(".");
1681 } else {
1682 SetDefaultFileName(dLGDefaultFileName);
1683 }
1684 ScanDir(vFileDialogInternal, GetCurrentPath());
1685}
1686
1687void IGFD::FileManager::SortFields(const FileDialogInternal& vFileDialogInternal) {
1688 m_SortFields(vFileDialogInternal, m_FileList, m_FilteredFileList);
1689}
1690
1691void IGFD::FileManager::m_SortFields(const FileDialogInternal& vFileDialogInternal, std::vector<std::shared_ptr<FileInfos> >& vFileInfosList, std::vector<std::shared_ptr<FileInfos> >& vFileInfosFilteredList) {
1692 static constexpr const char* defaultHeaderString[] = {
1693 tableHeaderFileNameString,
1694 tableHeaderFileTypeString,
1695 tableHeaderFileSizeString,
1696 tableHeaderFileDateString,
1697#ifdef USE_THUMBNAILS
1698 tableHeaderFileThumbnailsString,
1699#endif // #ifdef USE_THUMBNAILS
1700 };
1701 for (int i = 0; i < SortingFieldEnum::NUM_FIELDS; ++i) {
1702 header[i] = defaultHeaderString[i];
1703 }
1704 bool ascending = sortingDirection[sortingField];
1705#ifdef USE_CUSTOM_SORTING_ICON
1706 header[sortingField] = (ascending ? tableHeaderAscendingIcon : tableHeaderDescendingIcon)
1707 + header[sortingField];
1708#endif
1709
1710 std::stable_sort(vFileInfosList.begin(), vFileInfosList.end(), [&](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool {
1711 bool a_used = a.use_count() != 0;
1712 bool b_used = b.use_count() != 0;
1713 if (a_used != b_used) return a_used < b_used;
1714 if (!a_used) return true; // neither is used
1715
1716 // We want ".." ALWAYS at the top
1717 bool a_not_dotdot = a->fileNameExt != "..";
1718 bool b_not_dotdot = b->fileNameExt != "..";
1719 if (a_not_dotdot != b_not_dotdot) return a_not_dotdot < b_not_dotdot;
1720
1721 auto compare = [&](auto extractor) {
1722 // directories before (or after) files
1723 auto a_type = a->fileType;
1724 auto b_type = b->fileType;
1725 if (a_type != b_type) return ascending ? a_type < b_type : b_type < a_type;
1726
1727 // then sort on column-specific properties
1728 auto a_properties = extractor(a);
1729 auto b_properties = extractor(b);
1730 return ascending ? a_properties < b_properties : b_properties < a_properties;
1731 };
1732 switch (sortingField) {
1733 case SortingFieldEnum::FIELD_FILENAME:
1734 return compare([](const auto& x) { return x->fileNameExt_optimized; });
1735 case SortingFieldEnum::FIELD_TYPE:
1736 return compare([](const auto& x) { return x->fileExtLevels_optimized[0]; });
1737 case SortingFieldEnum::FIELD_SIZE:
1738 return compare([](const auto& x) { return x->fileSize; });
1739 case SortingFieldEnum::FIELD_DATE:
1740 return compare([](const auto& x) { return x->fileModifDate; });
1741#ifdef USE_THUMBNAILS
1742 case SortingFieldEnum::FIELD_THUMBNAILS:
1743 return compare([](const auto& x) { return std::make_tuple(x->thumbnailInfo.textureWidth, x->thumbnailInfo.textureHeight); });
1744#endif
1745 default:
1746 return false; // we shouldn't get here
1747 }
1748 });
1749
1750 m_ApplyFilteringOnFileList(vFileDialogInternal, vFileInfosList, vFileInfosFilteredList);
1751}
1752
1753bool IGFD::FileManager::m_CompleteFileInfosWithUserFileAttirbutes(const FileDialogInternal& vFileDialogInternal, const std::shared_ptr<FileInfos>& vInfos) {
1754 if (vFileDialogInternal.getDialogConfig().userFileAttributes != nullptr) {
1755 if (!vFileDialogInternal.getDialogConfig().userFileAttributes(vInfos.get(), vFileDialogInternal.getDialogConfig().userDatas)) {
1756 return false; // the file will be ignored, so not added to the file list, so not displayed
1757 } else {
1758 if (!vInfos->fileType.isDir()) {
1759 vInfos->formatedFileSize = IGFD::Utils::FormatFileSize(vInfos->fileSize);
1760 }
1761 }
1762 }
1763 return true; // file will be added to file list, so displayed
1764}
1765
1766void IGFD::FileManager::ClearFileLists() {
1767 m_FilteredFileList.clear();
1768 m_FileList.clear();
1769}
1770
1771void IGFD::FileManager::ClearPathLists() {
1772 m_FilteredPathList.clear();
1773 m_PathList.clear();
1774}
1775
1776void IGFD::FileManager::m_AddFile(const FileDialogInternal& vFileDialogInternal, const std::string& vPath, const std::string& vFileName, const FileType& vFileType) {
1777 auto infos = std::make_shared<FileInfos>();
1778
1779 infos->filePath = vPath;
1780 infos->fileNameExt = vFileName;
1781 infos->fileNameExt_optimized = Utils::LowerCaseString(infos->fileNameExt);
1782 infos->fileType = vFileType;
1783
1784 if (infos->fileNameExt.empty() || (infos->fileNameExt == "." && !vFileDialogInternal.filterManager.dLGFilters.empty())) { // filename empty or filename is the current dir '.' //-V807
1785 return;
1786 }
1787
1788 if (infos->fileNameExt != ".." && (vFileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DontShowHiddenFiles) && infos->fileNameExt[0] == '.') { // dont show hidden files
1789 if (!vFileDialogInternal.filterManager.dLGFilters.empty() || (vFileDialogInternal.filterManager.dLGFilters.empty() && infos->fileNameExt != ".")) { // except "." if in directory mode //-V728
1790 return;
1791 }
1792 }
1793
1794 if (infos->FinalizeFileTypeParsing(vFileDialogInternal.filterManager.GetSelectedFilter().count_dots)) {
1795 if (!vFileDialogInternal.filterManager.IsCoveredByFilters(*infos.get(), (vFileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_CaseInsensitiveExtention) != 0)) {
1796 return;
1797 }
1798 }
1799
1800 vFileDialogInternal.filterManager.m_FillFileStyle(infos);
1801
1802 m_CompleteFileInfos(infos);
1803
1804 if (m_CompleteFileInfosWithUserFileAttirbutes(vFileDialogInternal, infos)) {
1805 m_FileList.push_back(infos);
1806 }
1807}
1808
1809void IGFD::FileManager::m_AddPath(const FileDialogInternal& vFileDialogInternal, const std::string& vPath, const std::string& vFileName, const FileType& vFileType) {
1810 if (!vFileType.isDir()) return;
1811
1812 auto infos = std::make_shared<FileInfos>();
1813
1814 infos->filePath = vPath;
1815 infos->fileNameExt = vFileName;
1816 infos->fileNameExt_optimized = Utils::LowerCaseString(infos->fileNameExt);
1817 infos->fileType = vFileType;
1818
1819 if (infos->fileNameExt.empty() || (infos->fileNameExt == "." && !vFileDialogInternal.filterManager.dLGFilters.empty())) { // filename empty or filename is the current dir '.' //-V807
1820 return;
1821 }
1822
1823 if (infos->fileNameExt != ".." && (vFileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DontShowHiddenFiles) && infos->fileNameExt[0] == '.') { // dont show hidden files
1824 if (!vFileDialogInternal.filterManager.dLGFilters.empty() || (vFileDialogInternal.filterManager.dLGFilters.empty() && infos->fileNameExt != ".")) { // except "." if in directory mode //-V728
1825 return;
1826 }
1827 }
1828
1829 vFileDialogInternal.filterManager.m_FillFileStyle(infos);
1830
1831 m_CompleteFileInfos(infos);
1832
1833 if (m_CompleteFileInfosWithUserFileAttirbutes(vFileDialogInternal, infos)) {
1834 m_PathList.push_back(infos);
1835 }
1836}
1837
1838void IGFD::FileManager::ScanDir(const FileDialogInternal& vFileDialogInternal, const std::string& vPath) {
1839 std::string path = vPath;
1840
1841 if (m_CurrentPathDecomposition.empty()) {
1842 SetCurrentDir(path);
1843 }
1844
1845 if (!m_CurrentPathDecomposition.empty()) {
1846#ifdef _IGFD_WIN_
1847 if (path == fsRoot) path += IGFD::Utils::GetPathSeparator();
1848#endif // _IGFD_WIN_
1849
1850 ClearFileLists();
1851
1852 const auto& files = m_FileSystemPtr->ScanDirectory(vPath);
1853 for (const auto& file : files) {
1854 m_AddFile(vFileDialogInternal, path, file.fileNameExt, file.fileType);
1855 }
1856
1857 m_SortFields(vFileDialogInternal, m_FileList, m_FilteredFileList);
1858 }
1859}
1860
1861void IGFD::FileManager::m_ScanDirForPathSelection(const FileDialogInternal& vFileDialogInternal, const std::string& vPath) {
1862 std::string path = vPath;
1863
1864 if (!path.empty()) {
1865#ifdef _IGFD_WIN_
1866 if (path == fsRoot) path += IGFD::Utils::GetPathSeparator();
1867#endif // _IGFD_WIN_
1868
1869 ClearPathLists();
1870
1871 const auto& files = m_FileSystemPtr->ScanDirectory(path);
1872 for (const auto& file : files) {
1873 if (file.fileType.isDir()) {
1874 m_AddPath(vFileDialogInternal, path, file.fileNameExt, file.fileType);
1875 }
1876 }
1877
1878 m_SortFields(vFileDialogInternal, m_PathList, m_FilteredPathList);
1879 }
1880}
1881
1882void IGFD::FileManager::m_OpenPathPopup(const FileDialogInternal& vFileDialogInternal, std::vector<std::string>::iterator vPathIter) {
1883 const auto path = ComposeNewPath(vPathIter);
1884 m_ScanDirForPathSelection(vFileDialogInternal, path);
1885 m_PopupComposedPath = vPathIter;
1886 ImGui::OpenPopup("IGFD_Path_Popup");
1887}
1888
1889bool IGFD::FileManager::GetDrives() {
1890 auto drives = m_FileSystemPtr->GetDrivesList();
1891 if (!drives.empty()) {
1892 m_CurrentPath.clear();
1893 m_CurrentPathDecomposition.clear();
1894 ClearFileLists();
1895 for (auto& drive : drives) {
1896 auto info = std::make_shared<FileInfos>();
1897 info->fileNameExt = drive;
1898 info->fileNameExt_optimized = Utils::LowerCaseString(drive);
1899 info->fileType.SetContent(FileType::ContentType::Directory);
1900
1901 if (!info->fileNameExt.empty()) {
1902 m_FileList.push_back(info);
1903 }
1904 }
1905 showDrives = true;
1906 return true;
1907 }
1908 return false;
1909}
1910
1911bool IGFD::FileManager::IsComposerEmpty() {
1912 return m_CurrentPathDecomposition.empty();
1913}
1914
1915size_t IGFD::FileManager::GetComposerSize() {
1916 return m_CurrentPathDecomposition.size();
1917}
1918
1919bool IGFD::FileManager::IsFileListEmpty() {
1920 return m_FileList.empty();
1921}
1922
1923bool IGFD::FileManager::IsPathListEmpty() {
1924 return m_PathList.empty();
1925}
1926
1927size_t IGFD::FileManager::GetFullFileListSize() {
1928 return m_FileList.size();
1929}
1930
1931std::shared_ptr<IGFD::FileInfos> IGFD::FileManager::GetFullFileAt(size_t vIdx) {
1932 if (vIdx < m_FileList.size()) return m_FileList[vIdx];
1933 return nullptr;
1934}
1935
1936bool IGFD::FileManager::IsFilteredListEmpty() {
1937 return m_FilteredFileList.empty();
1938}
1939
1940bool IGFD::FileManager::IsPathFilteredListEmpty() {
1941 return m_FilteredPathList.empty();
1942}
1943
1944size_t IGFD::FileManager::GetFilteredListSize() {
1945 return m_FilteredFileList.size();
1946}
1947
1948size_t IGFD::FileManager::GetPathFilteredListSize() {
1949 return m_FilteredPathList.size();
1950}
1951
1952std::shared_ptr<IGFD::FileInfos> IGFD::FileManager::GetFilteredFileAt(size_t vIdx) {
1953 if (vIdx < m_FilteredFileList.size()) return m_FilteredFileList[vIdx];
1954 return nullptr;
1955}
1956
1957std::shared_ptr<IGFD::FileInfos> IGFD::FileManager::GetFilteredPathAt(size_t vIdx) {
1958 if (vIdx < m_FilteredPathList.size()) return m_FilteredPathList[vIdx];
1959 return nullptr;
1960}
1961
1962std::vector<std::string>::iterator IGFD::FileManager::GetCurrentPopupComposedPath() {
1963 return m_PopupComposedPath;
1964}
1965
1966bool IGFD::FileManager::IsFileNameSelected(const std::string& vFileName) {
1967 return m_SelectedFileNames.find(vFileName) != m_SelectedFileNames.end();
1968}
1969
1970std::string IGFD::FileManager::GetBack() {
1971 return m_CurrentPathDecomposition.back();
1972}
1973
1974void IGFD::FileManager::ClearComposer() {
1975 m_CurrentPathDecomposition.clear();
1976}
1977
1978void IGFD::FileManager::ClearAll() {
1979 ClearComposer();
1980 ClearFileLists();
1981 ClearPathLists();
1982}
1983void IGFD::FileManager::ApplyFilteringOnFileList(const FileDialogInternal& vFileDialogInternal) {
1984 m_ApplyFilteringOnFileList(vFileDialogInternal, m_FileList, m_FilteredFileList);
1985}
1986
1987void IGFD::FileManager::m_ApplyFilteringOnFileList(const FileDialogInternal& vFileDialogInternal, std::vector<std::shared_ptr<FileInfos> >& vFileInfosList, std::vector<std::shared_ptr<FileInfos> >& vFileInfosFilteredList) {
1988 vFileInfosFilteredList.clear();
1989 for (const auto& file : vFileInfosList) {
1990 if (!file.use_count()) continue;
1991 bool show = true;
1992 if (!file->SearchForTag(vFileDialogInternal.searchManager.searchTag)) // if search tag
1993 show = false;
1994 if (dLGDirectoryMode && !file->fileType.isDir()) show = false;
1995 if (show) vFileInfosFilteredList.push_back(file);
1996 }
1997}
1998
1999void IGFD::FileManager::m_CompleteFileInfos(const std::shared_ptr<FileInfos>& vInfos) {
2000 if (!vInfos.use_count()) return;
2001
2002 if (vInfos->fileNameExt != "." && vInfos->fileNameExt != "..") {
2003 // _stat struct :
2004 // dev_t st_dev; /* ID of device containing file */
2005 // ino_t st_ino; /* inode number */
2006 // mode_t st_mode; /* protection */
2007 // nlink_t st_nlink; /* number of hard links */
2008 // uid_t st_uid; /* user ID of owner */
2009 // gid_t st_gid; /* group ID of owner */
2010 // dev_t st_rdev; /* device ID (if special file) */
2011 // off_t st_size; /* total size, in bytes */
2012 // blksize_t st_blksize; /* blocksize for file system I/O */
2013 // blkcnt_t st_blocks; /* number of 512B blocks allocated */
2014 // time_t st_atime; /* time of last access - not sure out of ntfs */
2015 // time_t st_mtime; /* time of last modification - not sure out of ntfs */
2016 // time_t st_ctime; /* time of last status change - not sure out of ntfs */
2017
2018 std::string fpn;
2019
2020 // FIXME: so the condition is always true?
2021 if (vInfos->fileType.isFile() || vInfos->fileType.isLinkToUnknown() || vInfos->fileType.isDir()) {
2022 fpn = vInfos->filePath + IGFD::Utils::GetPathSeparator() + vInfos->fileNameExt;
2023 }
2024
2025 struct stat statInfos = {};
2026 char timebuf[100];
2027 int result = stat(fpn.c_str(), &statInfos);
2028 if (!result) {
2029 if (!vInfos->fileType.isDir()) {
2030 vInfos->fileSize = (size_t)statInfos.st_size;
2031 vInfos->formatedFileSize = IGFD::Utils::FormatFileSize(vInfos->fileSize);
2032 }
2033
2034 size_t len = 0;
2035#ifdef _MSC_VER
2036 struct tm _tm;
2037 errno_t err = localtime_s(&_tm, &statInfos.st_mtime);
2038 if (!err) len = strftime(timebuf, 99, DateTimeFormat, &_tm);
2039#else // _MSC_VER
2040 struct tm* _tm = localtime(&statInfos.st_mtime);
2041 if (_tm) len = strftime(timebuf, 99, DateTimeFormat, _tm);
2042#endif // _MSC_VER
2043 if (len) {
2044 vInfos->fileModifDate = std::string(timebuf, len);
2045 }
2046 }
2047 }
2048}
2049
2050void IGFD::FileManager::m_RemoveFileNameInSelection(const std::string& vFileName) {
2051 m_SelectedFileNames.erase(vFileName);
2052
2053 if (m_SelectedFileNames.size() == 1) {
2054 snprintf(fileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%s", vFileName.c_str());
2055 } else {
2056 snprintf(fileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%zu files Selected", m_SelectedFileNames.size());
2057 }
2058}
2059
2060void IGFD::FileManager::m_m_AddFileNameInSelection(const std::string& vFileName, bool vSetLastSelectionFileName) {
2061 m_SelectedFileNames.emplace(vFileName);
2062
2063 if (m_SelectedFileNames.size() == 1) {
2064 snprintf(fileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%s", vFileName.c_str());
2065 } else {
2066 snprintf(fileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%zu files Selected", m_SelectedFileNames.size());
2067 }
2068
2069 if (vSetLastSelectionFileName) m_LastSelectedFileName = vFileName;
2070}
2071
2072void IGFD::FileManager::SetCurrentDir(const std::string& vPath) {
2073 std::string path = vPath;
2074#ifdef _IGFD_WIN_
2075 if (fsRoot == path) path += IGFD::Utils::GetPathSeparator();
2076#endif // _IGFD_WIN_
2077
2078 bool dir_opened = m_FileSystemPtr->IsDirectory(path);
2079 if (!dir_opened) {
2080 path = ".";
2081 dir_opened = m_FileSystemPtr->IsDirectory(path);
2082 }
2083 if (dir_opened) {
2084#ifdef _IGFD_WIN_
2085 DWORD numchar = 0;
2086 std::wstring wpath = IGFD::Utils::UTF8Decode(path);
2087 numchar = GetFullPathNameW(wpath.c_str(), 0, nullptr, nullptr);
2088 std::wstring fpath(numchar, 0);
2089 GetFullPathNameW(wpath.c_str(), numchar, (wchar_t*)fpath.data(), nullptr);
2090 std::string real_path = IGFD::Utils::UTF8Encode(fpath);
2091 while (real_path.back() == '\0') // for fix issue we can have with std::string concatenation.. if there is a \0 at end
2092 real_path = real_path.substr(0, real_path.size() - 1U);
2093 if (!real_path.empty())
2094#elif defined(_IGFD_UNIX_) // _IGFD_UNIX_ is _IGFD_WIN_ or APPLE
2095 char real_path[PATH_MAX];
2096 char* numchar = realpath(path.c_str(), real_path);
2097 if (numchar != nullptr)
2098#endif // _IGFD_WIN_
2099 {
2100 m_CurrentPath = std::move(real_path);
2101 if (m_CurrentPath[m_CurrentPath.size() - 1] == PATH_SEP) {
2102 m_CurrentPath = m_CurrentPath.substr(0, m_CurrentPath.size() - 1);
2103 }
2104 IGFD::Utils::SetBuffer(inputPathBuffer, MAX_PATH_BUFFER_SIZE, m_CurrentPath);
2105 m_CurrentPathDecomposition = IGFD::Utils::SplitStringToVector(m_CurrentPath, PATH_SEP, false);
2106#ifdef _IGFD_UNIX_ // _IGFD_UNIX_ is _IGFD_WIN_ or APPLE
2107 m_CurrentPathDecomposition.insert(m_CurrentPathDecomposition.begin(), IGFD::Utils::GetPathSeparator());
2108#endif // _IGFD_UNIX_
2109 if (!m_CurrentPathDecomposition.empty()) {
2110#ifdef _IGFD_WIN_
2111 fsRoot = m_CurrentPathDecomposition[0];
2112#endif // _IGFD_WIN_
2113 }
2114 }
2115 }
2116}
2117
2118bool IGFD::FileManager::CreateDir(const std::string& vPath) {
2119 if (!vPath.empty()) {
2120 std::string path = m_CurrentPath + IGFD::Utils::GetPathSeparator() + vPath;
2121 return m_FileSystemPtr->CreateDirectoryIfNotExist(path);
2122 }
2123 return false;
2124}
2125
2126std::string IGFD::FileManager::ComposeNewPath(std::vector<std::string>::iterator vIter) {
2127 std::string res;
2128
2129 while (true) {
2130 if (!res.empty()) {
2131#ifdef _IGFD_WIN_
2132 res = *vIter + IGFD::Utils::GetPathSeparator() + res;
2133#elif defined(_IGFD_UNIX_) // _IGFD_UNIX_ is _IGFD_WIN_ or APPLE
2134 if (*vIter == fsRoot)
2135 res = *vIter + res;
2136 else
2137 res = *vIter + PATH_SEP + res;
2138#endif // _IGFD_WIN_
2139 } else
2140 res = *vIter;
2141
2142 if (vIter == m_CurrentPathDecomposition.begin()) {
2143#ifdef _IGFD_UNIX_ // _IGFD_UNIX_ is _IGFD_WIN_ or APPLE
2144 if (res[0] != PATH_SEP) res = PATH_SEP + res;
2145#else
2146 if (res.back() != PATH_SEP) res.push_back(PATH_SEP);
2147#endif // defined(_IGFD_UNIX_)
2148 break;
2149 }
2150
2151 --vIter;
2152 }
2153
2154 return res;
2155}
2156
2157bool IGFD::FileManager::SetPathOnParentDirectoryIfAny() {
2158 if (m_CurrentPathDecomposition.size() > 1) {
2159 m_CurrentPath = ComposeNewPath(m_CurrentPathDecomposition.end() - 2);
2160 return true;
2161 }
2162 return false;
2163}
2164
2165std::string IGFD::FileManager::GetCurrentPath() {
2166 if (m_CurrentPath.empty()) m_CurrentPath = ".";
2167 return m_CurrentPath;
2168}
2169
2170void IGFD::FileManager::SetCurrentPath(const std::string& vCurrentPath) {
2171 if (vCurrentPath.empty())
2172 m_CurrentPath = ".";
2173 else
2174 m_CurrentPath = vCurrentPath;
2175}
2176
2177void IGFD::FileManager::SetDefaultFileName(const std::string& vFileName) {
2178 dLGDefaultFileName = vFileName;
2179 IGFD::Utils::SetBuffer(fileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFileName);
2180}
2181
2182bool IGFD::FileManager::SelectDirectory(const std::shared_ptr<FileInfos>& vInfos) {
2183 if (!vInfos.use_count()) return false;
2184
2185 bool pathClick = false;
2186
2187 if (vInfos->fileNameExt == "..") {
2188 pathClick = SetPathOnParentDirectoryIfAny();
2189 } else {
2190 std::string newPath;
2191
2192 if (showDrives) {
2193 newPath = vInfos->fileNameExt + IGFD::Utils::GetPathSeparator();
2194 } else {
2195#ifdef __linux__
2196 if (fsRoot == m_CurrentPath)
2197 newPath = m_CurrentPath + vInfos->fileNameExt;
2198 else
2199#endif // __linux__
2200 newPath = m_CurrentPath + IGFD::Utils::GetPathSeparator() + vInfos->fileNameExt;
2201 }
2202
2203 if (m_FileSystemPtr->IsDirectoryCanBeOpened(newPath)) {
2204 if (showDrives) {
2205 m_CurrentPath = vInfos->fileNameExt;
2206 fsRoot = m_CurrentPath;
2207 } else {
2208 m_CurrentPath = newPath; //-V820
2209 }
2210 pathClick = true;
2211 }
2212 }
2213
2214 return pathClick;
2215}
2216
2217void IGFD::FileManager::SelectFileName(const FileDialogInternal& vFileDialogInternal, const std::shared_ptr<FileInfos>& vInfos) {
2218 if (!vInfos.use_count()) return;
2219
2220 if (ImGui::IsKeyDown(ImGuiMod_Ctrl)) {
2221 if (dLGcountSelectionMax == 0) // infinite selection
2222 {
2223 if (m_SelectedFileNames.find(vInfos->fileNameExt) == m_SelectedFileNames.end()) // not found +> add
2224 {
2225 m_m_AddFileNameInSelection(vInfos->fileNameExt, true);
2226 } else { // found +> remove
2227 m_RemoveFileNameInSelection(vInfos->fileNameExt);
2228 }
2229 } else // selection limited by size
2230 {
2231 if (m_SelectedFileNames.size() < dLGcountSelectionMax) {
2232 if (m_SelectedFileNames.find(vInfos->fileNameExt) == m_SelectedFileNames.end()) // not found +> add
2233 {
2234 m_m_AddFileNameInSelection(vInfos->fileNameExt, true);
2235 } else { // found +> remove
2236 m_RemoveFileNameInSelection(vInfos->fileNameExt);
2237 }
2238 }
2239 }
2240 } else if (ImGui::IsKeyDown(ImGuiMod_Shift)) {
2241 if (dLGcountSelectionMax != 1) {
2242 m_SelectedFileNames.clear();
2243 // we will iterate filelist and get the last selection after the start selection
2244 bool startMultiSelection = false;
2245 std::string fileNameToSelect = vInfos->fileNameExt;
2246 std::string savedLastSelectedFileName; // for invert selection mode
2247 for (const auto& file : m_FileList) {
2248 if (!file.use_count()) continue;
2249
2250 bool canTake = true;
2251 if (!file->SearchForTag(vFileDialogInternal.searchManager.searchTag)) canTake = false;
2252 if (canTake) // if not filtered, we will take files who are filtered by the dialog
2253 {
2254 if (file->fileNameExt == m_LastSelectedFileName) {
2255 startMultiSelection = true;
2256 m_m_AddFileNameInSelection(m_LastSelectedFileName, false);
2257 } else if (startMultiSelection) {
2258 if (dLGcountSelectionMax == 0) // infinite selection
2259 {
2260 m_m_AddFileNameInSelection(file->fileNameExt, false);
2261 } else { // selection limited by size
2262 if (m_SelectedFileNames.size() < dLGcountSelectionMax) {
2263 m_m_AddFileNameInSelection(file->fileNameExt, false);
2264 } else {
2265 startMultiSelection = false;
2266 if (!savedLastSelectedFileName.empty()) m_LastSelectedFileName = savedLastSelectedFileName;
2267 break;
2268 }
2269 }
2270 }
2271
2272 if (file->fileNameExt == fileNameToSelect) {
2273 if (!startMultiSelection) // we are before the last Selected FileName, so we must inverse
2274 {
2275 savedLastSelectedFileName = m_LastSelectedFileName;
2276 m_LastSelectedFileName = fileNameToSelect;
2277 fileNameToSelect = savedLastSelectedFileName;
2278 startMultiSelection = true;
2279 m_m_AddFileNameInSelection(m_LastSelectedFileName, false);
2280 } else {
2281 startMultiSelection = false;
2282 if (!savedLastSelectedFileName.empty()) m_LastSelectedFileName = savedLastSelectedFileName;
2283 break;
2284 }
2285 }
2286 }
2287 }
2288 }
2289 } else {
2290 m_SelectedFileNames.clear();
2291 IGFD::Utils::ResetBuffer(fileNameBuffer);
2292 m_m_AddFileNameInSelection(vInfos->fileNameExt, true);
2293 }
2294}
2295
2296void IGFD::FileManager::DrawDirectoryCreation(const FileDialogInternal& vFileDialogInternal) {
2297 if (vFileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableCreateDirectoryButton) return;
2298
2299 if (IMGUI_BUTTON(createDirButtonString)) {
2300 if (!m_CreateDirectoryMode) {
2301 m_CreateDirectoryMode = true;
2302 IGFD::Utils::ResetBuffer(directoryNameBuffer);
2303 }
2304 }
2305 if (ImGui::IsItemHovered()) ImGui::SetTooltip(buttonCreateDirString);
2306
2307 if (m_CreateDirectoryMode) {
2308 ImGui::SameLine();
2309
2310 ImGui::PushItemWidth(100.0f);
2311 ImGui::InputText("##DirectoryFileName", directoryNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER);
2312 ImGui::PopItemWidth();
2313
2314 ImGui::SameLine();
2315
2316 if (IMGUI_BUTTON(okButtonString)) {
2317 std::string newDir = std::string(directoryNameBuffer);
2318 if (CreateDir(newDir)) {
2319 SetCurrentPath(m_CurrentPath + IGFD::Utils::GetPathSeparator() + newDir);
2320 OpenCurrentPath(vFileDialogInternal);
2321 }
2322
2323 m_CreateDirectoryMode = false;
2324 }
2325
2326 ImGui::SameLine();
2327
2328 if (IMGUI_BUTTON(cancelButtonString)) {
2329 m_CreateDirectoryMode = false;
2330 }
2331 }
2332
2333 ImGui::SameLine();
2334}
2335
2336void IGFD::FileManager::DrawPathComposer(const FileDialogInternal& vFileDialogInternal) {
2337 if (IMGUI_BUTTON(resetButtonString)) {
2338 SetCurrentPath(".");
2339 OpenCurrentPath(vFileDialogInternal);
2340 }
2341 if (ImGui::IsItemHovered()) ImGui::SetTooltip(buttonResetPathString);
2342
2343#ifdef _IGFD_WIN_
2344 ImGui::SameLine();
2345
2346 if (IMGUI_BUTTON(drivesButtonString)) {
2347 drivesClicked = true;
2348 }
2349 if (ImGui::IsItemHovered()) ImGui::SetTooltip(buttonDriveString);
2350#endif // _IGFD_WIN_
2351
2352 ImGui::SameLine();
2353
2354 if (IMGUI_BUTTON(editPathButtonString)) {
2355 inputPathActivated = !inputPathActivated;
2356 if (inputPathActivated) {
2357 if (!m_CurrentPathDecomposition.empty()) {
2358 auto endIt = m_CurrentPathDecomposition.end();
2359 m_CurrentPath = ComposeNewPath(--endIt);
2360 IGFD::Utils::SetBuffer(inputPathBuffer, MAX_PATH_BUFFER_SIZE, m_CurrentPath);
2361 }
2362 }
2363 }
2364 if (ImGui::IsItemHovered()) ImGui::SetTooltip(buttonEditPathString);
2365
2366 ImGui::SameLine();
2367
2368 ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
2369
2370 // show current path
2371 if (!m_CurrentPathDecomposition.empty()) {
2372 ImGui::SameLine();
2373
2374 if (inputPathActivated) {
2375 ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
2376 ImGui::InputText("##pathedition", inputPathBuffer, MAX_PATH_BUFFER_SIZE);
2377 ImGui::PopItemWidth();
2378 } else {
2379 int _id = 0;
2380 for (auto itPathDecomp = m_CurrentPathDecomposition.begin(); itPathDecomp != m_CurrentPathDecomposition.end(); ++itPathDecomp) {
2381 if (itPathDecomp != m_CurrentPathDecomposition.begin()) {
2382#if defined(CUSTOM_PATH_SPACING)
2383 ImGui::SameLine(0, CUSTOM_PATH_SPACING);
2384#else
2385 ImGui::SameLine();
2386#endif // USE_CUSTOM_PATH_SPACING
2387 if (!(vFileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableQuickPathSelection)) {
2388#if defined(_IGFD_WIN_)
2389 const char* sep = "\\";
2390#elif defined(_IGFD_UNIX_)
2391 const char* sep = "/";
2392 if (itPathDecomp != m_CurrentPathDecomposition.begin() + 1)
2393#endif
2394 {
2395 ImGui::PushID(_id++);
2396 bool click = IMGUI_PATH_BUTTON(sep);
2397 ImGui::PopID();
2398
2399#if defined(CUSTOM_PATH_SPACING)
2400 ImGui::SameLine(0, CUSTOM_PATH_SPACING);
2401#else
2402 ImGui::SameLine();
2403#endif // USE_CUSTOM_PATH_SPACING
2404
2405 if (click) {
2406 m_OpenPathPopup(vFileDialogInternal, itPathDecomp - 1);
2407 } else if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
2408 m_SetCurrentPath(itPathDecomp - 1);
2409 break;
2410 }
2411 }
2412 }
2413 }
2414
2415 ImGui::PushID(_id++);
2416 bool click = IMGUI_PATH_BUTTON((*itPathDecomp).c_str());
2417 ImGui::PopID();
2418 if (click) {
2419 m_CurrentPath = ComposeNewPath(itPathDecomp);
2420 puPathClicked = true;
2421 break;
2422 } else if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { // activate input for path
2423 m_SetCurrentPath(itPathDecomp);
2424 break;
2425 }
2426 }
2427 }
2428 }
2429}
2430
2431void IGFD::FileManager::m_SetCurrentPath(std::vector<std::string>::iterator vPathIter) {
2432 m_CurrentPath = ComposeNewPath(vPathIter);
2433 IGFD::Utils::SetBuffer(inputPathBuffer, MAX_PATH_BUFFER_SIZE, m_CurrentPath);
2434 inputPathActivated = true;
2435}
2436
2437std::string IGFD::FileManager::GetResultingPath() {
2438 if (dLGDirectoryMode && m_SelectedFileNames.size() == 1) { // if directory mode with selection 1
2439 std::string selectedDirectory = fileNameBuffer;
2440 std::string path = m_CurrentPath;
2441 if (!selectedDirectory.empty() && selectedDirectory != ".") {
2442 path += IGFD::Utils::GetPathSeparator() + selectedDirectory;
2443 }
2444 return path;
2445 }
2446 return m_CurrentPath; // if file mode
2447}
2448
2449std::string IGFD::FileManager::GetResultingFileName(FileDialogInternal& vFileDialogInternal, IGFD_ResultMode vFlag) {
2450 if (!dLGDirectoryMode) { // if not directory mode
2451 const auto& filename = std::string(fileNameBuffer);
2452 return vFileDialogInternal.filterManager.ReplaceExtentionWithCurrentFilterIfNeeded(filename, vFlag);
2453 }
2454 return ""; // directory mode
2455}
2456
2457std::string IGFD::FileManager::GetResultingFilePathName(FileDialogInternal& vFileDialogInternal, IGFD_ResultMode vFlag) {
2458 if (!dLGDirectoryMode) { // if not directory mode
2459 auto result = GetResultingPath();
2460 const auto& filename = GetResultingFileName(vFileDialogInternal, vFlag);
2461 if (!filename.empty()) {
2462 if (m_FileSystemPtr != nullptr && m_FileSystemPtr->IsFileExist(filename)) {
2463 result = filename; // #144, exist file, so absolute, so return it (maybe set by user in inputText)
2464 } else { // #144, else concate path with current filename
2465#ifdef _IGFD_UNIX_
2466 if (fsRoot != result)
2467#endif // _IGFD_UNIX_
2468 {
2469 result += IGFD::Utils::GetPathSeparator();
2470 }
2471 result += filename;
2472 }
2473 }
2474
2475 return result;
2476 }
2477 return ""; // file mode
2478}
2479
2480std::map<std::string, std::string> IGFD::FileManager::GetResultingSelection(FileDialogInternal& vFileDialogInternal, IGFD_ResultMode vFlag) {
2481 std::map<std::string, std::string> res;
2482 for (const auto& selectedFileName : m_SelectedFileNames) {
2483 auto result = GetResultingPath();
2484#ifdef _IGFD_UNIX_
2485 if (fsRoot != result)
2486#endif // _IGFD_UNIX_
2487 {
2488 result += IGFD::Utils::GetPathSeparator();
2489 }
2490 result += vFileDialogInternal.filterManager.ReplaceExtentionWithCurrentFilterIfNeeded(selectedFileName, vFlag);
2491 res[selectedFileName] = result;
2492 }
2493 return res;
2494}
2495
2496//#pragma endregion
2497
2498//#pragma region FileDialogInternal
2499
2500void IGFD::FileDialogInternal::NewFrame() {
2501 canWeContinue = true; // reset flag for possibily validate the dialog
2502 isOk = false; // reset dialog result
2503 fileManager.drivesClicked = false;
2504 fileManager.puPathClicked = false;
2505
2506 needToExitDialog = false;
2507
2508#ifdef USE_DIALOG_EXIT_WITH_KEY
2509 if (ImGui::IsKeyPressed(IGFD_EXIT_KEY)) {
2510 // we do that here with the data's defined at the last frame
2511 // because escape key can quit input activation and at the end of the frame all flag will be false
2512 // so we will detect nothing
2513 if (!(fileManager.inputPathActivated || searchManager.searchInputIsActive || fileInputIsActive || fileListViewIsActive)) {
2514 needToExitDialog = true; // need to quit dialog
2515 }
2516 } else
2517#endif
2518 {
2519 searchManager.searchInputIsActive = false;
2520 fileInputIsActive = false;
2521 fileListViewIsActive = false;
2522 }
2523}
2524
2525void IGFD::FileDialogInternal::EndFrame() {
2526 // directory change
2527 if (fileManager.puPathClicked) {
2528 fileManager.OpenCurrentPath(*this);
2529 }
2530
2531 if (fileManager.drivesClicked) {
2532 if (fileManager.GetDrives()) {
2533 fileManager.ApplyFilteringOnFileList(*this);
2534 }
2535 }
2536
2537 if (fileManager.inputPathActivated) {
2538 auto gio = ImGui::GetIO();
2539 if (ImGui::IsKeyReleased(ImGuiKey_Enter)) {
2540 fileManager.SetCurrentPath(std::string(fileManager.inputPathBuffer));
2541 fileManager.OpenCurrentPath(*this);
2542 fileManager.inputPathActivated = false;
2543 }
2544 if (ImGui::IsKeyReleased(ImGuiKey_Escape)) {
2545 fileManager.inputPathActivated = false;
2546 }
2547 }
2548}
2549
2550void IGFD::FileDialogInternal::ResetForNewDialog() {
2551}
2552
2553void IGFD::FileDialogInternal::configureDialog(const std::string& vKey, const std::string& vTitle, const char* vFilters, const FileDialogConfig& vConfig) {
2554 m_DialogConfig = vConfig;
2555 ResetForNewDialog();
2556 dLGkey = vKey;
2557 dLGtitle = vTitle;
2558
2559 // treatment
2560 if (m_DialogConfig.sidePane == nullptr) {
2561 m_DialogConfig.sidePaneWidth = 0.0f;
2562 }
2563
2564 if (m_DialogConfig.filePathName.empty()) {
2565 if (m_DialogConfig.path.empty()) {
2566 fileManager.dLGpath = fileManager.GetCurrentPath();
2567 } else {
2568 fileManager.dLGpath = m_DialogConfig.path;
2569 }
2570 fileManager.SetCurrentPath(m_DialogConfig.path);
2571 fileManager.dLGcountSelectionMax = (size_t)m_DialogConfig.countSelectionMax;
2572 fileManager.SetDefaultFileName(m_DialogConfig.fileName);
2573 } else {
2574 auto ps = fileManager.GetFileSystemInstance()->ParsePathFileName(m_DialogConfig.filePathName);
2575 if (ps.isOk) {
2576 fileManager.dLGpath = ps.path;
2577 fileManager.SetDefaultFileName(ps.name);
2578 filterManager.dLGdefaultExt = "." + ps.ext;
2579 } else {
2580 fileManager.dLGpath = fileManager.GetCurrentPath();
2581 fileManager.SetDefaultFileName("");
2582 filterManager.dLGdefaultExt.clear();
2583 }
2584 }
2585
2586 filterManager.dLGdefaultExt.clear();
2587 filterManager.ParseFilters(vFilters);
2588 filterManager.SetSelectedFilterWithExt(filterManager.dLGdefaultExt);
2589 fileManager.SetCurrentPath(fileManager.dLGpath);
2590 fileManager.dLGDirectoryMode = (vFilters == nullptr);
2591 fileManager.dLGcountSelectionMax = m_DialogConfig.countSelectionMax; //-V101
2592 fileManager.ClearAll();
2593 showDialog = true;
2594}
2595
2596const IGFD::FileDialogConfig& IGFD::FileDialogInternal::getDialogConfig() const {
2597 return m_DialogConfig;
2598}
2599
2600IGFD::FileDialogConfig& IGFD::FileDialogInternal::getDialogConfigRef() {
2601 return m_DialogConfig;
2602}
2603
2604//#pragma endregion
2605
2606//#pragma endregion
2607
2608//#pragma region Optional Features
2609
2610//#pragma region ThumbnailFeature
2611
2612IGFD::ThumbnailFeature::ThumbnailFeature() {
2613#ifdef USE_THUMBNAILS
2614 m_DisplayMode = DisplayModeEnum::FILE_LIST;
2615#endif
2616}
2617
2618IGFD::ThumbnailFeature::~ThumbnailFeature() = default;
2619
2620void IGFD::ThumbnailFeature::m_NewThumbnailFrame(FileDialogInternal& /*vFileDialogInternal*/) {
2621#ifdef USE_THUMBNAILS
2622 m_StartThumbnailFileDatasExtraction();
2623#endif
2624}
2625
2626void IGFD::ThumbnailFeature::m_EndThumbnailFrame(FileDialogInternal& vFileDialogInternal) {
2627#ifdef USE_THUMBNAILS
2628 m_ClearThumbnails(vFileDialogInternal);
2629#else
2630 (void)vFileDialogInternal;
2631#endif
2632}
2633
2634void IGFD::ThumbnailFeature::m_QuitThumbnailFrame(FileDialogInternal& vFileDialogInternal) {
2635#ifdef USE_THUMBNAILS
2636 m_StopThumbnailFileDatasExtraction();
2637 m_ClearThumbnails(vFileDialogInternal);
2638#else
2639 (void)vFileDialogInternal;
2640#endif
2641}
2642
2643#ifdef USE_THUMBNAILS
2644void IGFD::ThumbnailFeature::m_StartThumbnailFileDatasExtraction() {
2645 const bool res = m_ThumbnailGenerationThread.use_count() && m_ThumbnailGenerationThread->joinable();
2646 if (!res) {
2647 m_IsWorking = true;
2648 m_CountFiles = 0U;
2649 m_ThumbnailGenerationThread = std::shared_ptr<std::thread>(new std::thread(&IGFD::ThumbnailFeature::m_ThreadThumbnailFileDatasExtractionFunc, this), [this](std::thread* obj) {
2650 m_IsWorking = false;
2651 if (obj) {
2652 m_ThumbnailFileDatasToGetCv.notify_all();
2653 obj->join();
2654 }
2655 });
2656 }
2657}
2658
2659bool IGFD::ThumbnailFeature::m_StopThumbnailFileDatasExtraction() {
2660 const bool res = m_ThumbnailGenerationThread.use_count() && m_ThumbnailGenerationThread->joinable();
2661 if (res) {
2662 m_ThumbnailGenerationThread.reset();
2663 }
2664
2665 return res;
2666}
2667
2668void IGFD::ThumbnailFeature::m_ThreadThumbnailFileDatasExtractionFunc() {
2669 m_CountFiles = 0U;
2670 m_IsWorking = true;
2671
2672 // infinite loop while is thread working
2673 while (m_IsWorking) {
2674 std::unique_lock<std::mutex> thumbnailFileDatasToGetLock(m_ThumbnailFileDatasToGetMutex);
2675 m_ThumbnailFileDatasToGetCv.wait(thumbnailFileDatasToGetLock);
2676 if (!m_ThumbnailFileDatasToGet.empty()) {
2677 std::shared_ptr<FileInfos> file = nullptr;
2678 // get the first file in the list
2679 file = (*m_ThumbnailFileDatasToGet.begin());
2680 m_ThumbnailFileDatasToGet.pop_front();
2681 thumbnailFileDatasToGetLock.unlock();
2682
2683 // retrieve datas of the texture file if its an image file
2684 if (file.use_count()) {
2685 if (file->fileType.isFile()) //-V522
2686 {
2687 //|| file->fileExtLevels == ".hdr" => format float so in few times
2688 if (file->SearchForExts(".png,.bmp,.tga,.jpg,.jpeg,.gif,.psd,.pic,.ppm,.pgm", true)) {
2689 auto fpn = file->filePath + IGFD::Utils::GetPathSeparator() + file->fileNameExt;
2690
2691 int w = 0;
2692 int h = 0;
2693 int chans = 0;
2694 uint8_t* datas = stbi_load(fpn.c_str(), &w, &h, &chans, STBI_rgb_alpha);
2695 if (datas) {
2696 if (w && h) {
2697 // resize with respect to glyph ratio
2698 const float ratioX = (float)w / (float)h;
2699 const float newX = DisplayMode_ThumbailsList_ImageHeight * ratioX;
2700 float newY = w / ratioX;
2701 if (newX < w) newY = DisplayMode_ThumbailsList_ImageHeight;
2702
2703 const auto newWidth = (int)newX;
2704 const auto newHeight = (int)newY;
2705 const auto newBufSize = (size_t)(newWidth * newHeight * 4U); //-V112 //-V1028
2706 auto resizedData = new uint8_t[newBufSize];
2707
2708 const int resizeSucceeded = stbir_resize_uint8(datas, w, h, 0, resizedData, newWidth, newHeight, 0,
2709 4); //-V112
2710
2711 if (resizeSucceeded) {
2712 auto th = &file->thumbnailInfo;
2713
2714 th->textureFileDatas = resizedData;
2715 th->textureWidth = newWidth;
2716 th->textureHeight = newHeight;
2717 th->textureChannels = 4; //-V112
2718
2719 // we set that at least, because will launch the gpu creation of the texture in the
2720 // main thread
2721 th->isReadyToUpload = true;
2722
2723 // need gpu loading
2724 m_AddThumbnailToCreate(file);
2725 }
2726 } else {
2727 printf("image loading fail : w:%i h:%i c:%i\n", w, h, 4); //-V112
2728 }
2729
2730 stbi_image_free(datas);
2731 }
2732 }
2733 }
2734 }
2735 } else {
2736 thumbnailFileDatasToGetLock.unlock();
2737 }
2738 }
2739}
2740
2741void IGFD::ThumbnailFeature::m_VariadicProgressBar(float fraction, const ImVec2& size_arg, const char* fmt, ...) {
2742 va_list args;
2743 va_start(args, fmt);
2744 char TempBuffer[512];
2745 const int w = vsnprintf(TempBuffer, 511, fmt, args);
2746 va_end(args);
2747 if (w) {
2748 ImGui::ProgressBar(fraction, size_arg, TempBuffer);
2749 }
2750}
2751
2752void IGFD::ThumbnailFeature::m_DrawThumbnailGenerationProgress() {
2753 if (m_ThumbnailGenerationThread.use_count() && m_ThumbnailGenerationThread->joinable()) {
2754 if (!m_ThumbnailFileDatasToGet.empty()) {
2755 const auto p = (float)((double)m_CountFiles / (double)m_ThumbnailFileDatasToGet.size()); // read => no thread concurency issues
2756 m_VariadicProgressBar(p, ImVec2(50, 0), "%u/%u", m_CountFiles,
2757 (uint32_t)m_ThumbnailFileDatasToGet.size()); // read => no thread concurency issues
2758 ImGui::SameLine();
2759 }
2760 }
2761}
2762
2763void IGFD::ThumbnailFeature::m_AddThumbnailToLoad(const std::shared_ptr<FileInfos>& vFileInfos) {
2764 if (vFileInfos.use_count()) {
2765 if (vFileInfos->fileType.isFile()) {
2766 //|| file->fileExtLevels == ".hdr" => format float so in few times
2767 if (vFileInfos->SearchForExts(".png,.bmp,.tga,.jpg,.jpeg,.gif,.psd,.pic,.ppm,.pgm", true)) {
2768 // write => thread concurency issues
2769 m_ThumbnailFileDatasToGetMutex.lock();
2770 m_ThumbnailFileDatasToGet.push_back(vFileInfos);
2771 vFileInfos->thumbnailInfo.isLoadingOrLoaded = true;
2772 m_ThumbnailFileDatasToGetMutex.unlock();
2773 m_ThumbnailFileDatasToGetCv.notify_all();
2774 }
2775 }
2776 }
2777}
2778
2779void IGFD::ThumbnailFeature::m_AddThumbnailToCreate(const std::shared_ptr<FileInfos>& vFileInfos) {
2780 if (vFileInfos.use_count()) {
2781 // write => thread concurency issues
2782 m_ThumbnailToCreateMutex.lock();
2783 m_ThumbnailToCreate.push_back(vFileInfos);
2784 m_ThumbnailToCreateMutex.unlock();
2785 }
2786}
2787
2788void IGFD::ThumbnailFeature::m_AddThumbnailToDestroy(const IGFD_Thumbnail_Info& vIGFD_Thumbnail_Info) {
2789 // write => thread concurency issues
2790 m_ThumbnailToDestroyMutex.lock();
2791 m_ThumbnailToDestroy.push_back(vIGFD_Thumbnail_Info);
2792 m_ThumbnailToDestroyMutex.unlock();
2793}
2794
2795void IGFD::ThumbnailFeature::m_DrawDisplayModeToolBar() {
2796 if (IMGUI_RADIO_BUTTON(DisplayMode_FilesList_ButtonString, m_DisplayMode == DisplayModeEnum::FILE_LIST)) m_DisplayMode = DisplayModeEnum::FILE_LIST;
2797 if (ImGui::IsItemHovered()) ImGui::SetTooltip(DisplayMode_FilesList_ButtonHelp);
2798 ImGui::SameLine();
2799 if (IMGUI_RADIO_BUTTON(DisplayMode_ThumbailsList_ButtonString, m_DisplayMode == DisplayModeEnum::THUMBNAILS_LIST)) m_DisplayMode = DisplayModeEnum::THUMBNAILS_LIST;
2800 if (ImGui::IsItemHovered()) ImGui::SetTooltip(DisplayMode_ThumbailsList_ButtonHelp);
2801 ImGui::SameLine();
2802 /* todo
2803 if (IMGUI_RADIO_BUTTON(DisplayMode_ThumbailsGrid_ButtonString,
2804 m_DisplayMode == DisplayModeEnum::THUMBNAILS_GRID))
2805 m_DisplayMode = DisplayModeEnum::THUMBNAILS_GRID;
2806 if (ImGui::IsItemHovered()) ImGui::SetTooltip(DisplayMode_ThumbailsGrid_ButtonHelp);
2807 ImGui::SameLine();
2808 */
2809 m_DrawThumbnailGenerationProgress();
2810}
2811
2812void IGFD::ThumbnailFeature::m_ClearThumbnails(FileDialogInternal& vFileDialogInternal) {
2813 // directory wil be changed so the file list will be erased
2814 if (vFileDialogInternal.fileManager.puPathClicked) {
2815 size_t count = vFileDialogInternal.fileManager.GetFullFileListSize();
2816 for (size_t idx = 0U; idx < count; idx++) {
2817 auto file = vFileDialogInternal.fileManager.GetFullFileAt(idx);
2818 if (file.use_count()) {
2819 if (file->thumbnailInfo.isReadyToDisplay) //-V522
2820 {
2821 m_AddThumbnailToDestroy(file->thumbnailInfo);
2822 }
2823 }
2824 }
2825 }
2826}
2827
2828void IGFD::ThumbnailFeature::SetCreateThumbnailCallback(const CreateThumbnailFun& vCreateThumbnailFun) {
2829 m_CreateThumbnailFun = vCreateThumbnailFun;
2830}
2831
2832void IGFD::ThumbnailFeature::SetDestroyThumbnailCallback(const DestroyThumbnailFun& vCreateThumbnailFun) {
2833 m_DestroyThumbnailFun = vCreateThumbnailFun;
2834}
2835
2836void IGFD::ThumbnailFeature::ManageGPUThumbnails() {
2837 if (m_CreateThumbnailFun) {
2838 m_ThumbnailToCreateMutex.lock();
2839 if (!m_ThumbnailToCreate.empty()) {
2840 for (const auto& file : m_ThumbnailToCreate) {
2841 if (file.use_count()) {
2842 m_CreateThumbnailFun(&file->thumbnailInfo);
2843 }
2844 }
2845 m_ThumbnailToCreate.clear();
2846 }
2847 m_ThumbnailToCreateMutex.unlock();
2848 } else {
2849 printf(
2850 "No Callback found for create texture\nYou need to define the callback with a call to "
2851 "SetCreateThumbnailCallback\n");
2852 }
2853
2854 if (m_DestroyThumbnailFun) {
2855 m_ThumbnailToDestroyMutex.lock();
2856 if (!m_ThumbnailToDestroy.empty()) {
2857 for (auto thumbnail : m_ThumbnailToDestroy) {
2858 m_DestroyThumbnailFun(&thumbnail);
2859 }
2860 m_ThumbnailToDestroy.clear();
2861 }
2862 m_ThumbnailToDestroyMutex.unlock();
2863 } else {
2864 printf(
2865 "No Callback found for destroy texture\nYou need to define the callback with a call to "
2866 "SetCreateThumbnailCallback\n");
2867 }
2868}
2869
2870#endif // USE_THUMBNAILS
2871
2872//#pragma endregion
2873
2874//#pragma region BookMarkFeature
2875
2876IGFD::BookMarkFeature::BookMarkFeature() {
2877#ifdef USE_BOOKMARK
2878 m_BookmarkWidth = defaultBookmarkPaneWith;
2879#endif // USE_BOOKMARK
2880}
2881
2882#ifdef USE_BOOKMARK
2883void IGFD::BookMarkFeature::m_DrawBookmarkButton() {
2884 IMGUI_TOGGLE_BUTTON(bookmarksButtonString, &m_BookmarkPaneShown);
2885
2886 if (ImGui::IsItemHovered()) ImGui::SetTooltip(bookmarksButtonHelpString);
2887}
2888
2889bool IGFD::BookMarkFeature::m_DrawBookmarkPane(FileDialogInternal& vFileDialogInternal, const ImVec2& vSize) {
2890 bool res = false;
2891
2892 ImGui::BeginChild("##bookmarkpane", vSize);
2893
2894 static int selectedBookmarkForEdition = -1;
2895
2896 if (IMGUI_BUTTON(addBookmarkButtonString "##ImGuiFileDialogAddBookmark")) {
2897 if (!vFileDialogInternal.fileManager.IsComposerEmpty()) {
2898 BookmarkStruct bookmark;
2899 bookmark.name = vFileDialogInternal.fileManager.GetBack();
2900 bookmark.path = vFileDialogInternal.fileManager.GetCurrentPath();
2901 m_Bookmarks.push_back(bookmark);
2902 }
2903 }
2904 if (selectedBookmarkForEdition >= 0 && selectedBookmarkForEdition < (int)m_Bookmarks.size()) {
2905 ImGui::SameLine();
2906 if (IMGUI_BUTTON(removeBookmarkButtonString "##ImGuiFileDialogAddBookmark")) {
2907 m_Bookmarks.erase(m_Bookmarks.begin() + selectedBookmarkForEdition);
2908 if (selectedBookmarkForEdition == (int)m_Bookmarks.size()) selectedBookmarkForEdition--;
2909 }
2910
2911 if (selectedBookmarkForEdition >= 0 && selectedBookmarkForEdition < (int)m_Bookmarks.size()) {
2912 ImGui::SameLine();
2913
2914 ImGui::PushItemWidth(vSize.x - ImGui::GetCursorPosX());
2915 if (ImGui::InputText("##ImGuiFileDialogBookmarkEdit", m_BookmarkEditBuffer, MAX_FILE_DIALOG_NAME_BUFFER)) {
2916 m_Bookmarks[(size_t)selectedBookmarkForEdition].name = std::string(m_BookmarkEditBuffer);
2917 }
2918 ImGui::PopItemWidth();
2919 }
2920 }
2921
2922 ImGui::Separator();
2923
2924 if (!m_Bookmarks.empty()) {
2925 m_BookmarkClipper.Begin((int)m_Bookmarks.size(), ImGui::GetTextLineHeightWithSpacing());
2926 while (m_BookmarkClipper.Step()) {
2927 for (int i = m_BookmarkClipper.DisplayStart; i < m_BookmarkClipper.DisplayEnd; i++) {
2928 if (i < 0) continue;
2929 const BookmarkStruct& bookmark = m_Bookmarks[(size_t)i];
2930 ImGui::PushID(i);
2931 if (ImGui::Selectable(bookmark.name.c_str(), selectedBookmarkForEdition == i,
2932 ImGuiSelectableFlags_AllowDoubleClick) ||
2933 (selectedBookmarkForEdition == -1 && bookmark.path == vFileDialogInternal.fileManager.GetCurrentPath())) // select if path is current
2934 {
2935 selectedBookmarkForEdition = i;
2936 IGFD::Utils::ResetBuffer(m_BookmarkEditBuffer);
2937 IGFD::Utils::AppendToBuffer(m_BookmarkEditBuffer, MAX_FILE_DIALOG_NAME_BUFFER, bookmark.name);
2938
2939 if (ImGui::IsMouseDoubleClicked(0)) // apply path
2940 {
2941 vFileDialogInternal.fileManager.SetCurrentPath(bookmark.path);
2942 vFileDialogInternal.fileManager.OpenCurrentPath(vFileDialogInternal);
2943 res = true;
2944 }
2945 }
2946 ImGui::PopID();
2947 if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", bookmark.path.c_str()); //-V111
2948 }
2949 }
2950 m_BookmarkClipper.End();
2951 }
2952
2953 ImGui::EndChild();
2954
2955 return res;
2956}
2957
2958std::string IGFD::BookMarkFeature::SerializeBookmarks(const bool& vDontSerializeCodeBasedBookmarks) {
2959 std::string res;
2960
2961 size_t idx = 0;
2962 for (auto& it : m_Bookmarks) {
2963 if (vDontSerializeCodeBasedBookmarks && it.defined_by_code) continue;
2964
2965 if (idx++ != 0) res += "##"; // ## because reserved by imgui, so an input text cant have ##
2966
2967 res += it.name + "##" + it.path;
2968 }
2969
2970 return res;
2971}
2972
2973void IGFD::BookMarkFeature::DeserializeBookmarks(const std::string& vBookmarks) {
2974 if (!vBookmarks.empty()) {
2975 m_Bookmarks.clear();
2976 auto arr = IGFD::Utils::SplitStringToVector(vBookmarks, '#', false);
2977 for (size_t i = 0; i < arr.size(); i += 2) {
2978 if (i + 1 < arr.size()) // for avoid crash if arr size is impair due to user mistake after edition
2979 {
2980 BookmarkStruct bookmark;
2981 bookmark.name = arr[i];
2982 // if bad format we jump this bookmark
2983 bookmark.path = arr[i + 1];
2984 m_Bookmarks.push_back(bookmark);
2985 }
2986 }
2987 }
2988}
2989
2990void IGFD::BookMarkFeature::AddBookmark(const std::string& vBookMarkName, const std::string& vBookMarkPath) {
2991 if (vBookMarkName.empty() || vBookMarkPath.empty()) return;
2992
2993 BookmarkStruct bookmark;
2994 bookmark.name = vBookMarkName;
2995 bookmark.path = vBookMarkPath;
2996 bookmark.defined_by_code = true;
2997 m_Bookmarks.push_back(bookmark);
2998}
2999
3000bool IGFD::BookMarkFeature::RemoveBookmark(const std::string& vBookMarkName) {
3001 if (vBookMarkName.empty()) return false;
3002
3003 for (auto bookmark_it = m_Bookmarks.begin(); bookmark_it != m_Bookmarks.end(); ++bookmark_it) {
3004 if ((*bookmark_it).name == vBookMarkName) {
3005 m_Bookmarks.erase(bookmark_it);
3006 return true;
3007 }
3008 }
3009
3010 return false;
3011}
3012#endif // USE_BOOKMARK
3013
3014//#pragma endregion
3015
3016//#pragma region KeyExplorerFeature
3017
3018IGFD::KeyExplorerFeature::KeyExplorerFeature() = default;
3019
3020#ifdef USE_EXPLORATION_BY_KEYS
3021bool IGFD::KeyExplorerFeature::m_LocateItem_Loop(FileDialogInternal& vFileDialogInternal, ImWchar vC) {
3022 bool found = false;
3023
3024 auto& fdi = vFileDialogInternal.fileManager;
3025 if (!fdi.IsFilteredListEmpty()) {
3026 auto countFiles = fdi.GetFilteredListSize();
3027 for (size_t i = m_LocateFileByInputChar_lastFileIdx; i < countFiles; i++) {
3028 auto nfo = fdi.GetFilteredFileAt(i);
3029 if (nfo.use_count()) {
3030 if (nfo->fileNameExt_optimized[0] == vC || // lower case search //-V522
3031 nfo->fileNameExt[0] == vC) // maybe upper case search
3032 {
3033 // float p = ((float)i) * ImGui::GetTextLineHeightWithSpacing();
3034 float p = (float)((double)i / (double)countFiles) * ImGui::GetScrollMaxY();
3035 ImGui::SetScrollY(p);
3036 m_LocateFileByInputChar_lastFound = true;
3037 m_LocateFileByInputChar_lastFileIdx = i;
3038 m_StartFlashItem(m_LocateFileByInputChar_lastFileIdx);
3039
3040 auto infos = fdi.GetFilteredFileAt(m_LocateFileByInputChar_lastFileIdx);
3041 if (infos.use_count()) {
3042 if (infos->fileType.isDir()) //-V522
3043 {
3044 if (fdi.dLGDirectoryMode) // directory chooser
3045 {
3046 fdi.SelectFileName(vFileDialogInternal, infos);
3047 }
3048 } else {
3049 fdi.SelectFileName(vFileDialogInternal, infos);
3050 }
3051
3052 found = true;
3053 break;
3054 }
3055 }
3056 }
3057 }
3058 }
3059
3060 return found;
3061}
3062
3063void IGFD::KeyExplorerFeature::m_LocateByInputKey(FileDialogInternal& vFileDialogInternal) {
3064 ImGuiContext& g = *GImGui;
3065 auto& fdi = vFileDialogInternal.fileManager;
3066 if (!g.ActiveId && !fdi.IsFilteredListEmpty()) {
3067 auto& queueChar = ImGui::GetIO().InputQueueCharacters;
3068 auto countFiles = fdi.GetFilteredListSize();
3069
3070 // point by char
3071 if (!queueChar.empty()) {
3072 ImWchar c = queueChar.back();
3073 if (m_LocateFileByInputChar_InputQueueCharactersSize != queueChar.size()) {
3074 if (c == m_LocateFileByInputChar_lastChar) // next file starting with same char until
3075 {
3076 if (m_LocateFileByInputChar_lastFileIdx < countFiles - 1U)
3077 m_LocateFileByInputChar_lastFileIdx++;
3078 else
3079 m_LocateFileByInputChar_lastFileIdx = 0;
3080 }
3081
3082 if (!m_LocateItem_Loop(vFileDialogInternal, c)) {
3083 // not found, loop again from 0 this time
3084 m_LocateFileByInputChar_lastFileIdx = 0;
3085 m_LocateItem_Loop(vFileDialogInternal, c);
3086 }
3087
3088 m_LocateFileByInputChar_lastChar = c;
3089 }
3090 }
3091
3092 m_LocateFileByInputChar_InputQueueCharactersSize = queueChar.size();
3093 }
3094}
3095
3096void IGFD::KeyExplorerFeature::m_ExploreWithkeys(FileDialogInternal& vFileDialogInternal, ImGuiID vListViewID) {
3097 auto& fdi = vFileDialogInternal.fileManager;
3098 if (!fdi.IsFilteredListEmpty()) {
3099 bool canWeExplore = false;
3100 bool hasNav = (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
3101
3102 ImGuiContext& g = *GImGui;
3103 if (!hasNav && !g.ActiveId) // no nav and no activated inputs
3104 canWeExplore = true;
3105
3106 if (g.NavId && g.NavId == vListViewID) {
3107 if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter) || ImGui::IsKeyPressed(ImGuiKey_Space)) {
3108 ImGui::ActivateItemByID(vListViewID);
3109 ImGui::SetActiveID(vListViewID, g.CurrentWindow);
3110 }
3111 }
3112
3113 if (vListViewID == g.LastActiveId - 1) // if listview id is the last acticated nav id (ImGui::ActivateItemByID(vListViewID);)
3114 canWeExplore = true;
3115
3116 if (canWeExplore && ImGui::IsWindowFocused()) {
3117 if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
3118 ImGui::ClearActiveID();
3119 g.LastActiveId = 0;
3120 }
3121
3122 auto countFiles = fdi.GetFilteredListSize();
3123
3124 // explore
3125 bool exploreByKey = false;
3126 bool enterInDirectory = false;
3127 bool exitDirectory = false;
3128
3129 if ((hasNav && ImGui::IsKeyPressed(ImGuiKey_UpArrow)) || (!hasNav && ImGui::IsKeyPressed(ImGuiKey_UpArrow))) {
3130 exploreByKey = true;
3131 if (m_LocateFileByInputChar_lastFileIdx > 0)
3132 m_LocateFileByInputChar_lastFileIdx--;
3133 else
3134 m_LocateFileByInputChar_lastFileIdx = countFiles - 1U;
3135 } else if ((hasNav && ImGui::IsKeyPressed(ImGuiKey_DownArrow)) || (!hasNav && ImGui::IsKeyPressed(ImGuiKey_DownArrow))) {
3136 exploreByKey = true;
3137 if (m_LocateFileByInputChar_lastFileIdx < countFiles - 1U)
3138 m_LocateFileByInputChar_lastFileIdx++;
3139 else
3140 m_LocateFileByInputChar_lastFileIdx = 0U;
3141 } else if (ImGui::IsKeyReleased(ImGuiKey_Enter)) {
3142 exploreByKey = true;
3143 enterInDirectory = true;
3144 } else if (ImGui::IsKeyReleased(ImGuiKey_Backspace)) {
3145 exploreByKey = true;
3146 exitDirectory = true;
3147 }
3148
3149 if (exploreByKey) {
3150 // float totalHeight = m_FilteredFileList.size() * ImGui::GetTextLineHeightWithSpacing();
3151 float p = (float)((double)m_LocateFileByInputChar_lastFileIdx / (double)(countFiles - 1U)) * ImGui::GetScrollMaxY(); // seems not udpated in tables version outside tables
3152 // float p = ((float)locateFileByInputChar_lastFileIdx) * ImGui::GetTextLineHeightWithSpacing();
3153 ImGui::SetScrollY(p);
3154 m_StartFlashItem(m_LocateFileByInputChar_lastFileIdx);
3155
3156 auto infos = fdi.GetFilteredFileAt(m_LocateFileByInputChar_lastFileIdx);
3157 if (infos.use_count()) {
3158 if (infos->fileType.isDir()) //-V522
3159 {
3160 if (!fdi.dLGDirectoryMode || enterInDirectory) {
3161 if (enterInDirectory) {
3162 if (fdi.SelectDirectory(infos)) {
3163 // changement de repertoire
3164 vFileDialogInternal.fileManager.OpenCurrentPath(vFileDialogInternal);
3165 if (m_LocateFileByInputChar_lastFileIdx > countFiles - 1U) {
3166 m_LocateFileByInputChar_lastFileIdx = 0;
3167 }
3168 }
3169 }
3170 } else // directory chooser
3171 {
3172 fdi.SelectFileName(vFileDialogInternal, infos);
3173 }
3174 } else {
3175 fdi.SelectFileName(vFileDialogInternal, infos);
3176
3177 if (enterInDirectory) {
3178 vFileDialogInternal.isOk = true;
3179 }
3180 }
3181
3182 if (exitDirectory) {
3183 auto nfo = std::make_shared<FileInfos>();
3184 nfo->fileNameExt = "..";
3185
3186 if (fdi.SelectDirectory(nfo)) {
3187 // changement de repertoire
3188 vFileDialogInternal.fileManager.OpenCurrentPath(vFileDialogInternal);
3189 if (m_LocateFileByInputChar_lastFileIdx > countFiles - 1U) {
3190 m_LocateFileByInputChar_lastFileIdx = 0;
3191 }
3192 }
3193#ifdef _IGFD_WIN_
3194 else {
3195 if (fdi.GetComposerSize() == 1U) {
3196 if (fdi.GetDrives()) {
3197 fdi.ApplyFilteringOnFileList(vFileDialogInternal);
3198 }
3199 }
3200 }
3201#endif // _IGFD_WIN_
3202 }
3203 }
3204 }
3205 }
3206 }
3207}
3208
3209bool IGFD::KeyExplorerFeature::m_FlashableSelectable(const char* label, bool selected, ImGuiSelectableFlags flags, bool vFlashing, const ImVec2& size_arg) {
3210 using namespace ImGui;
3211
3212 ImGuiWindow* window = GetCurrentWindow();
3213 if (window->SkipItems) return false;
3214
3215 ImGuiContext& g = *GImGui;
3216 const ImGuiStyle& style = g.Style;
3217
3218 // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle.
3219 ImGuiID id = window->GetID(label);
3220 ImVec2 label_size = CalcTextSize(label, NULL, true);
3221 ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
3222 ImVec2 pos = window->DC.CursorPos;
3223 pos.y += window->DC.CurrLineTextBaseOffset;
3224 ItemSize(size, 0.0f);
3225
3226 // Fill horizontal space
3227 // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match
3228 // other widgets.
3229 const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0;
3230 const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x;
3231 const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x;
3232 if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth)) size.x = ImMax(label_size.x, max_x - min_x);
3233
3234 // Text stays at the submission position, but bounding box may be extended on both sides
3235 const ImVec2 text_min = pos;
3236 const ImVec2 text_max(min_x + size.x, pos.y + size.y);
3237
3238 // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable.
3239 ImRect bb(min_x, pos.y, text_max.x, text_max.y);
3240 if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) {
3241 const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x;
3242 const float spacing_y = style.ItemSpacing.y;
3243 const float spacing_L = IM_TRUNC(spacing_x * 0.50f);
3244 const float spacing_U = IM_TRUNC(spacing_y * 0.50f);
3245 bb.Min.x -= spacing_L;
3246 bb.Min.y -= spacing_U;
3247 bb.Max.x += (spacing_x - spacing_L);
3248 bb.Max.y += (spacing_y - spacing_U);
3249 }
3250 // if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); }
3251
3252 // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackground for every Selectable..
3253 const float backup_clip_rect_min_x = window->ClipRect.Min.x;
3254 const float backup_clip_rect_max_x = window->ClipRect.Max.x;
3255 if (span_all_columns) {
3256 window->ClipRect.Min.x = window->ParentWorkRect.Min.x;
3257 window->ClipRect.Max.x = window->ParentWorkRect.Max.x;
3258 }
3259
3260 const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0;
3261 const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? ImGuiItemFlags_Disabled : ImGuiItemFlags_None);
3262 if (span_all_columns) {
3263 window->ClipRect.Min.x = backup_clip_rect_min_x;
3264 window->ClipRect.Max.x = backup_clip_rect_max_x;
3265 }
3266
3267 if (!item_add) return false;
3268
3269 const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
3270 if (disabled_item && !disabled_global) // Only testing this as an optimization
3271 BeginDisabled();
3272
3273 // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only,
3274 // which would be advantageous since most selectable are not selected.
3275 if (span_all_columns && window->DC.CurrentColumns)
3276 PushColumnsBackground();
3277 else if (span_all_columns && g.CurrentTable)
3278 TablePushBackgroundChannel();
3279
3280 // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
3281 ImGuiButtonFlags button_flags = 0;
3282 if (flags & ImGuiSelectableFlags_NoHoldingActiveID) {
3283 button_flags |= ImGuiButtonFlags_NoHoldingActiveId;
3284 }
3285 if (flags & ImGuiSelectableFlags_NoSetKeyOwner) {
3286 button_flags |= ImGuiButtonFlags_NoSetKeyOwner;
3287 }
3288 if (flags & ImGuiSelectableFlags_SelectOnClick) {
3289 button_flags |= ImGuiButtonFlags_PressedOnClick;
3290 }
3291 if (flags & ImGuiSelectableFlags_SelectOnRelease) {
3292 button_flags |= ImGuiButtonFlags_PressedOnRelease;
3293 }
3294 if (flags & ImGuiSelectableFlags_AllowDoubleClick) {
3295 button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
3296 }
3297 if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap)) {
3298 button_flags |= ImGuiButtonFlags_AllowOverlap;
3299 }
3300
3301 const bool was_selected = selected;
3302 bool hovered, held;
3303 bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
3304
3305 // Auto-select when moved into
3306 // - This will be more fully fleshed in the range-select branch
3307 // - This is not exposed as it won't nicely work with some user side handling of shift/control
3308 // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons
3309 // - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope())
3310 // - (2) usage will fail with clipped items
3311 // The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API.
3312 if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId)
3313 if (g.NavJustMovedToId == id) selected = pressed = true;
3314
3315 // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard
3316 if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover))) {
3317 if (!g.NavHighlightItemUnderNav && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) {
3318 SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, WindowRectAbsToRel(window, bb)); // (bb == NavRect)
3319 g.NavCursorVisible = true;
3320 }
3321 }
3322 if (pressed) MarkItemEdited(id);
3323
3324 // In this branch, Selectable() cannot toggle the selection so this will never trigger.
3325 if (selected != was_selected) //-V547
3326 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection;
3327
3329 // this function copy ImGui::Selectable just for this line....
3330 hovered |= vFlashing;
3332
3333 // Render
3334 if (hovered || selected) {
3335 const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
3336 RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
3337 }
3338 if (g.NavId == id) RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding);
3339
3340 if (span_all_columns && window->DC.CurrentColumns)
3341 PopColumnsBackground();
3342 else if (span_all_columns && g.CurrentTable)
3343 TablePopBackgroundChannel();
3344
3345 RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb);
3346
3347 // Automatically close popups
3348 if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups)) CloseCurrentPopup();
3349
3350 if (disabled_item && !disabled_global) EndDisabled();
3351
3352 IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
3353 return pressed; //-V1020
3354}
3355
3356void IGFD::KeyExplorerFeature::m_StartFlashItem(size_t vIdx) {
3357 m_FlashAlpha = 1.0f;
3358 m_FlashedItem = vIdx;
3359}
3360
3361bool IGFD::KeyExplorerFeature::m_BeginFlashItem(size_t vIdx) {
3362 bool res = false;
3363
3364 if (m_FlashedItem == vIdx && std::abs(m_FlashAlpha - 0.0f) > 0.00001f) {
3365 m_FlashAlpha -= m_FlashAlphaAttenInSecs * ImGui::GetIO().DeltaTime;
3366 if (m_FlashAlpha < 0.0f) m_FlashAlpha = 0.0f;
3367
3368 ImVec4 hov = ImGui::GetStyleColorVec4(ImGuiCol_HeaderHovered);
3369 hov.w = m_FlashAlpha;
3370 ImGui::PushStyleColor(ImGuiCol_HeaderHovered, hov);
3371 res = true;
3372 }
3373
3374 return res;
3375}
3376
3377void IGFD::KeyExplorerFeature::m_EndFlashItem() {
3378 ImGui::PopStyleColor();
3379}
3380
3381void IGFD::KeyExplorerFeature::SetFlashingAttenuationInSeconds(float vAttenValue) {
3382 m_FlashAlphaAttenInSecs = 1.0f / ImMax(vAttenValue, 0.01f);
3383}
3384#endif // USE_EXPLORATION_BY_KEYS
3385
3386//#pragma endregion
3387
3388//#pragma endregion
3389
3390//#pragma region FileDialog
3391
3392IGFD::FileDialog::FileDialog() : BookMarkFeature(), KeyExplorerFeature(), ThumbnailFeature() {
3393}
3394IGFD::FileDialog::~FileDialog() = default;
3395
3399
3400// path and fileNameExt can be specified
3401void IGFD::FileDialog::OpenDialog(const std::string& vKey, const std::string& vTitle, const char* vFilters, const FileDialogConfig& vConfig) {
3402 if (m_FileDialogInternal.showDialog) // if already opened, quit
3403 return;
3404 m_FileDialogInternal.configureDialog(vKey, vTitle, vFilters, vConfig);
3405}
3406
3410
3411bool IGFD::FileDialog::Display(const std::string& vKey, ImGuiWindowFlags vFlags, ImVec2 vMinSize, ImVec2 vMaxSize) {
3412 bool res = false;
3413
3414 if (m_FileDialogInternal.showDialog && m_FileDialogInternal.dLGkey == vKey) {
3415 if (m_FileDialogInternal.puUseCustomLocale) setlocale(m_FileDialogInternal.localeCategory, m_FileDialogInternal.localeBegin.c_str());
3416
3417 auto& fdFile = m_FileDialogInternal.fileManager;
3418 auto& fdFilter = m_FileDialogInternal.filterManager;
3419
3420 static ImGuiWindowFlags flags; // todo: not a good solution for multi instance, to fix
3421
3422 // to be sure than only one dialog is displayed per frame
3423 ImGuiContext& g = *GImGui;
3424 if (g.FrameCount == m_FileDialogInternal.lastImGuiFrameCount) // one instance was displayed this frame before
3425 // for this key +> quit
3426 return res;
3427 m_FileDialogInternal.lastImGuiFrameCount = g.FrameCount; // mark this instance as used this frame
3428
3429 std::string name = m_FileDialogInternal.dLGtitle + "##" + m_FileDialogInternal.dLGkey;
3430 if (m_FileDialogInternal.name != name) {
3431 fdFile.ClearComposer();
3432 fdFile.ClearFileLists();
3433 flags = vFlags;
3434 }
3435
3436 m_NewFrame();
3437
3438#ifdef IMGUI_HAS_VIEWPORT
3439 if (!ImGui::GetIO().ConfigViewportsNoDecoration) {
3440 // https://github.com/ocornut/imgui/issues/4534
3441 ImGuiWindowClass window_class;
3442 window_class.ViewportFlagsOverrideClear = ImGuiViewportFlags_NoDecoration;
3443 ImGui::SetNextWindowClass(&window_class);
3444 }
3445#endif // IMGUI_HAS_VIEWPORT
3446
3447 bool beg = false;
3448 if (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_NoDialog) // disable our own dialog system (standard or modal)
3449 {
3450 beg = true;
3451 } else {
3452 ImGui::SetNextWindowSizeConstraints(vMinSize, vMaxSize);
3453
3454 if (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_Modal && !m_FileDialogInternal.okResultToConfirm) // disable modal because the confirm dialog for overwrite is
3455 // a new modal
3456 {
3457 ImGui::OpenPopup(name.c_str());
3458 beg = ImGui::BeginPopupModal(name.c_str(), (bool*)nullptr, flags | ImGuiWindowFlags_NoScrollbar);
3459 } else {
3460 beg = ImGui::Begin(name.c_str(), (bool*)nullptr, flags | ImGuiWindowFlags_NoScrollbar);
3461 }
3462 }
3463 if (beg) {
3464#ifdef IMGUI_HAS_VIEWPORT
3465 // if decoration is enabled we disable the resizing feature of imgui for avoid crash with SDL2 and GLFW3
3466 if (ImGui::GetIO().ConfigViewportsNoDecoration) {
3467 flags = vFlags;
3468 } else {
3469 auto win = ImGui::GetCurrentWindowRead();
3470 if (win->Viewport->Idx != 0)
3471 flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar;
3472 else
3473 flags = vFlags;
3474 }
3475#endif // IMGUI_HAS_VIEWPORT
3476
3477 ImGuiID _frameId = ImGui::GetID(name.c_str());
3478 ImVec2 frameSize = ImVec2(0, 0);
3479 if (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_NoDialog) frameSize = vMaxSize;
3480 if (ImGui::BeginChild(_frameId, frameSize, false, flags | ImGuiWindowFlags_NoScrollbar)) {
3481 m_FileDialogInternal.name = name; //-V820
3482
3483 if (fdFile.dLGpath.empty()) fdFile.dLGpath = "."; // defaut path is '.'
3484
3485 fdFilter.SetDefaultFilterIfNotDefined();
3486
3487 // init list of files
3488 if (fdFile.IsFileListEmpty() && !fdFile.showDrives) {
3489 if (fdFile.dLGpath != ".") // Removes extension seperator in filename if we don't check
3490 IGFD::Utils::ReplaceString(fdFile.dLGDefaultFileName, fdFile.dLGpath, ""); // local path
3491
3492 if (!fdFile.dLGDefaultFileName.empty()) {
3493 fdFile.SetDefaultFileName(fdFile.dLGDefaultFileName);
3494 fdFilter.SetSelectedFilterWithExt(fdFilter.dLGdefaultExt);
3495 } else if (fdFile.dLGDirectoryMode) // directory mode
3496 fdFile.SetDefaultFileName(".");
3497 fdFile.ScanDir(m_FileDialogInternal, fdFile.dLGpath);
3498 }
3499
3500 // draw dialog parts
3501 m_DrawHeader(); // bookmark, directory, path
3502 m_DrawContent(); // bookmark, files view, side pane
3503 res = m_DrawFooter(); // file field, filter combobox, ok/cancel buttons
3504
3505 m_EndFrame();
3506 }
3507 ImGui::EndChild();
3508
3509 // for display in dialog center, the confirm to overwrite dlg
3510 m_FileDialogInternal.dialogCenterPos = ImGui::GetCurrentWindowRead()->ContentRegionRect.GetCenter();
3511
3512 // when the confirm to overwrite dialog will appear we need to
3513 // disable the modal mode of the main file dialog
3514 // see prOkResultToConfirm under
3515 if (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_Modal && !m_FileDialogInternal.okResultToConfirm) {
3516 ImGui::EndPopup();
3517 }
3518 }
3519
3520 if (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_NoDialog) { // disable our own dialog system (standard or modal)
3521 } else {
3522 // same things here regarding prOkResultToConfirm
3523 if (!(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_Modal) || m_FileDialogInternal.okResultToConfirm) {
3524 ImGui::End();
3525 }
3526 }
3527 // confirm the result and show the confirm to overwrite dialog if needed
3528 res = m_Confirm_Or_OpenOverWriteFileDialog_IfNeeded(res, vFlags);
3529
3530 if (m_FileDialogInternal.puUseCustomLocale) setlocale(m_FileDialogInternal.localeCategory, m_FileDialogInternal.localeEnd.c_str());
3531 }
3532
3533 return res;
3534}
3535
3536void IGFD::FileDialog::m_NewFrame() {
3537 m_FileDialogInternal.NewFrame();
3538 m_NewThumbnailFrame(m_FileDialogInternal);
3539}
3540
3541void IGFD::FileDialog::m_EndFrame() {
3542 m_EndThumbnailFrame(m_FileDialogInternal);
3543 m_FileDialogInternal.EndFrame();
3544}
3545void IGFD::FileDialog::m_QuitFrame() {
3546 m_QuitThumbnailFrame(m_FileDialogInternal);
3547}
3548
3549void IGFD::FileDialog::m_DrawHeader() {
3550#ifdef USE_BOOKMARK
3551 if (!(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableBookmarkMode)) {
3552 m_DrawBookmarkButton();
3553 ImGui::SameLine();
3554 }
3555
3556#endif // USE_BOOKMARK
3557
3558 m_FileDialogInternal.fileManager.DrawDirectoryCreation(m_FileDialogInternal);
3559
3560 if (
3561#ifdef USE_BOOKMARK
3562 !(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableBookmarkMode) ||
3563#endif // USE_BOOKMARK
3564 !(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableCreateDirectoryButton)) {
3565 ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
3566 ImGui::SameLine();
3567 }
3568 m_FileDialogInternal.fileManager.DrawPathComposer(m_FileDialogInternal);
3569
3570#ifdef USE_THUMBNAILS
3571 if (!(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableThumbnailMode)) {
3572 m_DrawDisplayModeToolBar();
3573 ImGui::SameLine();
3574 ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
3575 ImGui::SameLine();
3576 }
3577#endif // USE_THUMBNAILS
3578
3579 m_FileDialogInternal.searchManager.DrawSearchBar(m_FileDialogInternal);
3580}
3581
3582void IGFD::FileDialog::m_DrawContent() {
3583 ImVec2 size = ImGui::GetContentRegionAvail() - ImVec2(0.0f, m_FileDialogInternal.footerHeight);
3584
3585#ifdef USE_BOOKMARK
3586 if (!(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableBookmarkMode)) {
3587 if (m_BookmarkPaneShown) {
3588 // size.x -= m_BookmarkWidth;
3589 float otherWidth = size.x - m_BookmarkWidth;
3590 ImGui::PushID("##splitterbookmark");
3591 IGFD::Utils::ImSplitter(true, 4.0f, &m_BookmarkWidth, &otherWidth, 10.0f, 10.0f + m_FileDialogInternal.getDialogConfig().sidePaneWidth, size.y);
3592 ImGui::PopID();
3593 size.x -= otherWidth;
3594 m_DrawBookmarkPane(m_FileDialogInternal, size);
3595 ImGui::SameLine();
3596 }
3597 }
3598#endif // USE_BOOKMARK
3599
3600 size.x = ImGui::GetContentRegionAvail().x - m_FileDialogInternal.getDialogConfig().sidePaneWidth;
3601
3602 if (m_FileDialogInternal.getDialogConfig().sidePane) {
3603 ImGui::PushID("##splittersidepane");
3604 IGFD::Utils::ImSplitter(true, 4.0f, &size.x, &m_FileDialogInternal.getDialogConfigRef().sidePaneWidth, 10.0f, 10.0f, size.y);
3605 ImGui::PopID();
3606 }
3607
3608#ifdef USE_THUMBNAILS
3609 if (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableThumbnailMode) {
3610 m_DrawFileListView(size);
3611 } else {
3612 switch (m_DisplayMode) {
3613 case DisplayModeEnum::FILE_LIST: m_DrawFileListView(size); break;
3614 case DisplayModeEnum::THUMBNAILS_LIST: m_DrawThumbnailsListView(size); break;
3615 case DisplayModeEnum::THUMBNAILS_GRID: m_DrawThumbnailsGridView(size);
3616 }
3617 }
3618#else // USE_THUMBNAILS
3619 m_DrawFileListView(size);
3620#endif // USE_THUMBNAILS
3621
3622 if (m_FileDialogInternal.getDialogConfig().sidePane) {
3623 m_DrawSidePane(size.y);
3624 }
3625
3626 if (!(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableQuickPathSelection)) {
3627 m_DisplayPathPopup(size);
3628 }
3629}
3630
3631void IGFD::FileDialog::m_DisplayPathPopup(ImVec2 vSize) {
3632 ImVec2 size = ImVec2(vSize.x * 0.5f, vSize.y * 0.5f);
3633 if (ImGui::BeginPopup("IGFD_Path_Popup")) {
3634 auto& fdi = m_FileDialogInternal.fileManager;
3635
3636 ImGui::PushID(this);
3637
3638 static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_NoHostExtendY;
3639 auto listViewID = ImGui::GetID("##FileDialog_pathTable");
3640 if (ImGui::BeginTableEx("##FileDialog_pathTable", listViewID, 1, flags, size, 0.0f)) //-V112
3641 {
3642 ImGui::TableSetupScrollFreeze(0, 1); // Make header always visible
3643 ImGui::TableSetupColumn(tableHeaderFileNameString, ImGuiTableColumnFlags_WidthStretch | (defaultSortOrderFilename ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending), -1, 0);
3644
3645 ImGui::TableHeadersRow();
3646
3647 if (!fdi.IsPathFilteredListEmpty()) {
3648 std::string _str;
3649 ImFont* _font = nullptr;
3650 bool _showColor = false;
3651
3652 m_PathListClipper.Begin((int)fdi.GetPathFilteredListSize(), ImGui::GetTextLineHeightWithSpacing());
3653 while (m_PathListClipper.Step()) {
3654 for (int i = m_PathListClipper.DisplayStart; i < m_PathListClipper.DisplayEnd; i++) {
3655 if (i < 0) continue;
3656
3657 auto infos = fdi.GetFilteredPathAt((size_t)i);
3658 if (!infos.use_count()) continue;
3659
3660 m_BeginFileColorIconStyle(infos, _showColor, _str, &_font);
3661
3662 bool selected = fdi.IsFileNameSelected(infos->fileNameExt); // found
3663
3664 ImGui::TableNextRow();
3665
3666 if (ImGui::TableNextColumn()) // file name
3667 {
3668 if (ImGui::Selectable(infos->fileNameExt.c_str(), &selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SpanAvailWidth)) {
3669 fdi.SetCurrentPath(fdi.ComposeNewPath(fdi.GetCurrentPopupComposedPath()));
3670 fdi.puPathClicked = fdi.SelectDirectory(infos);
3671 ImGui::CloseCurrentPopup();
3672 }
3673 }
3674
3675 m_EndFileColorIconStyle(_showColor, _font);
3676 }
3677 }
3678 m_PathListClipper.End();
3679 }
3680
3681 ImGui::EndTable();
3682 }
3683
3684 ImGui::PopID();
3685
3686 ImGui::EndPopup();
3687 }
3688}
3689
3690bool IGFD::FileDialog::m_DrawOkButton() {
3691 auto& fdFile = m_FileDialogInternal.fileManager;
3692 if (m_FileDialogInternal.canWeContinue && strlen(fdFile.fileNameBuffer)) {
3693 if (IMGUI_BUTTON(okButtonString "##validationdialog", ImVec2(okButtonWidth, 0.0f)) || m_FileDialogInternal.isOk) {
3694 m_FileDialogInternal.isOk = true;
3695 return true;
3696 }
3697
3698#if !invertOkAndCancelButtons
3699 ImGui::SameLine();
3700#endif
3701 }
3702
3703 return false;
3704}
3705
3706bool IGFD::FileDialog::m_DrawCancelButton() {
3707 if (IMGUI_BUTTON(cancelButtonString "##validationdialog", ImVec2(cancelButtonWidth, 0.0f)) || m_FileDialogInternal.needToExitDialog) // dialog exit asked
3708 {
3709 m_FileDialogInternal.isOk = false;
3710 return true;
3711 }
3712
3713#if invertOkAndCancelButtons
3714 ImGui::SameLine();
3715#endif
3716
3717 return false;
3718}
3719
3720bool IGFD::FileDialog::m_DrawValidationButtons() {
3721 bool res = false;
3722
3723 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (ImGui::GetContentRegionAvail().x - prOkCancelButtonWidth) * okCancelButtonAlignement);
3724
3725 ImGui::BeginGroup();
3726
3727 if (invertOkAndCancelButtons) {
3728 res |= m_DrawCancelButton();
3729 res |= m_DrawOkButton();
3730 } else {
3731 res |= m_DrawOkButton();
3732 res |= m_DrawCancelButton();
3733 }
3734
3735 ImGui::EndGroup();
3736
3737 prOkCancelButtonWidth = ImGui::GetItemRectSize().x;
3738
3739 return res;
3740}
3741
3742bool IGFD::FileDialog::m_DrawFooter() {
3743 auto& fdFile = m_FileDialogInternal.fileManager;
3744
3745 float posY = ImGui::GetCursorPos().y; // height of last bar calc
3746 ImGui::AlignTextToFramePadding();
3747 if (!fdFile.dLGDirectoryMode)
3748 ImGui::Text(fileNameString);
3749 else // directory chooser
3750 ImGui::Text(dirNameString);
3751 ImGui::SameLine();
3752
3753 // Input file fields
3754 float width = ImGui::GetContentRegionAvail().x;
3755 if (!fdFile.dLGDirectoryMode) {
3756 ImGuiContext& g = *GImGui;
3757 width -= m_FileDialogInternal.filterManager.GetFilterComboBoxWidth() + g.Style.ItemSpacing.x;
3758 }
3759
3760 ImGui::PushItemWidth(width);
3761 ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue;
3762 if (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_ReadOnlyFileNameField) {
3763 flags |= ImGuiInputTextFlags_ReadOnly;
3764 }
3765 if (ImGui::InputText("##FileName", fdFile.fileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, flags)) {
3766 m_FileDialogInternal.isOk = true;
3767 }
3768 if (ImGui::GetItemID() == ImGui::GetActiveID()) m_FileDialogInternal.fileInputIsActive = true;
3769 ImGui::PopItemWidth();
3770
3771 // combobox of filters
3772 m_FileDialogInternal.filterManager.DrawFilterComboBox(m_FileDialogInternal);
3773
3774 bool res = m_DrawValidationButtons();
3775 m_FileDialogInternal.footerHeight = ImGui::GetCursorPosY() - posY;
3776 return res;
3777}
3778
3779void IGFD::FileDialog::m_SelectableItem(int vidx, std::shared_ptr<FileInfos> vInfos, bool vSelected, const char* vFmt, ...) {
3780 if (!vInfos.use_count()) return;
3781
3782 auto& fdi = m_FileDialogInternal.fileManager;
3783
3784 static ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags_AllowDoubleClick | ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SpanAvailWidth;
3785
3786 va_list args;
3787 va_start(args, vFmt);
3788 vsnprintf(fdi.variadicBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFmt, args);
3789 va_end(args);
3790
3791 float h = 0.0f;
3792#ifdef USE_THUMBNAILS
3793 if (m_DisplayMode == DisplayModeEnum::THUMBNAILS_LIST && !(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableThumbnailMode)) {
3794 h = DisplayMode_ThumbailsList_ImageHeight;
3795 }
3796#endif // USE_THUMBNAILS
3797#ifdef USE_EXPLORATION_BY_KEYS
3798 bool flashed = m_BeginFlashItem((size_t)vidx);
3799 bool res = m_FlashableSelectable(fdi.variadicBuffer, vSelected, selectableFlags, flashed, ImVec2(-1.0f, h));
3800 if (flashed) m_EndFlashItem();
3801#else // USE_EXPLORATION_BY_KEYS
3802 (void)vidx; // remove a warnings ofr unused var
3803
3804 bool res = ImGui::Selectable(fdi.variadicBuffer, vSelected, selectableFlags, ImVec2(-1.0f, h));
3805#endif // USE_EXPLORATION_BY_KEYS
3806 if (res) {
3807 if (vInfos->fileType.isDir()) {
3808 // nav system, selectable cause open directory or select directory
3809 if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) {
3810 // little fix for get back the mouse behavior in nav system
3811 if (ImGui::IsMouseDoubleClicked(0)) // 0 -> left mouse button double click
3812 {
3813 fdi.puPathClicked = fdi.SelectDirectory(vInfos);
3814 } else if (fdi.dLGDirectoryMode) // directory chooser
3815 {
3816 fdi.SelectFileName(m_FileDialogInternal, vInfos);
3817 } else {
3818 //fdi.puPathClicked = fdi.SelectDirectory(vInfos);
3819 }
3820 } else // no nav system => classic behavior
3821 {
3822 if (ImGui::IsMouseDoubleClicked(0)) // 0 -> left mouse button double click
3823 {
3824 fdi.puPathClicked = fdi.SelectDirectory(vInfos);
3825 } else if (fdi.dLGDirectoryMode) // directory chooser
3826 {
3827 fdi.SelectFileName(m_FileDialogInternal, vInfos);
3828 }
3829 }
3830 } else {
3831 fdi.SelectFileName(m_FileDialogInternal, vInfos);
3832
3833 if (ImGui::IsMouseDoubleClicked(0)) {
3834 m_FileDialogInternal.isOk = true;
3835 }
3836 }
3837 }
3838}
3839
3840void IGFD::FileDialog::m_DisplayFileInfosTooltip(const int32_t& vRowIdx, const int32_t& vColumnIdx, std::shared_ptr<FileInfos> vFileInfos) {
3841 if (ImGui::IsItemHovered()) {
3842 if (vFileInfos != nullptr && vFileInfos->tooltipColumn == vColumnIdx) {
3843 if (!vFileInfos->tooltipMessage.empty()) {
3844 ImGui::SetTooltip("%s", vFileInfos->tooltipMessage.c_str());
3845 }
3846 }
3847 }
3848}
3849
3850void IGFD::FileDialog::m_BeginFileColorIconStyle(std::shared_ptr<FileInfos> vFileInfos, bool& vOutShowColor, std::string& vOutStr, ImFont** vOutFont) {
3851 vOutStr.clear();
3852 vOutShowColor = false;
3853
3854 if (vFileInfos->fileStyle.use_count()) //-V807 //-V522
3855 {
3856 vOutShowColor = true;
3857
3858 *vOutFont = vFileInfos->fileStyle->font;
3859 }
3860
3861 if (vOutShowColor && !vFileInfos->fileStyle->icon.empty())
3862 vOutStr = vFileInfos->fileStyle->icon;
3863 else if (vFileInfos->fileType.isDir())
3864 vOutStr = dirEntryString;
3865 else if (vFileInfos->fileType.isLinkToUnknown())
3866 vOutStr = linkEntryString;
3867 else if (vFileInfos->fileType.isFile())
3868 vOutStr = fileEntryString;
3869
3870 vOutStr += " " + vFileInfos->fileNameExt;
3871
3872 if (vOutShowColor) ImGui::PushStyleColor(ImGuiCol_Text, vFileInfos->fileStyle->color);
3873 if (*vOutFont) ImGui::PushFont(*vOutFont);
3874}
3875
3876void IGFD::FileDialog::m_EndFileColorIconStyle(const bool& vShowColor, ImFont* vFont) {
3877 if (vFont) ImGui::PopFont();
3878 if (vShowColor) ImGui::PopStyleColor();
3879}
3880
3881void IGFD::FileDialog::m_DrawFileListView(ImVec2 vSize) {
3882 auto& fdi = m_FileDialogInternal.fileManager;
3883
3884 ImGui::PushID(this);
3885
3886 static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_NoHostExtendY
3887#ifndef USE_CUSTOM_SORTING_ICON
3888 | ImGuiTableFlags_Sortable
3889#endif // USE_CUSTOM_SORTING_ICON
3890 ;
3891 auto listViewID = ImGui::GetID("##FileDialog_fileTable");
3892 if (ImGui::BeginTableEx("##FileDialog_fileTable", listViewID, 4, flags, vSize, 0.0f)) //-V112
3893 {
3894 ImGui::TableSetupScrollFreeze(0, 1); // Make header always visible
3895 ImGui::TableSetupColumn(fdi.header[FileManager::SortingFieldEnum::FIELD_FILENAME].c_str(), ImGuiTableColumnFlags_WidthStretch | (defaultSortOrderFilename ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending), -1, 0);
3896 ImGui::TableSetupColumn(fdi.header[FileManager::SortingFieldEnum::FIELD_TYPE].c_str(),
3897 ImGuiTableColumnFlags_WidthFixed | (defaultSortOrderType ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending) |
3898 ((m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_HideColumnType) ? ImGuiTableColumnFlags_DefaultHide : 0),
3899 -1, 1);
3900 ImGui::TableSetupColumn(fdi.header[FileManager::SortingFieldEnum::FIELD_SIZE].c_str(),
3901 ImGuiTableColumnFlags_WidthFixed | (defaultSortOrderSize ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending) |
3902 ((m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_HideColumnSize) ? ImGuiTableColumnFlags_DefaultHide : 0),
3903 -1, 2);
3904 ImGui::TableSetupColumn(fdi.header[FileManager::SortingFieldEnum::FIELD_DATE].c_str(),
3905 ImGuiTableColumnFlags_WidthFixed | (defaultSortOrderDate ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending) |
3906 ((m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_HideColumnDate) ? ImGuiTableColumnFlags_DefaultHide : 0),
3907 -1, 3);
3908
3909#ifndef USE_CUSTOM_SORTING_ICON
3910 // Sort our data if sort specs have been changed!
3911 if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs()) {
3912 if (sorts_specs->SpecsDirty && !fdi.IsFileListEmpty()) {
3913 bool direction = sorts_specs->Specs->SortDirection == ImGuiSortDirection_Ascending;
3914
3915 if (sorts_specs->Specs->ColumnUserID == 0) {
3916 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME;
3917 fdi.sortingDirection[0] = direction;
3918 fdi.SortFields(m_FileDialogInternal);
3919 } else if (sorts_specs->Specs->ColumnUserID == 1) {
3920 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_TYPE;
3921 fdi.sortingDirection[1] = direction;
3922 fdi.SortFields(m_FileDialogInternal);
3923 } else if (sorts_specs->Specs->ColumnUserID == 2) {
3924 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_SIZE;
3925 fdi.sortingDirection[2] = direction;
3926 fdi.SortFields(m_FileDialogInternal);
3927 } else // if (sorts_specs->Specs->ColumnUserID == 3) => alwayd true for the moment, to uncomment if we
3928 // add a fourth column
3929 {
3930 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_DATE;
3931 fdi.sortingDirection[3] = direction;
3932 fdi.SortFields(m_FileDialogInternal);
3933 }
3934
3935 sorts_specs->SpecsDirty = false;
3936 }
3937 }
3938
3939 ImGui::TableHeadersRow();
3940#else // USE_CUSTOM_SORTING_ICON
3941 ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
3942 for (int column = 0; column < 4; column++) //-V112
3943 {
3944 ImGui::TableSetColumnIndex(column);
3945 const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn()
3946 ImGui::PushID(column);
3947 ImGui::TableHeader(column_name);
3948 ImGui::PopID();
3949 if (ImGui::IsItemClicked()) {
3950 if (column == 0) {
3951 if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME)
3952 fdi.sortingDirection[0] = !fdi.sortingDirection[0];
3953 else
3954 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME;
3955
3956 fdi.SortFields(m_FileDialogInternal);
3957 } else if (column == 1) {
3958 if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_TYPE)
3959 fdi.sortingDirection[1] = !fdi.sortingDirection[1];
3960 else
3961 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_TYPE;
3962
3963 fdi.SortFields(m_FileDialogInternal);
3964 } else if (column == 2) {
3965 if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_SIZE)
3966 fdi.sortingDirection[2] = !fdi.sortingDirection[2];
3967 else
3968 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_SIZE;
3969
3970 fdi.SortFields(m_FileDialogInternal);
3971 } else // if (column == 3) => alwayd true for the moment, to uncomment if we add a fourth column
3972 {
3973 if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_DATE)
3974 fdi.sortingDirection[3] = !fdi.sortingDirection[3];
3975 else
3976 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_DATE;
3977
3978 fdi.SortFields(m_FileDialogInternal);
3979 }
3980 }
3981 }
3982#endif // USE_CUSTOM_SORTING_ICON
3983 if (!fdi.IsFilteredListEmpty()) {
3984 std::string _str;
3985 ImFont* _font = nullptr;
3986 bool _showColor = false;
3987
3988 int column_id = 0;
3989 m_FileListClipper.Begin((int)fdi.GetFilteredListSize(), ImGui::GetTextLineHeightWithSpacing());
3990 while (m_FileListClipper.Step()) {
3991 for (int i = m_FileListClipper.DisplayStart; i < m_FileListClipper.DisplayEnd; i++) {
3992 if (i < 0) continue;
3993
3994 auto infos = fdi.GetFilteredFileAt((size_t)i);
3995 if (!infos.use_count()) continue;
3996
3997 m_BeginFileColorIconStyle(infos, _showColor, _str, &_font);
3998
3999 bool selected = fdi.IsFileNameSelected(infos->fileNameExt); // found
4000
4001 ImGui::TableNextRow();
4002
4003 column_id = 0;
4004 if (ImGui::TableNextColumn()) // file name
4005 {
4006 m_SelectableItem(i, infos, selected, _str.c_str());
4007 m_DisplayFileInfosTooltip(i, column_id++, infos);
4008 }
4009 if (ImGui::TableNextColumn()) // file type
4010 {
4011 ImGui::Text("%s", infos->fileExtLevels[0].c_str());
4012 m_DisplayFileInfosTooltip(i, column_id++, infos);
4013 }
4014 if (ImGui::TableNextColumn()) // file size
4015 {
4016 if (!infos->fileType.isDir()) {
4017 RightAlignText(infos->formatedFileSize.first.c_str(), "1888.88");
4018 ImGui::SameLine(0.0f, 0.0f);
4019 ImGui::Text(" %s ", infos->formatedFileSize.second.c_str());
4020 } else {
4022 }
4023 m_DisplayFileInfosTooltip(i, column_id++, infos);
4024 }
4025 if (ImGui::TableNextColumn()) // file date + time
4026 {
4027 ImGui::Text("%s", infos->fileModifDate.c_str());
4028 m_DisplayFileInfosTooltip(i, column_id++, infos);
4029 }
4030
4031 m_EndFileColorIconStyle(_showColor, _font);
4032 }
4033 }
4034 m_FileListClipper.End();
4035 }
4036
4037#ifdef USE_EXPLORATION_BY_KEYS
4038 if (!fdi.inputPathActivated) {
4039 m_LocateByInputKey(m_FileDialogInternal);
4040 m_ExploreWithkeys(m_FileDialogInternal, listViewID);
4041 }
4042#endif // USE_EXPLORATION_BY_KEYS
4043
4044 ImGuiContext& g = *GImGui;
4045 if (g.LastActiveId - 1 == listViewID || g.LastActiveId == listViewID) {
4046 m_FileDialogInternal.fileListViewIsActive = true;
4047 }
4048
4049 ImGui::EndTable();
4050 }
4051
4052 ImGui::PopID();
4053}
4054
4055#ifdef USE_THUMBNAILS
4056void IGFD::FileDialog::m_DrawThumbnailsListView(ImVec2 vSize) {
4057 auto& fdi = m_FileDialogInternal.fileManager;
4058
4059 ImGui::PushID(this);
4060
4061 static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_NoHostExtendY
4062#ifndef USE_CUSTOM_SORTING_ICON
4063 | ImGuiTableFlags_Sortable
4064#endif // USE_CUSTOM_SORTING_ICON
4065 ;
4066 auto listViewID = ImGui::GetID("##FileDialog_fileTable");
4067 if (ImGui::BeginTableEx("##FileDialog_fileTable", listViewID, 5, flags, vSize, 0.0f)) {
4068 ImGui::TableSetupScrollFreeze(0, 1); // Make header always visible
4069 ImGui::TableSetupColumn(fdi.headerFileName.c_str(), ImGuiTableColumnFlags_WidthStretch | (defaultSortOrderFilename ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending), -1, 0);
4070 ImGui::TableSetupColumn(fdi.headerFileType.c_str(),
4071 ImGuiTableColumnFlags_WidthFixed | (defaultSortOrderType ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending) |
4072 ((m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_HideColumnType) ? ImGuiTableColumnFlags_DefaultHide : 0),
4073 -1, 1);
4074 ImGui::TableSetupColumn(fdi.headerFileSize.c_str(),
4075 ImGuiTableColumnFlags_WidthFixed | (defaultSortOrderSize ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending) |
4076 ((m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_HideColumnSize) ? ImGuiTableColumnFlags_DefaultHide : 0),
4077 -1, 2);
4078 ImGui::TableSetupColumn(fdi.headerFileDate.c_str(),
4079 ImGuiTableColumnFlags_WidthFixed | (defaultSortOrderDate ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending) |
4080 ((m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_HideColumnDate) ? ImGuiTableColumnFlags_DefaultHide : 0),
4081 -1, 3);
4082 // not needed to have an option for hide the thumbnails since this is why this view is used
4083 ImGui::TableSetupColumn(fdi.header[FileManager::SortingFieldEnum::FIELD_THUMBNAILS].c_str(), ImGuiTableColumnFlags_WidthFixed | (defaultSortOrderThumbnails ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending), -1, 4); //-V112
4084
4085#ifndef USE_CUSTOM_SORTING_ICON
4086 // Sort our data if sort specs have been changed!
4087 if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs()) {
4088 if (sorts_specs->SpecsDirty && !fdi.IsFileListEmpty()) {
4089 bool direction = sorts_specs->Specs->SortDirection == ImGuiSortDirection_Ascending;
4090
4091 if (sorts_specs->Specs->ColumnUserID == 0) {
4092 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME;
4093 fdi.sortingDirection[0] = direction;
4094 fdi.SortFields(m_FileDialogInternal);
4095 } else if (sorts_specs->Specs->ColumnUserID == 1) {
4096 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_TYPE;
4097 fdi.sortingDirection[1] = direction;
4098 fdi.SortFields(m_FileDialogInternal);
4099 } else if (sorts_specs->Specs->ColumnUserID == 2) {
4100 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_SIZE;
4101 fdi.sortingDirection[2] = direction;
4102 fdi.SortFields(m_FileDialogInternal);
4103 } else if (sorts_specs->Specs->ColumnUserID == 3) {
4104 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_DATE;
4105 fdi.sortingDirection[3] = direction;
4106 fdi.SortFields(m_FileDialogInternal);
4107 } else // if (sorts_specs->Specs->ColumnUserID == 4) = > always true for the moment, to uncomment if we
4108 // add another column
4109 {
4110 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_THUMBNAILS;
4111 fdi.sortingDirection[4] = direction;
4112 fdi.SortFields(m_FileDialogInternal);
4113 }
4114
4115 sorts_specs->SpecsDirty = false;
4116 }
4117 }
4118
4119 ImGui::TableHeadersRow();
4120#else // USE_CUSTOM_SORTING_ICON
4121 ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
4122 for (int column = 0; column < 5; column++) {
4123 ImGui::TableSetColumnIndex(column);
4124 const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn()
4125 ImGui::PushID(column);
4126 ImGui::TableHeader(column_name);
4127 ImGui::PopID();
4128 if (ImGui::IsItemClicked()) {
4129 if (column == 0) {
4130 if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME)
4131 fdi.sortingDirection[0] = !fdi.sortingDirection[0];
4132 else
4133 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME;
4134
4135 fdi.SortFields(m_FileDialogInternal);
4136 } else if (column == 1) {
4137 if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_TYPE)
4138 fdi.sortingDirection[1] = !fdi.sortingDirection[1];
4139 else
4140 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_TYPE;
4141
4142 fdi.SortFields(m_FileDialogInternal);
4143 } else if (column == 2) {
4144 if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_SIZE)
4145 fdi.sortingDirection[2] = !fdi.sortingDirection[2];
4146 else
4147 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_SIZE;
4148
4149 fdi.SortFields(m_FileDialogInternal);
4150 } else if (column == 3) {
4151 if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_DATE)
4152 fdi.sortingDirection[3] = !fdi.sortingDirection[3];
4153 else
4154 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_DATE;
4155
4156 fdi.SortFields(m_FileDialogInternal);
4157 } else // if (sorts_specs->Specs->ColumnUserID == 4) = > always true for the moment, to uncomment if we
4158 // add another column
4159 {
4160 if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_THUMBNAILS)
4161 fdi.sortingDirection[4] = !fdi.sortingDirection[4];
4162 else
4163 fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_THUMBNAILS;
4164
4165 fdi.SortFields(m_FileDialogInternal);
4166 }
4167 }
4168 }
4169#endif // USE_CUSTOM_SORTING_ICON
4170 if (!fdi.IsFilteredListEmpty()) {
4171 std::string _str;
4172 ImFont* _font = nullptr;
4173 bool _showColor = false;
4174
4175 ImGuiContext& g = *GImGui;
4176 const float itemHeight = ImMax(g.FontSize, DisplayMode_ThumbailsList_ImageHeight) + g.Style.ItemSpacing.y;
4177
4178 int column_id = 0;
4179 m_FileListClipper.Begin((int)fdi.GetFilteredListSize(), itemHeight);
4180 while (m_FileListClipper.Step()) {
4181 for (int i = m_FileListClipper.DisplayStart; i < m_FileListClipper.DisplayEnd; i++) {
4182 if (i < 0) continue;
4183
4184 auto infos = fdi.GetFilteredFileAt((size_t)i);
4185 if (!infos.use_count()) continue;
4186
4187 m_BeginFileColorIconStyle(infos, _showColor, _str, &_font);
4188
4189 bool selected = fdi.IsFileNameSelected(infos->fileNameExt); // found
4190
4191 ImGui::TableNextRow();
4192
4193 column_id = 0;
4194 if (ImGui::TableNextColumn()) // file name
4195 {
4196 m_SelectableItem(i, infos, selected, _str.c_str());
4197 m_DisplayFileInfosTooltip(i, column_id++, infos);
4198 }
4199 if (ImGui::TableNextColumn()) // file type
4200 {
4201 ImGui::Text("%s", infos->fileExtLevels[0].c_str());
4202 m_DisplayFileInfosTooltip(i, column_id++, infos);
4203 }
4204 if (ImGui::TableNextColumn()) // file size
4205 {
4206 if (!infos->fileType.isDir()) {
4207 RightAlignText(infos->formatedFileSize.first.c_str(), "1888.88");
4208 ImGui::SameLine(0.0f, 0.0f);
4209 ImGui::Text(" %s ", infos->formatedFileSize.second.c_str());
4210 } else {
4212 }
4213 m_DisplayFileInfosTooltip(i, column_id++, infos);
4214 }
4215 if (ImGui::TableNextColumn()) // file date + time
4216 {
4217 ImGui::Text("%s", infos->fileModifDate.c_str());
4218 m_DisplayFileInfosTooltip(i, column_id++, infos);
4219 }
4220 if (ImGui::TableNextColumn()) // file thumbnails
4221 {
4222 auto th = &infos->thumbnailInfo;
4223
4224 if (!th->isLoadingOrLoaded) {
4225 m_AddThumbnailToLoad(infos);
4226 }
4227 if (th->isReadyToDisplay && th->textureID) {
4228 ImGui::Image((ImTextureID)th->textureID, ImVec2((float)th->textureWidth, (float)th->textureHeight));
4229 }
4230 m_DisplayFileInfosTooltip(i, column_id++, infos);
4231 }
4232
4233 m_EndFileColorIconStyle(_showColor, _font);
4234 }
4235 }
4236 m_FileListClipper.End();
4237 }
4238
4239#ifdef USE_EXPLORATION_BY_KEYS
4240 if (!fdi.inputPathActivated) {
4241 m_LocateByInputKey(m_FileDialogInternal);
4242 m_ExploreWithkeys(m_FileDialogInternal, listViewID);
4243 }
4244#endif // USE_EXPLORATION_BY_KEYS
4245
4246 ImGuiContext& g = *GImGui;
4247 if (g.LastActiveId - 1 == listViewID || g.LastActiveId == listViewID) {
4248 m_FileDialogInternal.fileListViewIsActive = true;
4249 }
4250
4251 ImGui::EndTable();
4252 }
4253
4254 ImGui::PopID();
4255}
4256
4257void IGFD::FileDialog::m_DrawThumbnailsGridView(ImVec2 vSize) {
4258 if (ImGui::BeginChild("##thumbnailsGridsFiles", vSize)) {
4259 // todo
4260 }
4261
4262 ImGui::EndChild();
4263}
4264
4265#endif
4266
4267void IGFD::FileDialog::m_DrawSidePane(float vHeight) {
4268 ImGui::SameLine();
4269
4270 ImGui::BeginChild("##FileTypes", ImVec2(0, vHeight));
4271
4272 m_FileDialogInternal.getDialogConfig().sidePane(
4273 m_FileDialogInternal.filterManager.GetSelectedFilter().getFirstFilter().c_str(),
4274 m_FileDialogInternal.getDialogConfigRef().userDatas,
4275 &m_FileDialogInternal.canWeContinue);
4276 ImGui::EndChild();
4277}
4278
4279void IGFD::FileDialog::Close() {
4280 m_FileDialogInternal.dLGkey.clear();
4281 m_FileDialogInternal.showDialog = false;
4282}
4283
4284bool IGFD::FileDialog::WasOpenedThisFrame(const std::string& vKey) const {
4285 bool res = m_FileDialogInternal.showDialog && m_FileDialogInternal.dLGkey == vKey;
4286 if (res) {
4287 res &= m_FileDialogInternal.lastImGuiFrameCount == GImGui->FrameCount; // return true if a dialog was displayed in this frame
4288 }
4289 return res;
4290}
4291
4292bool IGFD::FileDialog::WasOpenedThisFrame() const {
4293 bool res = m_FileDialogInternal.showDialog;
4294 if (res) {
4295 res &= m_FileDialogInternal.lastImGuiFrameCount == GImGui->FrameCount; // return true if a dialog was displayed in this frame
4296 }
4297 return res;
4298}
4299
4300bool IGFD::FileDialog::IsOpened(const std::string& vKey) const {
4301 return (m_FileDialogInternal.showDialog && m_FileDialogInternal.dLGkey == vKey);
4302}
4303
4304bool IGFD::FileDialog::IsOpened() const {
4305 return m_FileDialogInternal.showDialog;
4306}
4307
4308std::string IGFD::FileDialog::GetOpenedKey() const {
4309 if (m_FileDialogInternal.showDialog) {
4310 return m_FileDialogInternal.dLGkey;
4311 }
4312 return "";
4313}
4314
4315std::string IGFD::FileDialog::GetFilePathName(IGFD_ResultMode vFlag) {
4316 return m_FileDialogInternal.fileManager.GetResultingFilePathName(m_FileDialogInternal, vFlag);
4317}
4318
4319std::string IGFD::FileDialog::GetCurrentPath() {
4320 return m_FileDialogInternal.fileManager.GetResultingPath();
4321}
4322
4323std::string IGFD::FileDialog::GetCurrentFileName(IGFD_ResultMode vFlag) {
4324 return m_FileDialogInternal.fileManager.GetResultingFileName(m_FileDialogInternal, vFlag);
4325}
4326
4327std::string IGFD::FileDialog::GetCurrentFilter() {
4328 return m_FileDialogInternal.filterManager.GetSelectedFilter().title;
4329}
4330
4331std::map<std::string, std::string> IGFD::FileDialog::GetSelection(IGFD_ResultMode vFlag) {
4332 return m_FileDialogInternal.fileManager.GetResultingSelection(m_FileDialogInternal, vFlag);
4333}
4334
4335IGFD::UserDatas IGFD::FileDialog::GetUserDatas() const {
4336 return m_FileDialogInternal.getDialogConfig().userDatas;
4337}
4338
4339bool IGFD::FileDialog::IsOk() const {
4340 return m_FileDialogInternal.isOk;
4341}
4342
4343void IGFD::FileDialog::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const FileStyle& vInfos) {
4344 m_FileDialogInternal.filterManager.SetFileStyle(vFlags, vCriteria, vInfos);
4345}
4346
4347void IGFD::FileDialog::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const ImVec4& vColor, const std::string& vIcon, ImFont* vFont) {
4348 m_FileDialogInternal.filterManager.SetFileStyle(vFlags, vCriteria, vColor, vIcon, vFont);
4349}
4350
4351void IGFD::FileDialog::SetFileStyle(FileStyle::FileStyleFunctor vFunctor) {
4352 m_FileDialogInternal.filterManager.SetFileStyle(vFunctor);
4353}
4354
4355bool IGFD::FileDialog::GetFileStyle(const IGFD_FileStyleFlags& vFlags, const std::string& vCriteria, ImVec4* vOutColor, std::string* vOutIcon, ImFont** vOutFont) {
4356 return m_FileDialogInternal.filterManager.GetFileStyle(vFlags, vCriteria, vOutColor, vOutIcon, vOutFont);
4357}
4358
4359void IGFD::FileDialog::ClearFilesStyle() {
4360 m_FileDialogInternal.filterManager.ClearFilesStyle();
4361}
4362
4363void IGFD::FileDialog::SetLocales(const int& /*vLocaleCategory*/, const std::string& vLocaleBegin, const std::string& vLocaleEnd) {
4364 m_FileDialogInternal.puUseCustomLocale = true;
4365 m_FileDialogInternal.localeBegin = vLocaleBegin;
4366 m_FileDialogInternal.localeEnd = vLocaleEnd;
4367}
4368
4372
4373bool IGFD::FileDialog::m_Confirm_Or_OpenOverWriteFileDialog_IfNeeded(bool vLastAction, ImGuiWindowFlags vFlags) {
4374 // if confirmation => return true for confirm the overwrite et quit the dialog
4375 // if cancel => return false && set IsOk to false for keep inside the dialog
4376
4377 // if IsOk == false => return false for quit the dialog
4378 if (!m_FileDialogInternal.isOk && vLastAction) {
4379 m_QuitFrame();
4380 return true;
4381 }
4382
4383 // if IsOk == true && no check of overwrite => return true for confirm the dialog
4384 if (m_FileDialogInternal.isOk && vLastAction && !(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_ConfirmOverwrite)) {
4385 m_QuitFrame();
4386 return true;
4387 }
4388
4389 // if IsOk == true && check of overwrite => return false and show confirm to overwrite dialog
4390 if ((m_FileDialogInternal.okResultToConfirm || (m_FileDialogInternal.isOk && vLastAction)) && (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_ConfirmOverwrite)) {
4391 if (m_FileDialogInternal.isOk) // catched only one time
4392 {
4393 if (!m_FileDialogInternal.fileManager.GetFileSystemInstance()->IsFileExist(GetFilePathName())) // not existing => quit dialog
4394 {
4395 m_QuitFrame();
4396 return true;
4397 } else // existing => confirm dialog to open
4398 {
4399 m_FileDialogInternal.isOk = false;
4400 m_FileDialogInternal.okResultToConfirm = true;
4401 }
4402 }
4403
4404 std::string name = OverWriteDialogTitleString "##" + m_FileDialogInternal.dLGtitle + m_FileDialogInternal.dLGkey + "OverWriteDialog";
4405
4406 bool res = false;
4407
4408 ImGui::OpenPopup(name.c_str());
4409 if (ImGui::BeginPopupModal(name.c_str(), (bool*)0, vFlags | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) {
4410 ImGui::SetWindowPos(m_FileDialogInternal.dialogCenterPos - ImGui::GetWindowSize() * 0.5f); // next frame needed for GetWindowSize to work
4411
4412 ImGui::Text("%s", OverWriteDialogMessageString);
4413
4414 if (IMGUI_BUTTON(OverWriteDialogConfirmButtonString)) {
4415 m_FileDialogInternal.okResultToConfirm = false;
4416 m_FileDialogInternal.isOk = true;
4417 res = true;
4418 ImGui::CloseCurrentPopup();
4419 }
4420
4421 ImGui::SameLine();
4422
4423 if (IMGUI_BUTTON(OverWriteDialogCancelButtonString)) {
4424 m_FileDialogInternal.okResultToConfirm = false;
4425 m_FileDialogInternal.isOk = false;
4426 res = false;
4427 ImGui::CloseCurrentPopup();
4428 }
4429
4430 ImGui::EndPopup();
4431 }
4432
4433 if (res) {
4434 m_QuitFrame();
4435 }
4436 return res;
4437 }
4438
4439 return false;
4440}
4441
4442//#pragma endregion
4443
4444//#pragma endregion
4445
4446#endif // __cplusplus
4447
4448//#pragma region IGFD_C_API
4449
4450// return an initialized IGFD_FileDialog_Config
4451IGFD_C_API IGFD_FileDialog_Config IGFD_FileDialog_Config_Get() {
4452 IGFD_FileDialog_Config res = {};
4453 res.path = "";
4454 res.fileName = "";
4455 res.filePathName = "";
4456 res.countSelectionMax = 1;
4457 res.userDatas = nullptr;
4458 res.sidePane = nullptr;
4459 res.sidePaneWidth = 250.0f;
4460 res.flags = ImGuiFileDialogFlags_Default;
4461 return res;
4462}
4463
4464// Return an initialized IGFD_Selection_Pair
4465IGFD_C_API IGFD_Selection_Pair IGFD_Selection_Pair_Get(void) {
4466 IGFD_Selection_Pair res = {};
4467 res.fileName = nullptr;
4468 res.filePathName = nullptr;
4469 return res;
4470}
4471
4472// destroy only the content of vSelection_Pair
4473IGFD_C_API void IGFD_Selection_Pair_DestroyContent(IGFD_Selection_Pair* vSelection_Pair) {
4474 if (vSelection_Pair) {
4475 delete[] vSelection_Pair->fileName;
4476 delete[] vSelection_Pair->filePathName;
4477 }
4478}
4479
4480// Return an initialized IGFD_Selection
4481IGFD_C_API IGFD_Selection IGFD_Selection_Get(void) {
4482 return {nullptr, 0U};
4483}
4484
4485// destroy only the content of vSelection
4486IGFD_C_API void IGFD_Selection_DestroyContent(IGFD_Selection* vSelection) {
4487 if (vSelection) {
4488 if (vSelection->table) {
4489 for (size_t i = 0U; i < vSelection->count; i++) {
4490 IGFD_Selection_Pair_DestroyContent(&vSelection->table[i]);
4491 }
4492 delete[] vSelection->table;
4493 }
4494 vSelection->count = 0U;
4495 }
4496}
4497
4498// create an instance of ImGuiFileDialog
4499IGFD_C_API ImGuiFileDialog* IGFD_Create(void) {
4500 return new ImGuiFileDialog();
4501}
4502
4503// destroy the instance of ImGuiFileDialog
4504IGFD_C_API void IGFD_Destroy(ImGuiFileDialog* vContextPtr) {
4505 if (vContextPtr != nullptr) {
4506 delete vContextPtr;
4507 vContextPtr = nullptr;
4508 }
4509}
4510
4511IGFD_C_API void IGFD_OpenDialog( // open a standard dialog
4512 ImGuiFileDialog* vContextPtr, // ImGuiFileDialog context
4513 const char* vKey, // key dialog
4514 const char* vTitle, // title
4515 const char* vFilters, // filters/filter collections. set it to null for directory mode
4516 const IGFD_FileDialog_Config vConfig) { // path
4517 if (vContextPtr != nullptr) {
4518 IGFD::FileDialogConfig config;
4519 config.path = vConfig.path;
4520 config.fileName = vConfig.fileName;
4521 config.filePathName = vConfig.filePathName;
4522 config.countSelectionMax = vConfig.countSelectionMax;
4523 config.userDatas = vConfig.userDatas;
4524 config.flags = vConfig.flags;
4525 config.sidePane = vConfig.sidePane;
4526 config.sidePaneWidth = vConfig.sidePaneWidth;
4527 vContextPtr->OpenDialog(vKey, vTitle, vFilters, config);
4528 }
4529}
4530
4531IGFD_C_API bool IGFD_DisplayDialog(ImGuiFileDialog* vContextPtr, const char* vKey, ImGuiWindowFlags vFlags, ImVec2 vMinSize, ImVec2 vMaxSize) {
4532 if (vContextPtr != nullptr) {
4533 return vContextPtr->Display(vKey, vFlags, vMinSize, vMaxSize);
4534 }
4535 return false;
4536}
4537
4538IGFD_C_API void IGFD_CloseDialog(ImGuiFileDialog* vContextPtr) {
4539 if (vContextPtr != nullptr) {
4540 vContextPtr->Close();
4541 }
4542}
4543
4544IGFD_C_API bool IGFD_IsOk(ImGuiFileDialog* vContextPtr) {
4545 if (vContextPtr != nullptr) {
4546 return vContextPtr->IsOk();
4547 }
4548 return false;
4549}
4550
4551IGFD_C_API bool IGFD_WasKeyOpenedThisFrame(ImGuiFileDialog* vContextPtr, const char* vKey) {
4552 if (vContextPtr != nullptr) {
4553 return vContextPtr->WasOpenedThisFrame(vKey);
4554 }
4555 return false;
4556}
4557
4558IGFD_C_API bool IGFD_WasOpenedThisFrame(ImGuiFileDialog* vContextPtr) {
4559 if (vContextPtr != nullptr) {
4560 return vContextPtr->WasOpenedThisFrame();
4561 }
4562
4563 return false;
4564}
4565
4566IGFD_C_API bool IGFD_IsKeyOpened(ImGuiFileDialog* vContextPtr, const char* vCurrentOpenedKey) {
4567 if (vContextPtr != nullptr) {
4568 return vContextPtr->IsOpened(vCurrentOpenedKey);
4569 }
4570
4571 return false;
4572}
4573
4574IGFD_C_API bool IGFD_IsOpened(ImGuiFileDialog* vContextPtr) {
4575 if (vContextPtr != nullptr) {
4576 return vContextPtr->IsOpened();
4577 }
4578
4579 return false;
4580}
4581
4582IGFD_C_API IGFD_Selection IGFD_GetSelection(ImGuiFileDialog* vContextPtr, IGFD_ResultMode vMode) {
4583 IGFD_Selection res = IGFD_Selection_Get();
4584 if (vContextPtr != nullptr) {
4585 auto sel = vContextPtr->GetSelection(vMode);
4586 if (!sel.empty()) {
4587 res.count = sel.size();
4588 res.table = new IGFD_Selection_Pair[res.count];
4589
4590 size_t idx = 0U;
4591 for (const auto& s : sel) {
4592 IGFD_Selection_Pair* pair = res.table + idx++;
4593
4594 // fileNameExt
4595 if (!s.first.empty()) {
4596 size_t siz = s.first.size() + 1U;
4597 pair->fileName = new char[siz];
4598#ifndef _MSC_VER
4599 strncpy(pair->fileName, s.first.c_str(), siz);
4600#else // _MSC_VER
4601 strncpy_s(pair->fileName, siz, s.first.c_str(), siz);
4602#endif // _MSC_VER
4603 pair->fileName[siz - 1U] = '\0';
4604 }
4605
4606 // filePathName
4607 if (!s.second.empty()) {
4608 size_t siz = s.second.size() + 1U;
4609 pair->filePathName = new char[siz];
4610#ifndef _MSC_VER
4611 strncpy(pair->filePathName, s.second.c_str(), siz);
4612#else // _MSC_VER
4613 strncpy_s(pair->filePathName, siz, s.second.c_str(), siz);
4614#endif // _MSC_VER
4615 pair->filePathName[siz - 1U] = '\0';
4616 }
4617 }
4618
4619 return res;
4620 }
4621 }
4622
4623 return res;
4624}
4625
4626IGFD_C_API char* IGFD_GetFilePathName(ImGuiFileDialog* vContextPtr, IGFD_ResultMode vMode) {
4627 char* res = nullptr;
4628
4629 if (vContextPtr != nullptr) {
4630 auto s = vContextPtr->GetFilePathName(vMode);
4631 if (!s.empty()) {
4632 size_t siz = s.size() + 1U;
4633 res = (char*)malloc(siz);
4634 if (res) {
4635#ifndef _MSC_VER
4636 strncpy(res, s.c_str(), siz);
4637#else // _MSC_VER
4638 strncpy_s(res, siz, s.c_str(), siz);
4639#endif // _MSC_VER
4640 res[siz - 1U] = '\0';
4641 }
4642 }
4643 }
4644
4645 return res;
4646}
4647
4648IGFD_C_API char* IGFD_GetCurrentFileName(ImGuiFileDialog* vContextPtr, IGFD_ResultMode vMode) {
4649 char* res = nullptr;
4650
4651 if (vContextPtr != nullptr) {
4652 auto s = vContextPtr->GetCurrentFileName(vMode);
4653 if (!s.empty()) {
4654 size_t siz = s.size() + 1U;
4655 res = (char*)malloc(siz);
4656 if (res) {
4657#ifndef _MSC_VER
4658 strncpy(res, s.c_str(), siz);
4659#else // _MSC_VER
4660 strncpy_s(res, siz, s.c_str(), siz);
4661#endif // _MSC_VER
4662 res[siz - 1U] = '\0';
4663 }
4664 }
4665 }
4666
4667 return res;
4668}
4669
4670IGFD_C_API char* IGFD_GetCurrentPath(ImGuiFileDialog* vContextPtr) {
4671 char* res = nullptr;
4672
4673 if (vContextPtr != nullptr) {
4674 auto s = vContextPtr->GetCurrentPath();
4675 if (!s.empty()) {
4676 size_t siz = s.size() + 1U;
4677 res = (char*)malloc(siz);
4678 if (res) {
4679#ifndef _MSC_VER
4680 strncpy(res, s.c_str(), siz);
4681#else // _MSC_VER
4682 strncpy_s(res, siz, s.c_str(), siz);
4683#endif // _MSC_VER
4684 res[siz - 1U] = '\0';
4685 }
4686 }
4687 }
4688
4689 return res;
4690}
4691
4692IGFD_C_API char* IGFD_GetCurrentFilter(ImGuiFileDialog* vContextPtr) {
4693 char* res = nullptr;
4694
4695 if (vContextPtr != nullptr) {
4696 auto s = vContextPtr->GetCurrentFilter();
4697 if (!s.empty()) {
4698 size_t siz = s.size() + 1U;
4699 res = (char*)malloc(siz);
4700 if (res) {
4701#ifndef _MSC_VER
4702 strncpy(res, s.c_str(), siz);
4703#else // _MSC_VER
4704 strncpy_s(res, siz, s.c_str(), siz);
4705#endif // _MSC_VER
4706 res[siz - 1U] = '\0';
4707 }
4708 }
4709 }
4710
4711 return res;
4712}
4713
4714IGFD_C_API void* IGFD_GetUserDatas(ImGuiFileDialog* vContextPtr) {
4715 if (vContextPtr != nullptr) {
4716 return vContextPtr->GetUserDatas();
4717 }
4718
4719 return nullptr;
4720}
4721
4722IGFD_C_API void IGFD_SetFileStyle(ImGuiFileDialog* vContextPtr, IGFD_FileStyleFlags vFlags, const char* vCriteria, ImVec4 vColor, const char* vIcon,
4723 ImFont* vFont) //-V813
4724{
4725 if (vContextPtr != nullptr) {
4726 vContextPtr->SetFileStyle(vFlags, vCriteria, vColor, vIcon, vFont);
4727 }
4728}
4729
4730IGFD_C_API void IGFD_SetFileStyle2(ImGuiFileDialog* vContextPtr, IGFD_FileStyleFlags vFlags, const char* vCriteria, float vR, float vG, float vB, float vA, const char* vIcon, ImFont* vFont) {
4731 if (vContextPtr != nullptr) {
4732 vContextPtr->SetFileStyle(vFlags, vCriteria, ImVec4(vR, vG, vB, vA), vIcon, vFont);
4733 }
4734}
4735
4736IGFD_C_API bool IGFD_GetFileStyle(ImGuiFileDialog* vContextPtr, IGFD_FileStyleFlags vFlags, const char* vCriteria, ImVec4* vOutColor, char** vOutIconText, ImFont** vOutFont) {
4737 if (vContextPtr != nullptr) {
4738 std::string icon;
4739 bool res = vContextPtr->GetFileStyle(vFlags, vCriteria, vOutColor, &icon, vOutFont);
4740 if (!icon.empty() && vOutIconText) {
4741 size_t siz = icon.size() + 1U;
4742 *vOutIconText = (char*)malloc(siz);
4743 if (*vOutIconText) {
4744#ifndef _MSC_VER
4745 strncpy(*vOutIconText, icon.c_str(), siz);
4746#else // _MSC_VER
4747 strncpy_s(*vOutIconText, siz, icon.c_str(), siz);
4748#endif // _MSC_VER
4749 (*vOutIconText)[siz - 1U] = '\0';
4750 }
4751 }
4752 return res;
4753 }
4754
4755 return false;
4756}
4757
4758IGFD_C_API void IGFD_ClearFilesStyle(ImGuiFileDialog* vContextPtr) {
4759 if (vContextPtr != nullptr) {
4760 vContextPtr->ClearFilesStyle();
4761 }
4762}
4763
4764IGFD_C_API void SetLocales(ImGuiFileDialog* vContextPtr, const int vCategory, const char* vBeginLocale, const char* vEndLocale) {
4765 if (vContextPtr != nullptr) {
4766 vContextPtr->SetLocales(vCategory, (vBeginLocale ? vBeginLocale : ""), (vEndLocale ? vEndLocale : ""));
4767 }
4768}
4769
4770#ifdef USE_EXPLORATION_BY_KEYS
4771IGFD_C_API void IGFD_SetFlashingAttenuationInSeconds(ImGuiFileDialog* vContextPtr, float vAttenValue) {
4772 if (vContextPtr != nullptr) {
4773 vContextPtr->SetFlashingAttenuationInSeconds(vAttenValue);
4774 }
4775}
4776#endif
4777
4778#ifdef USE_BOOKMARK
4779IGFD_C_API char* IGFD_SerializeBookmarks(ImGuiFileDialog* vContextPtr, bool vDontSerializeCodeBasedBookmarks) {
4780 char* res = nullptr;
4781
4782 if (vContextPtr != nullptr) {
4783 auto s = vContextPtr->SerializeBookmarks(vDontSerializeCodeBasedBookmarks);
4784 if (!s.empty()) {
4785 size_t siz = s.size() + 1U;
4786 res = (char*)malloc(siz);
4787 if (res) {
4788#ifndef _MSC_VER
4789 strncpy(res, s.c_str(), siz);
4790#else // _MSC_VER
4791 strncpy_s(res, siz, s.c_str(), siz);
4792#endif // _MSC_VER
4793 res[siz - 1U] = '\0';
4794 }
4795 }
4796 }
4797
4798 return res;
4799}
4800
4801IGFD_C_API void IGFD_DeserializeBookmarks(ImGuiFileDialog* vContextPtr, const char* vBookmarks) {
4802 if (vContextPtr != nullptr) {
4803 vContextPtr->DeserializeBookmarks(vBookmarks);
4804 }
4805}
4806
4807IGFD_C_API void IGFD_AddBookmark(ImGuiFileDialog* vContextPtr, const char* vBookMarkName, const char* vBookMarkPath) {
4808 if (vContextPtr != nullptr) {
4809 vContextPtr->AddBookmark(vBookMarkName, vBookMarkPath);
4810 }
4811}
4812
4813IGFD_C_API void IGFD_RemoveBookmark(ImGuiFileDialog* vContextPtr, const char* vBookMarkName) {
4814 if (vContextPtr != nullptr) {
4815 vContextPtr->RemoveBookmark(vBookMarkName);
4816 }
4817}
4818
4819#endif
4820
4821#ifdef USE_THUMBNAILS
4822IGFD_C_API void SetCreateThumbnailCallback(ImGuiFileDialog* vContextPtr, const IGFD_CreateThumbnailFun vCreateThumbnailFun) {
4823 if (vContextPtr != nullptr) {
4824 vContextPtr->SetCreateThumbnailCallback(vCreateThumbnailFun);
4825 }
4826}
4827
4828IGFD_C_API void SetDestroyThumbnailCallback(ImGuiFileDialog* vContextPtr, const IGFD_DestroyThumbnailFun vDestroyThumbnailFun) {
4829 if (vContextPtr != nullptr) {
4830 vContextPtr->SetDestroyThumbnailCallback(vDestroyThumbnailFun);
4831 }
4832}
4833
4834IGFD_C_API void ManageGPUThumbnails(ImGuiFileDialog* vContextPtr) {
4835 if (vContextPtr != nullptr) {
4836 vContextPtr->ManageGPUThumbnails();
4837 }
4838}
4839#endif // USE_THUMBNAILS
4840
4841//#pragma endregion
IGFD_C_API IGFD_Selection_Pair IGFD_Selection_Pair_Get(void)
IGFD_C_API void IGFD_Selection_Pair_DestroyContent(IGFD_Selection_Pair *vSelection_Pair)
IGFD_C_API void IGFD_RemoveBookmark(ImGuiFileDialog *vContextPtr, const char *vBookMarkName)
IGFD_C_API IGFD_Selection IGFD_GetSelection(ImGuiFileDialog *vContextPtr, IGFD_ResultMode vMode)
IGFD_C_API void * IGFD_GetUserDatas(ImGuiFileDialog *vContextPtr)
IGFD_C_API char * IGFD_GetFilePathName(ImGuiFileDialog *vContextPtr, IGFD_ResultMode vMode)
IGFD_C_API char * IGFD_SerializeBookmarks(ImGuiFileDialog *vContextPtr, bool vDontSerializeCodeBasedBookmarks)
IGFD_C_API void IGFD_DeserializeBookmarks(ImGuiFileDialog *vContextPtr, const char *vBookmarks)
IGFD_C_API bool IGFD_IsOk(ImGuiFileDialog *vContextPtr)
IGFD_C_API ImGuiFileDialog * IGFD_Create(void)
IGFD_C_API void IGFD_SetFileStyle2(ImGuiFileDialog *vContextPtr, IGFD_FileStyleFlags vFlags, const char *vCriteria, float vR, float vG, float vB, float vA, const char *vIcon, ImFont *vFont)
IGFD_C_API IGFD_FileDialog_Config IGFD_FileDialog_Config_Get()
IGFD_C_API void IGFD_ClearFilesStyle(ImGuiFileDialog *vContextPtr)
IGFD_C_API bool IGFD_IsKeyOpened(ImGuiFileDialog *vContextPtr, const char *vCurrentOpenedKey)
IGFD_C_API void IGFD_Destroy(ImGuiFileDialog *vContextPtr)
IGFD_C_API char * IGFD_GetCurrentPath(ImGuiFileDialog *vContextPtr)
IGFD_C_API void SetLocales(ImGuiFileDialog *vContextPtr, const int vCategory, const char *vBeginLocale, const char *vEndLocale)
IGFD_C_API IGFD_Selection IGFD_Selection_Get(void)
IGFD_C_API void IGFD_AddBookmark(ImGuiFileDialog *vContextPtr, const char *vBookMarkName, const char *vBookMarkPath)
IGFD_C_API char * IGFD_GetCurrentFileName(ImGuiFileDialog *vContextPtr, IGFD_ResultMode vMode)
IGFD_C_API void IGFD_CloseDialog(ImGuiFileDialog *vContextPtr)
IGFD_C_API void IGFD_SetFileStyle(ImGuiFileDialog *vContextPtr, IGFD_FileStyleFlags vFlags, const char *vCriteria, ImVec4 vColor, const char *vIcon, ImFont *vFont)
IGFD_C_API bool IGFD_IsOpened(ImGuiFileDialog *vContextPtr)
IGFD_C_API char * IGFD_GetCurrentFilter(ImGuiFileDialog *vContextPtr)
IGFD_C_API void IGFD_SetFlashingAttenuationInSeconds(ImGuiFileDialog *vContextPtr, float vAttenValue)
IGFD_C_API void IGFD_OpenDialog(ImGuiFileDialog *vContextPtr, const char *vKey, const char *vTitle, const char *vFilters, const IGFD_FileDialog_Config vConfig)
IGFD_C_API bool IGFD_WasOpenedThisFrame(ImGuiFileDialog *vContextPtr)
IGFD_C_API bool IGFD_DisplayDialog(ImGuiFileDialog *vContextPtr, const char *vKey, ImGuiWindowFlags vFlags, ImVec2 vMinSize, ImVec2 vMaxSize)
IGFD_C_API void IGFD_Selection_DestroyContent(IGFD_Selection *vSelection)
IGFD_C_API bool IGFD_WasKeyOpenedThisFrame(ImGuiFileDialog *vContextPtr, const char *vKey)
IGFD_C_API bool IGFD_GetFileStyle(ImGuiFileDialog *vContextPtr, IGFD_FileStyleFlags vFlags, const char *vCriteria, ImVec4 *vOutColor, char **vOutIconText, ImFont **vOutFont)
int g
ImGuiContext * GImGui
Definition imgui.cc:1289
auto CalcTextSize(std::string_view str)
Definition ImGuiUtils.hh:39
void TextUnformatted(const std::string &str)
Definition ImGuiUtils.hh:26
void RightAlignText(std::string_view text, std::string_view maxWidthText)
Definition ImGuiUtils.hh:51
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
auto count(InputRange &&range, const T &value)
Definition ranges.hh:349
size_t size(std::string_view utf8)
auto filter(ForwardRange &&range, Predicate pred)
Definition view.hh:538
constexpr auto end(const zstring_view &x)