From 65a81d67b94989b7b143a02ac053ab1142364dda Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Tue, 13 Jun 2023 23:57:42 +0200 Subject: [PATCH 01/13] Update sample doc --- doc/6-sample/README.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/6-sample/README.md b/doc/6-sample/README.md index 05891a49a..2f4fe3cd4 100644 --- a/doc/6-sample/README.md +++ b/doc/6-sample/README.md @@ -1,6 +1,6 @@ # samples -In the context of Furnace, a sound sample (usually just referred to as a sample) is a string of numbers that hold sampled PCM audio. +In the context of Furnace, a sound sample (usually just referred to as a sample) is a string of numbers that represent sampled PCM audio. In Furnace, these samples can be generated by importing a .wav (think of it as an higher quality MP3) file. @@ -13,6 +13,7 @@ as of Furnace 0.6, the following sound chips have sample support: - PC Engine/TurboGrafx-16/HuC6280 - Amiga/Paula - SegaPCM +- NEC PC-9801/YM2608 (ADPCM channel only) - Neo Geo/Neo Geo CD/YM2610 (ADPCM channels only) - Seta/Allumer X1-010 - Atari Lynx @@ -21,12 +22,16 @@ as of Furnace 0.6, the following sound chips have sample support: - QSound - ZX Spectrum 48k (1-bit) - RF5C68 -- WonderSwan +- SNES/S-DSP +- WonderSwan (second channel only) - tildearrow Sound Unit - VERA (last channel only) - Y8950 (last channel only) - Konami K007232 -- a few more that I've forgotten to mention +- Irem GA20 +- Ensoniq OTTO/ES5506 +- Yamaha PCMD8/YMZ280B +- MMC5 (last channel only) ## compatible sample mode @@ -45,9 +50,9 @@ due to limitations in some of those sound chips, some restrictions exist: - NES: if on DPCM mode, only a limited selection of frequencies is available, and loop position isn't supported (only entire sample). - SegaPCM: your sample can't be longer than 65535, and the maximum frequency is 31.25KHz. - QSound: your sample can't be longer than 65535, and the loop length shall not be greater than 32767. -- Neo Geo (ADPCM-A): no looping supported. your samples will play at ~18.5KHz. -- Neo Geo (ADPCM-B): no loop position supported (only entire sample), and the maximum frequency is ~55KHz. -- YM2608: the maximum frequency is ~55KHz. +- Neo Geo (ADPCM-A): no looping supported. your samples will play at 18.518KHz. +- Neo Geo (ADPCM-B): no loop position supported (only entire sample), and the maximum frequency is 55.555KHz. +- YM2608: the maximum frequency is 55.555KHz. - MSM6258/MSM6295: no arbitrary frequency. - ZX Spectrum Beeper: your sample can't be longer than 2048, and it always plays at ~55KHz. - Seta/Allumer X1-010: frequency resolution is terrible in the lower end. your sample can't be longer than 131072. From baf2964faa4fd700ef46113040b42640e22e5d83 Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Wed, 14 Jun 2023 10:05:54 +0200 Subject: [PATCH 02/13] Update README.md wavetable --- doc/5-wave/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/5-wave/README.md b/doc/5-wave/README.md index 19a07b104..a42ffd815 100644 --- a/doc/5-wave/README.md +++ b/doc/5-wave/README.md @@ -6,7 +6,11 @@ Furnace's wavetable editor is rather simple, you can draw the waveform using mou Furnace's wavetable editor features multiple ways of creating desired waveform shape: -- Shape tab allows you to select a few predefined basic shapes and indirectly edit it via "Duty", "Exponent" and "XOR Point" sliders TODO: what the last two are doing? What is amplitude/phase for?) +- Shape tab allows you to select a few predefined basic shapes and indirectly edit it via "Duty", "Exponent" and "XOR Point" sliders: + - `Duty` slider affects mainly pulse waves, determining its wisth, like on C64/VRC6 + - `Exponent` powers the waveform in the mathematical sense of the word (^2, ^3 and so on) + - `XOR Point` determines the point where the waveform gets negated. + - TODO: amplitude/phase part - FM is for creating the waveform with frequency modulation synthesis principles: One can set carrier/modulation levels, frquency multiplier, connection between operators and FM waveforms of these operators. - WaveTools allows user to fine-tune the waveform: scale said waveform in both X and Y axes, smoothen, amplify, normalize, convert to signed/unisgned, invert or even randomize the wavetable. From f8346f55885cb51fd3119a6690bcc7f7591c70c7 Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Wed, 14 Jun 2023 11:16:02 +0200 Subject: [PATCH 03/13] Update opll.md drum mode internals --- doc/7-systems/opll.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/7-systems/opll.md b/doc/7-systems/opll.md index 8b16e8881..115e4bd70 100644 --- a/doc/7-systems/opll.md +++ b/doc/7-systems/opll.md @@ -12,7 +12,11 @@ OPLL also spawned a few derivative chips, the best known of these is: the YM2413 is equipped with the following features: - 9 channels of 2 operator FM synthesis -- A drum/percussion mode, replacing the last 3 voices with 5 rhythm channels +- A drum/percussion mode, replacing the last 3 voices with 5 rhythm channels, with drum mode tones hard-defined in the chip itself, like FM instruments. Only pitch might be altered. + + - Drum mode works like following: FM channel 7 is for Kick Drum, which is a normal FM channel but routed through mxier twice for 2x volume, like all drum sounds. FM channel 8 splits to Snare Drum and Hi-Hat. Snare Drum is the carrier and it works with a special 1 bit noise generator combined with a square wave, all possible by overriding phase-generator with some different synthesis method. Hi-Hat is the modulator and it works with the noise generator and also the special synthesis. CH9 splits to Top-Cymbal and Tom-Tom, Top-Cymbal is the carrier and only has the special synthesis, while Tom-Tom is basically a 1op wave. + - Special syntheis mentioned already is: 5 square waves are gathered from 4x, 64x and 128x the pitch of channel 8 and 16x and 64x the pitch of channel 9 and they go through a process where 2 HH bits OR'd together, then 1 HH and 1 TC bit OR'd, then the two TC bits OR'd together, and those 3 results get XOR'd. + - 1 user-definable patch (this patch can be changed throughout the course of the song) - 15 pre-defined patches which can all be used at the same time - Support for ADSR on both the modulator and the carrier @@ -60,4 +64,4 @@ the YM2413 is equipped with the following features: - `58xx`: set DR of operator 2. - `5Bxy`: set KSR of operator. - `x` is the operator (1-2). a value of 0 means "all operators". - - `y` determines whether KSR is on. \ No newline at end of file + - `y` determines whether KSR is on. From 72b654f708b507a2d4d49f13bddad07d6860222f Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Wed, 14 Jun 2023 11:17:42 +0200 Subject: [PATCH 04/13] Update opl.md ditto --- doc/7-systems/opl.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/7-systems/opl.md b/doc/7-systems/opl.md index b4a86e6db..80c002997 100644 --- a/doc/7-systems/opl.md +++ b/doc/7-systems/opl.md @@ -3,7 +3,7 @@ a series of FM sound chips which were very popular in DOS land. it was so popular that even Yamaha made a logo for it! essentially a downgraded version of Yamaha's other FM chips, with only 2 operators per channel. -however, it also had a drums mode, and later chips in the series added more waveforms (than just the typical sine) and even a 4-operator mode. +however, it also had a [drums mode](opll.md), and later chips in the series added more waveforms (than just the typical sine) and even a 4-operator mode. the original OPL (Yamaha YM3526) was present as an expansion for the Commodore 64 and MSX computers (erm, a variant of it). it only had 9 two-operator channels and drums mode. @@ -77,4 +77,4 @@ afterwards everyone moved to Windows and software mixed PCM streaming... - only in 4-op mode (OPL3). - `5Bxy`: set KSR of operator. - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". - - `y` determines whether KSR is on. \ No newline at end of file + - `y` determines whether KSR is on. From 3e7710651a3cb71d54819c440dc387df61ecf4fb Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Wed, 14 Jun 2023 11:23:08 +0200 Subject: [PATCH 05/13] Update ym2612.md src: genny vst and sonic.exe vs fnf --- doc/7-systems/ym2612.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/7-systems/ym2612.md b/doc/7-systems/ym2612.md index 1eeb148e2..cbb38e338 100644 --- a/doc/7-systems/ym2612.md +++ b/doc/7-systems/ym2612.md @@ -1,6 +1,6 @@ # Yamaha YM2612 -one of two chips that powered the Sega Genesis. It is a six-channel, four-operator FM synthesizer. Channel #6 can be turned into 8-bit PCM player. +one of two chips that powered the Sega Genesis. It is a six-channel, four-operator FM synthesizer. Channel #6 can be turned into 8-bit PCM player, that via software mixing, thanks to Z80 sound CPU, can play more than one channel of straight-shot samples at once. As of Furnace 0.6pre5, Furnace offers DualPCM, which allows 2 channels ofsoftware-mixed 8-bit PCM samples at 13750 Hz. # effects From 2658f4cfdcdaed60347cfef81dfacdd9ba33d628 Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Wed, 14 Jun 2023 11:23:26 +0200 Subject: [PATCH 06/13] typo --- doc/7-systems/ym2612.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/7-systems/ym2612.md b/doc/7-systems/ym2612.md index cbb38e338..dda452665 100644 --- a/doc/7-systems/ym2612.md +++ b/doc/7-systems/ym2612.md @@ -1,6 +1,6 @@ # Yamaha YM2612 -one of two chips that powered the Sega Genesis. It is a six-channel, four-operator FM synthesizer. Channel #6 can be turned into 8-bit PCM player, that via software mixing, thanks to Z80 sound CPU, can play more than one channel of straight-shot samples at once. As of Furnace 0.6pre5, Furnace offers DualPCM, which allows 2 channels ofsoftware-mixed 8-bit PCM samples at 13750 Hz. +one of two chips that powered the Sega Genesis. It is a six-channel, four-operator FM synthesizer. Channel #6 can be turned into 8-bit PCM player, that via software mixing, thanks to Z80 sound CPU, can play more than one channel of straight-shot samples at once. As of Furnace 0.6pre5, Furnace offers DualPCM, which allows 2 channels of software-mixed 8-bit PCM samples at 13750 Hz. # effects From e089c3940dfaa0ce14042bd1c1012e8320ab29a7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 15 Jun 2023 15:45:53 -0500 Subject: [PATCH 07/13] fix atari breakbeat issue #1157 --- demos/a2600/atari breakbeat.fur | Bin 1101 -> 1096 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/demos/a2600/atari breakbeat.fur b/demos/a2600/atari breakbeat.fur index dd4183befab50e4c8cde3799e4e2c3df042577a6..7dfbb0e35700a659689831e1cbfcb672b52c472e 100644 GIT binary patch literal 1096 zcmV-O1h@Nmob6XlZyQAzerIN9?IcY$AP`7CIDANzkfK^W5TpXNX$gu_FBa_|*tqMi zvGY;e5sic(kpOYy#x1JE58&7{9JuoX5ZXUbi33N_^33ecdL7%f7pq99o%orZdEe*# z*mqv9H=EnXy#uf5Z|oo5J?{FO4~b}lhGWOw{`e8cLP|z-^ua0FWUUvpZW;KBo;<2E z#td`%ozG%C-Qf+qUVCG&=X;$!-y6`o2c7pJ{_O^F&J=+$LuKrmAbJE`w}^fLwj|LH zz`9NJ9q=O%7K#1_R#u3dRieE$qCXsPUO?ergcsbfurM>sZ0Ma%uak+VK9&j1)t0`; zUp#6$THBA?!oghp-P}AHqI_eF*yy z_95(*Z8Er&_Q;@B+|L`p6=3^T?W4(=+;dnv>_YvC-$MMLZ79fC3V`(PL810 zR8Scy=gh%CBp%VvXgPdg(E?Q*eHF)E6+9*tZHrNaeq&-hkuTCjZ0a9)JwHb#TPl;{ zFcaj0WGtNwN>P#+qUR^4fwTH$b$!=S4krCx#1Js#H+JXBh@rg4~^8r1=)~yVpd5a<8Jt< z8$RlWkGkQyV_r55D4J-e7T^N`X{D{(32j>VRyc(tAq!|LeKq^M@CbSVNS`_{p8xx}`FO&75~eQcH@c*8EybZUGuQJ-l#Im4^YVMr%v{f-{N9}Wq}mhs z_obP+o=5%PUzk6TX6AYxxqTP|3rX-{- zRZ;{D{HVlVz`#Gizrex<12Y>F3lkF?V#K_&9p@V7lDjI!zSakJp5L9n@8jpIUoM`U ze$rg`D%IlJQwgl?Jo{r^632 zXvFR|Z_{hmi%ZR_*I250n{Z{laSh$yZvv$O8i@f3p%mZ+ZhvumV*&iZ&9ec%;x@+s z9*koO4*_f)0eE{1;Qc9p4`%_soCElF5#Z-#RKasC8}|hFq@xb6uR}W?|8KWLQ+^2V z@L$-<0rBG9a(FmBQ@(Q)KP#(`D~*Nf>eZ>SwfeLA`oqQN8&y2OJG^kdg#)(0g}L%L zta!`VTlAV6Fg<%3eq-0vtK-K@1^MS}`3#;66TS}?QWb23>NG_4k&%KmGUm_R!nspq z^XwD3J;y^ok3%zP)GG~*pci;h=5ujUz=pY_K70;RSVD!8MZ-?lu?hH?E@CACUd8|p zwAQ_5RfFIXCvZCi$bgq?#}IhWOHQ;|b{ocVlN66)iV>*QCu=y>)GTo%~v zl|#2*Ha=@Qrt>>Pikp^>>HKU+ansTzMvPHJ#t|2de4( zraVxy8kfQZXsr-P^cxxfy@~_^uuUNJCk42lO8gci$A~)Jwyw?~{hWVq^2_|D@SC3h zoIjiVmV!NqUnmGvC4!iGQazL{(>yzUlmqgumNQCH|fAtLmi1Kd2xK zi=RpOyZuz+-)a6;b@n>HE8*| Date: Fri, 16 Jun 2023 13:34:03 -0500 Subject: [PATCH 08/13] YM2612: fix DualPCM chan osc sustain issue #1162 --- src/engine/platform/genesis.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 9f9051054..8e92c40f3 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -188,9 +188,11 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) { oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<7; } else { oscBuf[i]->data[oscBuf[i]->needle++]=fm.dacdata<<7; + oscBuf[6]->data[oscBuf[6]->needle++]=0; } } else { oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fm.ch_out[i]<<(chipType==2?2:7),-32768,32767); + oscBuf[6]->data[oscBuf[6]->needle++]=0; } } else { oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fm.ch_out[i]<<(chipType==2?2:7),-32768,32767); @@ -251,9 +253,11 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) { oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<7; } else { oscBuf[i]->data[oscBuf[i]->needle++]=fm_ymfm->debug_dac_data()<<7; + oscBuf[6]->data[oscBuf[6]->needle++]=0; } } else { oscBuf[i]->data[oscBuf[i]->needle++]=chOut; + oscBuf[6]->data[oscBuf[6]->needle++]=0; } } else { oscBuf[i]->data[oscBuf[i]->needle++]=chOut; From 7b30cfb1b75ad4569b86134fc4a0235e52f7c019 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 16 Jun 2023 15:43:41 -0500 Subject: [PATCH 09/13] IGFD: some changes --- extern/igfd/ImGuiFileDialog.cpp | 13 +++++++------ extern/igfd/ImGuiFileDialog.h | 2 ++ src/gui/fileDialog.cpp | 3 +++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/extern/igfd/ImGuiFileDialog.cpp b/extern/igfd/ImGuiFileDialog.cpp index 4af6ca444..4ad01ba1f 100644 --- a/extern/igfd/ImGuiFileDialog.cpp +++ b/extern/igfd/ImGuiFileDialog.cpp @@ -133,6 +133,9 @@ namespace IGFD #ifndef resetButtonString #define resetButtonString ICON_FA_REPEAT #endif // resetButtonString +#ifndef homeButtonString +#define homeButtonString ICON_FA_HOME +#endif // bomeButtonString #ifndef drivesButtonString #define drivesButtonString ICON_FA_HDD_O #endif // drivesButtonString @@ -170,7 +173,7 @@ namespace IGFD #define buttonEditPathString "Edit path\nYou can also right click on path buttons" #endif // buttonEditPathString #ifndef buttonResetPathString -#define buttonResetPathString "Reset to current directory" +#define buttonResetPathString "Go to home directory" #endif // buttonResetPathString #ifndef buttonParentDirString #define buttonParentDirString "Go to parent directory" @@ -700,10 +703,8 @@ namespace IGFD if (ImGui::IsItemHovered()) ImGui::SetTooltip(buttonResetSearchString); ImGui::SameLine(); - ImGui::Text(searchString); - ImGui::SameLine(); ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - bool edited = ImGui::InputText("##InputImGuiFileDialogSearchField", puSearchBuffer, MAX_FILE_DIALOG_NAME_BUFFER); + bool edited = ImGui::InputTextWithHint("##InputImGuiFileDialogSearchField", searchString, puSearchBuffer, MAX_FILE_DIALOG_NAME_BUFFER); if (ImGui::GetItemID() == ImGui::GetActiveID()) puSearchInputIsActive = true; ImGui::PopItemWidth(); @@ -2245,9 +2246,9 @@ namespace IGFD void IGFD::FileManager::DrawPathComposer(const FileDialogInternal& vFileDialogInternal) { - if (IMGUI_BUTTON(resetButtonString)) + if (IMGUI_BUTTON(homeButtonString)) { - SetCurrentPath("."); + SetCurrentPath(FileDialog::Instance()->homePath); OpenCurrentPath(vFileDialogInternal); } if (ImGui::IsItemHovered()) diff --git a/extern/igfd/ImGuiFileDialog.h b/extern/igfd/ImGuiFileDialog.h index 827a987eb..2ad9cbc82 100644 --- a/extern/igfd/ImGuiFileDialog.h +++ b/extern/igfd/ImGuiFileDialog.h @@ -878,6 +878,7 @@ namespace IGFD bool fileListActuallyEmpty = false; std::string puDLGpath; // base path set by user when OpenDialog/OpenModal was called + std::string puError; // last error std::string puDLGDefaultFileName; // base default file path name set by user when OpenDialog/OpenModal was called size_t puDLGcountSelectionMax = 1U; // 0 for infinite // base max selection count set by user when OpenDialog/OpenModal was called bool puDLGDirectoryMode = false; // is directory mode (defiend like : puDLGDirectoryMode = (filters.empty())) @@ -1145,6 +1146,7 @@ namespace IGFD double DpiScale; bool singleClickSel; bool mobileMode; + std::string homePath; public: static FileDialog* Instance() // Singleton for easier accces form anywhere but only one dialog at a time diff --git a/src/gui/fileDialog.cpp b/src/gui/fileDialog.cpp index 94ffa94ef..a0a45e1e0 100644 --- a/src/gui/fileDialog.cpp +++ b/src/gui/fileDialog.cpp @@ -1,5 +1,6 @@ #include "fileDialog.h" #include "ImGuiFileDialog.h" +#include "util.h" #include "../ta-log.h" #ifdef USE_NFD @@ -152,6 +153,7 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector filter, c ImGuiFileDialog::Instance()->singleClickSel=mobileUI; ImGuiFileDialog::Instance()->DpiScale=dpiScale; ImGuiFileDialog::Instance()->mobileMode=mobileUI; + ImGuiFileDialog::Instance()->homePath=getHomeDir(); ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,allowMultiple?999:1,nullptr,0,clickCallback); } opened=true; @@ -235,6 +237,7 @@ bool FurnaceGUIFileDialog::openSave(String header, std::vector filter, c ImGuiFileDialog::Instance()->singleClickSel=false; ImGuiFileDialog::Instance()->DpiScale=dpiScale; ImGuiFileDialog::Instance()->mobileMode=mobileUI; + ImGuiFileDialog::Instance()->homePath=getHomeDir(); ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); } opened=true; From 9b1fea5c36f2bed12e76b8d1238411ee2f5f17c2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 16 Jun 2023 16:12:20 -0500 Subject: [PATCH 10/13] IGFD: remove std::filesystem support it is unused in Furnace --- extern/igfd/ImGuiFileDialog.cpp | 89 +---------------------------- extern/igfd/ImGuiFileDialog.h | 12 +--- extern/igfd/ImGuiFileDialogConfig.h | 3 - 3 files changed, 3 insertions(+), 101 deletions(-) diff --git a/extern/igfd/ImGuiFileDialog.cpp b/extern/igfd/ImGuiFileDialog.cpp index 4ad01ba1f..b4941c793 100644 --- a/extern/igfd/ImGuiFileDialog.cpp +++ b/extern/igfd/ImGuiFileDialog.cpp @@ -42,10 +42,6 @@ SOFTWARE. #include #include #include -// this option need c++17 -#ifdef USE_STD_FILESYSTEM - #include -#endif #if defined (__EMSCRIPTEN__) // EMSCRIPTEN #include #endif // EMSCRIPTEN @@ -53,12 +49,7 @@ SOFTWARE. #define stat _stat #define stricmp _stricmp #include - // this option need c++17 - #ifdef USE_STD_FILESYSTEM - #include - #else - #include "dirent/dirent.h" // directly open the dirent file attached to this lib - #endif // USE_STD_FILESYSTEM + #include "dirent/dirent.h" // directly open the dirent file attached to this lib #define PATH_SEP '\\' #ifndef PATH_MAX #define PATH_MAX 260 @@ -67,10 +58,7 @@ SOFTWARE. #define UNIX #define stricmp strcasecmp #include - // this option need c++17 - #ifndef USE_STD_FILESYSTEM - #include - #endif // USE_STD_FILESYSTEM + #include #define PATH_SEP '/' #endif // defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) @@ -320,12 +308,10 @@ namespace IGFD //// INLINE FUNCTIONS /////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// -#ifndef USE_STD_FILESYSTEM inline int inAlphaSort(const struct dirent** a, const struct dirent** b) { return strcoll((*a)->d_name, (*b)->d_name); } -#endif ///////////////////////////////////////////////////////////////////////////////////// //// FILE EXTENTIONS INFOS ////////////////////////////////////////////////////////// @@ -497,16 +483,6 @@ namespace IGFD if (!name.empty()) { -#ifdef USE_STD_FILESYSTEM - namespace fs = std::filesystem; -#ifdef WIN32 - std::wstring wname = IGFD::Utils::string_to_wstring(name.c_str()); - fs::path pathName = fs::path(wname); -#else - fs::path pathName = fs::path(name); -#endif - bExists = fs::is_directory(pathName); -#else DIR* pDir = nullptr; pDir = opendir(name.c_str()); if (pDir != nullptr) @@ -514,7 +490,6 @@ namespace IGFD bExists = true; (void)closedir(pDir); } -#endif // USE_STD_FILESYSTEM } return bExists; // this is not a directory! @@ -529,18 +504,11 @@ namespace IGFD if (!IsDirectoryExist(name)) { #ifdef WIN32 -#ifdef USE_STD_FILESYSTEM - namespace fs = std::filesystem; - std::wstring wname = IGFD::Utils::string_to_wstring(name.c_str()); - fs::path pathName = fs::path(wname); - res = fs::create_directory(pathName); -#else std::wstring wname = IGFD::Utils::string_to_wstring(name); if (CreateDirectoryW(wname.c_str(), nullptr)) { res = true; } -#endif // USE_STD_FILESYSTEM #elif defined(__EMSCRIPTEN__) std::string str = std::string("FS.mkdir('") + name + "');"; emscripten_run_script(str.c_str()); @@ -563,26 +531,6 @@ namespace IGFD return res; } -#ifdef USE_STD_FILESYSTEM - // https://github.com/aiekick/ImGuiFileDialog/issues/54 - IGFD::Utils::PathStruct IGFD::Utils::ParsePathFileName(const std::string& vPathFileName) - { - namespace fs = std::filesystem; - PathStruct res; - if (vPathFileName.empty()) - return res; - - auto fsPath = fs::path(vPathFileName); - - if (fs::is_regular_file(fsPath)) { - res.name = fsPath.string(); - res.path = fsPath.parent_path().string(); - res.isOk = true; - } - - return res; - } -#else IGFD::Utils::PathStruct IGFD::Utils::ParsePathFileName(const std::string& vPathFileName) { PathStruct res; @@ -627,7 +575,6 @@ namespace IGFD return res; } -#endif // USE_STD_FILESYSTEM void IGFD::Utils::AppendToBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr) { std::string st = vStr; @@ -1552,24 +1499,6 @@ namespace IGFD ClearFileLists(); -#ifdef USE_STD_FILESYSTEM - //const auto wpath = IGFD::Utils::WGetString(path.c_str()); - const std::filesystem::path fspath(path); - const auto dir_iter = std::filesystem::directory_iterator(fspath); - AddFile(vFileDialogInternal, path, "..", 'd'); - for (const auto& file : dir_iter) - { - char fileType = 0; - if (file.is_symlink()) - fileType = 'l'; - else if (file.is_directory()) - fileType = 'd'; - else - fileType = 'f'; - auto fileNameExt = file.path().filename().string(); - AddFile(vFileDialogInternal, path, fileNameExt, fileType); - } -#else // dirent struct dirent** files = nullptr; int n = scandir(path.c_str(), &files, nullptr, inAlphaSort); logV("IGFD: %d entries in directory",n); @@ -1643,7 +1572,6 @@ namespace IGFD } else { logV("IGFD: it's empty"); } -#endif // USE_STD_FILESYSTEM logV("IGFD: sorting fields..."); SortFields(vFileDialogInternal, puSortingField, false); @@ -1891,16 +1819,6 @@ namespace IGFD path += std::string(1u, PATH_SEP); #endif // WIN32 -#ifdef USE_STD_FILESYSTEM - namespace fs = std::filesystem; - bool dir_opened = fs::is_directory(vPath); - if (!dir_opened) - { - path = "."; - dir_opened = fs::is_directory(vPath); - } - if (dir_opened) -#else DIR* dir = opendir(path.c_str()); if (dir == nullptr) { @@ -1909,7 +1827,6 @@ namespace IGFD } if (dir != nullptr) -#endif // USE_STD_FILESYSTEM { #ifdef WIN32 DWORD numchar = 0; @@ -1945,9 +1862,7 @@ namespace IGFD #endif // WIN32 } } -#ifndef USE_STD_FILESYSTEM closedir(dir); -#endif } } diff --git a/extern/igfd/ImGuiFileDialog.h b/extern/igfd/ImGuiFileDialog.h index 2ad9cbc82..9c2d246f8 100644 --- a/extern/igfd/ImGuiFileDialog.h +++ b/extern/igfd/ImGuiFileDialog.h @@ -534,23 +534,13 @@ if (IGFD_DisplayDialog(cfiledialog, "filedlg", ImGuiWindowFlags_NoCollapse, minS // destroy ImGuiFileDialog IGFD_Destroy(cfiledialog); ------------------------------------------------------------------------------------------------------------------ -## Std::filesystem (c++17) can be used instead of dirent.h ------------------------------------------------------------------------------------------------------------------ - -you just need to uncomment that in the config file - -#define USE_STD_FILESYSTEM - -in this mode dirent is not more required - ----------------------------------------------------------------------------------------------------------------- ## How to Integrate ImGuiFileDialog in your project ----------------------------------------------------------------------------------------------------------------- ### ImGuiFileDialog require : -* dirent v1.23 (only when USE_STD_FILESYSTEM is not defined) (https://github.com/tronkko/dirent/tree/v1.23) lib, only for windows. Successfully tested with version v1.23 only +* dirent v1.23 (https://github.com/tronkko/dirent/tree/v1.23) lib, only for windows. Successfully tested with version v1.23 only * Dear ImGui (https://github.com/ocornut/imgui/tree/master) (with/without tables widgets) ### Customize ImGuiFileDialog : diff --git a/extern/igfd/ImGuiFileDialogConfig.h b/extern/igfd/ImGuiFileDialogConfig.h index 1db638dda..4a212b600 100644 --- a/extern/igfd/ImGuiFileDialogConfig.h +++ b/extern/igfd/ImGuiFileDialogConfig.h @@ -2,9 +2,6 @@ // uncomment and modify defines under for customize ImGuiFileDialog -//this options need c++17 -//#define USE_STD_FILESYSTEM - //#define MAX_FILE_DIALOG_NAME_BUFFER 1024 //#define MAX_PATH_BUFFER_SIZE 1024 From ad9981fdea5aed2ee89b75a49711982cd339b537 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 16 Jun 2023 16:26:22 -0500 Subject: [PATCH 11/13] IGFD: tabs to spaces... --- extern/igfd/ImGuiFileDialog.cpp | 7854 +++++++++++++++---------------- extern/igfd/ImGuiFileDialog.h | 1758 +++---- 2 files changed, 4806 insertions(+), 4806 deletions(-) diff --git a/extern/igfd/ImGuiFileDialog.cpp b/extern/igfd/ImGuiFileDialog.cpp index b4941c793..a31048394 100644 --- a/extern/igfd/ImGuiFileDialog.cpp +++ b/extern/igfd/ImGuiFileDialog.cpp @@ -26,7 +26,7 @@ SOFTWARE. */ #ifndef IMGUI_DEFINE_MATH_OPERATORS - #define IMGUI_DEFINE_MATH_OPERATORS + #define IMGUI_DEFINE_MATH_OPERATORS #endif // IMGUI_DEFINE_MATH_OPERATORS #include "ImGuiFileDialog.h" @@ -43,23 +43,23 @@ SOFTWARE. #include #include #if defined (__EMSCRIPTEN__) // EMSCRIPTEN - #include + #include #endif // EMSCRIPTEN #ifdef WIN32 - #define stat _stat - #define stricmp _stricmp - #include - #include "dirent/dirent.h" // directly open the dirent file attached to this lib - #define PATH_SEP '\\' - #ifndef PATH_MAX - #define PATH_MAX 260 - #endif // PATH_MAX + #define stat _stat + #define stricmp _stricmp + #include + #include "dirent/dirent.h" // directly open the dirent file attached to this lib + #define PATH_SEP '\\' + #ifndef PATH_MAX + #define PATH_MAX 260 + #endif // PATH_MAX #elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined (__EMSCRIPTEN__) || defined(__HAIKU__) - #define UNIX - #define stricmp strcasecmp - #include - #include - #define PATH_SEP '/' + #define UNIX + #define stricmp strcasecmp + #include + #include + #define PATH_SEP '/' #endif // defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) #include "imgui.h" @@ -229,29 +229,29 @@ namespace IGFD #define DisplayMode_ThumbailsList_ImageHeight 32.0f #endif // DisplayMode_ThumbailsList_ImageHeight #ifndef IMGUI_RADIO_BUTTON - inline bool inRadioButton(const char* vLabel, bool vToggled) - { - bool pressed = false; + inline bool inRadioButton(const char* vLabel, bool vToggled) + { + bool pressed = false; - if (vToggled) - { - ImVec4 bua = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive); - ImVec4 te = ImGui::GetStyleColorVec4(ImGuiCol_Text); - ImGui::PushStyleColor(ImGuiCol_Button, te); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, te); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, te); - ImGui::PushStyleColor(ImGuiCol_Text, bua); - } + if (vToggled) + { + ImVec4 bua = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive); + ImVec4 te = ImGui::GetStyleColorVec4(ImGuiCol_Text); + ImGui::PushStyleColor(ImGuiCol_Button, te); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, te); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, te); + ImGui::PushStyleColor(ImGuiCol_Text, bua); + } - pressed = IMGUI_BUTTON(vLabel); + pressed = IMGUI_BUTTON(vLabel); - if (vToggled) - { - ImGui::PopStyleColor(4); //-V112 - } + if (vToggled) + { + ImGui::PopStyleColor(4); //-V112 + } - return pressed; - } + return pressed; + } #define IMGUI_RADIO_BUTTON inRadioButton #endif // IMGUI_RADIO_BUTTON #endif // USE_THUMBNAILS @@ -272,902 +272,902 @@ namespace IGFD #define removeBookmarkButtonString "-" #endif // removeBookmarkButtonString #ifndef IMGUI_TOGGLE_BUTTON - inline bool inToggleButton(const char* vLabel, bool* vToggled) - { - bool pressed = false; + inline bool inToggleButton(const char* vLabel, bool* vToggled) + { + bool pressed = false; - if (vToggled && *vToggled) - { - ImVec4 bua = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive); - //ImVec4 buh = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); - //ImVec4 bu = ImGui::GetStyleColorVec4(ImGuiCol_Button); - ImVec4 te = ImGui::GetStyleColorVec4(ImGuiCol_Text); - ImGui::PushStyleColor(ImGuiCol_Button, te); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, te); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, te); - ImGui::PushStyleColor(ImGuiCol_Text, bua); - } + if (vToggled && *vToggled) + { + ImVec4 bua = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive); + //ImVec4 buh = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); + //ImVec4 bu = ImGui::GetStyleColorVec4(ImGuiCol_Button); + ImVec4 te = ImGui::GetStyleColorVec4(ImGuiCol_Text); + ImGui::PushStyleColor(ImGuiCol_Button, te); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, te); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, te); + ImGui::PushStyleColor(ImGuiCol_Text, bua); + } - pressed = IMGUI_BUTTON(vLabel); + pressed = IMGUI_BUTTON(vLabel); - if (vToggled && *vToggled) - { - ImGui::PopStyleColor(4); //-V112 - } + if (vToggled && *vToggled) + { + ImGui::PopStyleColor(4); //-V112 + } - if (vToggled && pressed) - *vToggled = !*vToggled; + if (vToggled && pressed) + *vToggled = !*vToggled; - return pressed; - } + return pressed; + } #define IMGUI_TOGGLE_BUTTON inToggleButton #endif // IMGUI_TOGGLE_BUTTON #endif // USE_BOOKMARK - ///////////////////////////////////////////////////////////////////////////////////// - //// INLINE FUNCTIONS /////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + //// INLINE FUNCTIONS /////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// - inline int inAlphaSort(const struct dirent** a, const struct dirent** b) - { - return strcoll((*a)->d_name, (*b)->d_name); - } + inline int inAlphaSort(const struct dirent** a, const struct dirent** b) + { + return strcoll((*a)->d_name, (*b)->d_name); + } - ///////////////////////////////////////////////////////////////////////////////////// - //// FILE EXTENTIONS INFOS ////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + //// FILE EXTENTIONS INFOS ////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// - IGFD::FileStyle::FileStyle() - : color(0, 0, 0, 0) - { + IGFD::FileStyle::FileStyle() + : color(0, 0, 0, 0) + { - } - - IGFD::FileStyle::FileStyle(const FileStyle& vStyle) - { - color = vStyle.color; - icon = vStyle.icon; - font = vStyle.font; - flags = vStyle.flags; - } + } + + IGFD::FileStyle::FileStyle(const FileStyle& vStyle) + { + color = vStyle.color; + icon = vStyle.icon; + font = vStyle.font; + flags = vStyle.flags; + } - IGFD::FileStyle::FileStyle(const ImVec4& vColor, const std::string& vIcon, ImFont* vFont) - : color(vColor), icon(vIcon), font(vFont) - { + IGFD::FileStyle::FileStyle(const ImVec4& vColor, const std::string& vIcon, ImFont* vFont) + : color(vColor), icon(vIcon), font(vFont) + { - } + } - ///////////////////////////////////////////////////////////////////////////////////// - //// FILE INFOS ///////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + //// FILE INFOS ///////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// - // https://github.com/ocornut/imgui/issues/1720 - bool IGFD::Utils::Splitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size) - { - using namespace ImGui; - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiID id = window->GetID("##Splitter"); - ImRect bb; - bb.Min = window->DC.CursorPos + (split_vertically ? ImVec2(*size1, 0.0f) : ImVec2(0.0f, *size1)); - bb.Max = bb.Min + CalcItemSize(split_vertically ? ImVec2(thickness, splitter_long_axis_size) : ImVec2(splitter_long_axis_size, thickness), 0.0f, 0.0f); - return SplitterBehavior(bb, id, split_vertically ? ImGuiAxis_X : ImGuiAxis_Y, size1, size2, min_size1, min_size2, 1.0f); - } + // https://github.com/ocornut/imgui/issues/1720 + bool IGFD::Utils::Splitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size) + { + using namespace ImGui; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiID id = window->GetID("##Splitter"); + ImRect bb; + bb.Min = window->DC.CursorPos + (split_vertically ? ImVec2(*size1, 0.0f) : ImVec2(0.0f, *size1)); + bb.Max = bb.Min + CalcItemSize(split_vertically ? ImVec2(thickness, splitter_long_axis_size) : ImVec2(splitter_long_axis_size, thickness), 0.0f, 0.0f); + return SplitterBehavior(bb, id, split_vertically ? ImGuiAxis_X : ImGuiAxis_Y, size1, size2, min_size1, min_size2, 1.0f); + } #ifdef WIN32 - bool IGFD::Utils::WReplaceString(std::wstring& str, const std::wstring& oldStr, const std::wstring& newStr) - { - bool found = false; - size_t pos = 0; - while ((pos = str.find(oldStr, pos)) != std::wstring::npos) - { - found = true; - str.replace(pos, oldStr.length(), newStr); - pos += newStr.length(); - } - return found; - } + bool IGFD::Utils::WReplaceString(std::wstring& str, const std::wstring& oldStr, const std::wstring& newStr) + { + bool found = false; + size_t pos = 0; + while ((pos = str.find(oldStr, pos)) != std::wstring::npos) + { + found = true; + str.replace(pos, oldStr.length(), newStr); + pos += newStr.length(); + } + return found; + } - std::vector IGFD::Utils::WSplitStringToVector(const std::wstring& text, char delimiter, bool pushEmpty) - { - std::vector arr; - if (!text.empty()) - { - std::wstring::size_type start = 0; - std::wstring::size_type end = text.find(delimiter, start); - while (end != std::wstring::npos) - { - std::wstring token = text.substr(start, end - start); - if (!token.empty() || (token.empty() && pushEmpty)) //-V728 - arr.push_back(token); - start = end + 1; - end = text.find(delimiter, start); - } - std::wstring token = text.substr(start); - if (!token.empty() || (token.empty() && pushEmpty)) //-V728 - arr.push_back(token); - } - return arr; - } + std::vector IGFD::Utils::WSplitStringToVector(const std::wstring& text, char delimiter, bool pushEmpty) + { + std::vector arr; + if (!text.empty()) + { + std::wstring::size_type start = 0; + std::wstring::size_type end = text.find(delimiter, start); + while (end != std::wstring::npos) + { + std::wstring token = text.substr(start, end - start); + if (!token.empty() || (token.empty() && pushEmpty)) //-V728 + arr.push_back(token); + start = end + 1; + end = text.find(delimiter, start); + } + std::wstring token = text.substr(start); + if (!token.empty() || (token.empty() && pushEmpty)) //-V728 + arr.push_back(token); + } + return arr; + } - std::wstring IGFD::Utils::string_to_wstring(const std::string& str) - { - std::wstring ret; - if (!str.empty()) - { - size_t sz = std::mbstowcs(nullptr, str.c_str(), str.size()); - if (sz) - { - ret.resize(sz); - std::mbstowcs((wchar_t*)ret.data(), str.c_str(), sz); - } - } - return ret; - } + std::wstring IGFD::Utils::string_to_wstring(const std::string& str) + { + std::wstring ret; + if (!str.empty()) + { + size_t sz = std::mbstowcs(nullptr, str.c_str(), str.size()); + if (sz) + { + ret.resize(sz); + std::mbstowcs((wchar_t*)ret.data(), str.c_str(), sz); + } + } + return ret; + } - std::string IGFD::Utils::wstring_to_string(const std::wstring& str) - { - std::string ret; - if (!str.empty()) - { - size_t sz = std::wcstombs(nullptr, str.c_str(), str.size()); - if (sz) - { - ret.resize(sz); - std::wcstombs((char*)ret.data(), str.c_str(), sz); - } - } - return ret; - } + std::string IGFD::Utils::wstring_to_string(const std::wstring& str) + { + std::string ret; + if (!str.empty()) + { + size_t sz = std::wcstombs(nullptr, str.c_str(), str.size()); + if (sz) + { + ret.resize(sz); + std::wcstombs((char*)ret.data(), str.c_str(), sz); + } + } + return ret; + } #endif // WIN32 - bool IGFD::Utils::ReplaceString(std::string& str, const std::string& oldStr, const std::string& newStr) - { - bool found = false; - size_t pos = 0; - while ((pos = str.find(oldStr, pos)) != std::string::npos) - { - found = true; - str.replace(pos, oldStr.length(), newStr); - pos += newStr.length(); - } - return found; - } + bool IGFD::Utils::ReplaceString(std::string& str, const std::string& oldStr, const std::string& newStr) + { + bool found = false; + size_t pos = 0; + while ((pos = str.find(oldStr, pos)) != std::string::npos) + { + found = true; + str.replace(pos, oldStr.length(), newStr); + pos += newStr.length(); + } + return found; + } - std::vector IGFD::Utils::SplitStringToVector(const std::string& text, char delimiter, bool pushEmpty) - { - std::vector arr; - if (!text.empty()) - { - size_t start = 0; - size_t end = text.find(delimiter, start); - while (end != std::string::npos) - { - auto token = text.substr(start, end - start); - if (!token.empty() || (token.empty() && pushEmpty)) //-V728 - arr.push_back(token); - start = end + 1; - end = text.find(delimiter, start); - } - auto token = text.substr(start); - if (!token.empty() || (token.empty() && pushEmpty)) //-V728 - arr.push_back(token); - } - return arr; - } + std::vector IGFD::Utils::SplitStringToVector(const std::string& text, char delimiter, bool pushEmpty) + { + std::vector arr; + if (!text.empty()) + { + size_t start = 0; + size_t end = text.find(delimiter, start); + while (end != std::string::npos) + { + auto token = text.substr(start, end - start); + if (!token.empty() || (token.empty() && pushEmpty)) //-V728 + arr.push_back(token); + start = end + 1; + end = text.find(delimiter, start); + } + auto token = text.substr(start); + if (!token.empty() || (token.empty() && pushEmpty)) //-V728 + arr.push_back(token); + } + return arr; + } - std::vector IGFD::Utils::GetDrivesList() - { - std::vector res; + std::vector IGFD::Utils::GetDrivesList() + { + std::vector res; #ifdef WIN32 - const DWORD mydrives = 2048; - char lpBuffer[2048]; + const DWORD mydrives = 2048; + char lpBuffer[2048]; #define mini(a,b) (((a) < (b)) ? (a) : (b)) - const DWORD countChars = mini(GetLogicalDriveStringsA(mydrives, lpBuffer), 2047); + const DWORD countChars = mini(GetLogicalDriveStringsA(mydrives, lpBuffer), 2047); #undef mini - if (countChars > 0) - { - std::string var = std::string(lpBuffer, (size_t)countChars); - IGFD::Utils::ReplaceString(var, "\\", ""); - res = IGFD::Utils::SplitStringToVector(var, '\0', false); - } + if (countChars > 0) + { + std::string var = std::string(lpBuffer, (size_t)countChars); + IGFD::Utils::ReplaceString(var, "\\", ""); + res = IGFD::Utils::SplitStringToVector(var, '\0', false); + } #endif // WIN32 - return res; - } + return res; + } - bool IGFD::Utils::IsDirectoryExist(const std::string& name) - { - bool bExists = false; + bool IGFD::Utils::IsDirectoryExist(const std::string& name) + { + bool bExists = false; - if (!name.empty()) - { - DIR* pDir = nullptr; - pDir = opendir(name.c_str()); - if (pDir != nullptr) - { - bExists = true; - (void)closedir(pDir); - } - } + if (!name.empty()) + { + DIR* pDir = nullptr; + pDir = opendir(name.c_str()); + if (pDir != nullptr) + { + bExists = true; + (void)closedir(pDir); + } + } - return bExists; // this is not a directory! - } + return bExists; // this is not a directory! + } - bool IGFD::Utils::CreateDirectoryIfNotExist(const std::string& name) - { - bool res = false; + bool IGFD::Utils::CreateDirectoryIfNotExist(const std::string& name) + { + bool res = false; - if (!name.empty()) - { - if (!IsDirectoryExist(name)) - { + if (!name.empty()) + { + if (!IsDirectoryExist(name)) + { #ifdef WIN32 - std::wstring wname = IGFD::Utils::string_to_wstring(name); - if (CreateDirectoryW(wname.c_str(), nullptr)) - { - res = true; - } + std::wstring wname = IGFD::Utils::string_to_wstring(name); + if (CreateDirectoryW(wname.c_str(), nullptr)) + { + res = true; + } #elif defined(__EMSCRIPTEN__) - std::string str = std::string("FS.mkdir('") + name + "');"; - emscripten_run_script(str.c_str()); - res = true; + std::string str = std::string("FS.mkdir('") + name + "');"; + emscripten_run_script(str.c_str()); + res = true; #elif defined(UNIX) - char buffer[PATH_MAX] = {}; - snprintf(buffer, PATH_MAX, "mkdir -p %s", name.c_str()); - const int dir_err = std::system(buffer); - if (dir_err != -1) - { - res = true; - } + char buffer[PATH_MAX] = {}; + snprintf(buffer, PATH_MAX, "mkdir -p %s", name.c_str()); + const int dir_err = std::system(buffer); + if (dir_err != -1) + { + res = true; + } #endif // WIN32 - if (!res) { - std::cout << "Error creating directory " << name << std::endl; - } - } - } + if (!res) { + std::cout << "Error creating directory " << name << std::endl; + } + } + } - return res; - } + return res; + } - IGFD::Utils::PathStruct IGFD::Utils::ParsePathFileName(const std::string& vPathFileName) - { - PathStruct res; + IGFD::Utils::PathStruct IGFD::Utils::ParsePathFileName(const std::string& vPathFileName) + { + PathStruct res; - if (!vPathFileName.empty()) - { - std::string pfn = vPathFileName; - std::string separator(1u, PATH_SEP); - IGFD::Utils::ReplaceString(pfn, "\\", separator); - IGFD::Utils::ReplaceString(pfn, "/", separator); + if (!vPathFileName.empty()) + { + std::string pfn = vPathFileName; + std::string separator(1u, PATH_SEP); + IGFD::Utils::ReplaceString(pfn, "\\", separator); + IGFD::Utils::ReplaceString(pfn, "/", separator); - size_t lastSlash = pfn.find_last_of(separator); - if (lastSlash != std::string::npos) - { - res.name = pfn.substr(lastSlash + 1); - res.path = pfn.substr(0, lastSlash); - res.isOk = true; - } + size_t lastSlash = pfn.find_last_of(separator); + if (lastSlash != std::string::npos) + { + res.name = pfn.substr(lastSlash + 1); + res.path = pfn.substr(0, lastSlash); + res.isOk = true; + } - size_t lastPoint = pfn.find_last_of('.'); - if (lastPoint != std::string::npos) - { - if (!res.isOk) - { - res.name = pfn; - res.isOk = true; - } - res.ext = pfn.substr(lastPoint + 1); - IGFD::Utils::ReplaceString(res.name, "." + res.ext, ""); - } + size_t lastPoint = pfn.find_last_of('.'); + if (lastPoint != std::string::npos) + { + if (!res.isOk) + { + res.name = pfn; + res.isOk = true; + } + res.ext = pfn.substr(lastPoint + 1); + IGFD::Utils::ReplaceString(res.name, "." + res.ext, ""); + } if (res.path.empty()) { res.path=separator; } - if (!res.isOk) - { - res.name = std::move(pfn); - res.isOk = true; - } - } + if (!res.isOk) + { + res.name = std::move(pfn); + res.isOk = true; + } + } - return res; - } - void IGFD::Utils::AppendToBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr) - { - std::string st = vStr; - size_t len = vBufferLen - 1u; - size_t slen = strlen(vBuffer); + return res; + } + void IGFD::Utils::AppendToBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr) + { + std::string st = vStr; + size_t len = vBufferLen - 1u; + size_t slen = strlen(vBuffer); - if (!st.empty() && st != "\n") - { - IGFD::Utils::ReplaceString(st, "\n", ""); - IGFD::Utils::ReplaceString(st, "\r", ""); - } - vBuffer[slen] = '\0'; - std::string str = std::string(vBuffer); - //if (!str.empty()) str += "\n"; - str += vStr; - if (len > str.size()) len = str.size(); + if (!st.empty() && st != "\n") + { + IGFD::Utils::ReplaceString(st, "\n", ""); + IGFD::Utils::ReplaceString(st, "\r", ""); + } + vBuffer[slen] = '\0'; + std::string str = std::string(vBuffer); + //if (!str.empty()) str += "\n"; + str += vStr; + if (len > str.size()) len = str.size(); #ifdef MSVC - strncpy_s(vBuffer, vBufferLen, str.c_str(), len); + strncpy_s(vBuffer, vBufferLen, str.c_str(), len); #else // MSVC - strncpy(vBuffer, str.c_str(), len); + strncpy(vBuffer, str.c_str(), len); #endif // MSVC - vBuffer[len] = '\0'; - } + vBuffer[len] = '\0'; + } - void IGFD::Utils::ResetBuffer(char* vBuffer) - { - vBuffer[0] = '\0'; - } + void IGFD::Utils::ResetBuffer(char* vBuffer) + { + vBuffer[0] = '\0'; + } - void IGFD::Utils::SetBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr) - { - ResetBuffer(vBuffer); - AppendToBuffer(vBuffer, vBufferLen, vStr); - } + void IGFD::Utils::SetBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr) + { + ResetBuffer(vBuffer); + AppendToBuffer(vBuffer, vBufferLen, vStr); + } - ///////////////////////////////////////////////////////////////////////////////////// - //// FILE INFOS ///////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + //// FILE INFOS ///////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// - bool IGFD::FileInfos::IsTagFound(const std::string& vTag) const - { - if (!vTag.empty()) - { - if (fileNameExt_optimized == "..") return true; + bool IGFD::FileInfos::IsTagFound(const std::string& vTag) const + { + if (!vTag.empty()) + { + if (fileNameExt_optimized == "..") return true; - return - fileNameExt_optimized.find(vTag) != std::string::npos || // first try wihtout case and accents - fileNameExt.find(vTag) != std::string::npos; // second if searched with case and accents - } + return + fileNameExt_optimized.find(vTag) != std::string::npos || // first try wihtout case and accents + fileNameExt.find(vTag) != std::string::npos; // second if searched with case and accents + } - // if tag is empty => its a special case but all is found - return true; - } + // if tag is empty => its a special case but all is found + return true; + } - ///////////////////////////////////////////////////////////////////////////////////// - //// SEARCH MANAGER ///////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + //// SEARCH MANAGER ///////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// - void IGFD::SearchManager::Clear() - { - puSearchTag.clear(); - IGFD::Utils::ResetBuffer(puSearchBuffer); - } + void IGFD::SearchManager::Clear() + { + puSearchTag.clear(); + IGFD::Utils::ResetBuffer(puSearchBuffer); + } - void IGFD::SearchManager::DrawSearchBar(FileDialogInternal& vFileDialogInternal) - { - // search field - if (IMGUI_BUTTON(resetButtonString "##BtnImGuiFileDialogSearchField")) - { - Clear(); - vFileDialogInternal.puFileManager.ApplyFilteringOnFileList(vFileDialogInternal); - } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip(buttonResetSearchString); - ImGui::SameLine(); - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - bool edited = ImGui::InputTextWithHint("##InputImGuiFileDialogSearchField", searchString, puSearchBuffer, MAX_FILE_DIALOG_NAME_BUFFER); - if (ImGui::GetItemID() == ImGui::GetActiveID()) - puSearchInputIsActive = true; - ImGui::PopItemWidth(); - if (edited) - { - puSearchTag = puSearchBuffer; - vFileDialogInternal.puFileManager.ApplyFilteringOnFileList(vFileDialogInternal); - } - } + void IGFD::SearchManager::DrawSearchBar(FileDialogInternal& vFileDialogInternal) + { + // search field + if (IMGUI_BUTTON(resetButtonString "##BtnImGuiFileDialogSearchField")) + { + Clear(); + vFileDialogInternal.puFileManager.ApplyFilteringOnFileList(vFileDialogInternal); + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip(buttonResetSearchString); + ImGui::SameLine(); + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + bool edited = ImGui::InputTextWithHint("##InputImGuiFileDialogSearchField", searchString, puSearchBuffer, MAX_FILE_DIALOG_NAME_BUFFER); + if (ImGui::GetItemID() == ImGui::GetActiveID()) + puSearchInputIsActive = true; + ImGui::PopItemWidth(); + if (edited) + { + puSearchTag = puSearchBuffer; + vFileDialogInternal.puFileManager.ApplyFilteringOnFileList(vFileDialogInternal); + } + } - ///////////////////////////////////////////////////////////////////////////////////// - //// FILTER INFOS /////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + //// FILTER INFOS /////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// - void IGFD::FilterManager::FilterInfos::clear() - { - filter.clear(); - collectionfilters.clear(); - } + void IGFD::FilterManager::FilterInfos::clear() + { + filter.clear(); + collectionfilters.clear(); + } - bool IGFD::FilterManager::FilterInfos::empty() const - { - return filter.empty() && collectionfilters.empty(); - } + bool IGFD::FilterManager::FilterInfos::empty() const + { + return filter.empty() && collectionfilters.empty(); + } - bool IGFD::FilterManager::FilterInfos::exist(const std::string& vFilter) const - { - return filter == vFilter || (collectionfilters.find(vFilter) != collectionfilters.end()); - } + bool IGFD::FilterManager::FilterInfos::exist(const std::string& vFilter) const + { + return filter == vFilter || (collectionfilters.find(vFilter) != collectionfilters.end()); + } - ///////////////////////////////////////////////////////////////////////////////////// - //// FILTER MANAGER ///////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + //// FILTER MANAGER ///////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// - void IGFD::FilterManager::ParseFilters(const char* vFilters) - { - prParsedFilters.clear(); + void IGFD::FilterManager::ParseFilters(const char* vFilters) + { + prParsedFilters.clear(); - if (vFilters) - puDLGFilters = vFilters; // file mode - else - puDLGFilters.clear(); // directory mode + if (vFilters) + puDLGFilters = vFilters; // file mode + else + puDLGFilters.clear(); // directory mode - if (!puDLGFilters.empty()) - { - // ".*,.cpp,.h,.hpp" - // "Source files{.cpp,.h,.hpp},Image files{.png,.gif,.jpg,.jpeg},.md" + if (!puDLGFilters.empty()) + { + // ".*,.cpp,.h,.hpp" + // "Source files{.cpp,.h,.hpp},Image files{.png,.gif,.jpg,.jpeg},.md" - bool currentFilterFound = false; + bool currentFilterFound = false; - size_t nan = std::string::npos; - size_t p = 0, lp = 0; - while ((p = puDLGFilters.find_first_of("{,", p)) != nan) - { - FilterInfos infos; + size_t nan = std::string::npos; + size_t p = 0, lp = 0; + while ((p = puDLGFilters.find_first_of("{,", p)) != nan) + { + FilterInfos infos; - if (puDLGFilters[p] == '{') // { - { - infos.filter = puDLGFilters.substr(lp, p - lp); - p++; - lp = puDLGFilters.find('}', p); - if (lp != nan) - { - std::string fs = puDLGFilters.substr(p, lp - p); - auto arr = IGFD::Utils::SplitStringToVector(fs, ',', false); - for (auto a : arr) - { - infos.collectionfilters.emplace(a); - } - } - p = lp + 1; - } - else // , - { - infos.filter = puDLGFilters.substr(lp, p - lp); - p++; - } + if (puDLGFilters[p] == '{') // { + { + infos.filter = puDLGFilters.substr(lp, p - lp); + p++; + lp = puDLGFilters.find('}', p); + if (lp != nan) + { + std::string fs = puDLGFilters.substr(p, lp - p); + auto arr = IGFD::Utils::SplitStringToVector(fs, ',', false); + for (auto a : arr) + { + infos.collectionfilters.emplace(a); + } + } + p = lp + 1; + } + else // , + { + infos.filter = puDLGFilters.substr(lp, p - lp); + p++; + } - if (!currentFilterFound && prSelectedFilter.filter == infos.filter) - { - currentFilterFound = true; - prSelectedFilter = infos; - } + if (!currentFilterFound && prSelectedFilter.filter == infos.filter) + { + currentFilterFound = true; + prSelectedFilter = infos; + } - lp = p; - if (!infos.empty()) - prParsedFilters.emplace_back(infos); - } + lp = p; + if (!infos.empty()) + prParsedFilters.emplace_back(infos); + } - std::string token = puDLGFilters.substr(lp); - if (!token.empty()) - { - FilterInfos infos; - infos.filter = std::move(token); - prParsedFilters.emplace_back(infos); - } + std::string token = puDLGFilters.substr(lp); + if (!token.empty()) + { + FilterInfos infos; + infos.filter = std::move(token); + prParsedFilters.emplace_back(infos); + } - if (!currentFilterFound) - if (!prParsedFilters.empty()) - prSelectedFilter = *prParsedFilters.begin(); - } - } + if (!currentFilterFound) + if (!prParsedFilters.empty()) + prSelectedFilter = *prParsedFilters.begin(); + } + } - void IGFD::FilterManager::SetSelectedFilterWithExt(const std::string& vFilter) - { - if (!prParsedFilters.empty()) - { - if (!vFilter.empty()) - { - // std::map - for (const auto& infos : prParsedFilters) - { - if (vFilter == infos.filter) - { - prSelectedFilter = infos; - } - else - { - // maybe this ext is in an extention so we will - // explore the collections is they are existing - for (const auto& filter : infos.collectionfilters) - { - if (vFilter == filter) - { - prSelectedFilter = infos; - } - } - } - } - } + void IGFD::FilterManager::SetSelectedFilterWithExt(const std::string& vFilter) + { + if (!prParsedFilters.empty()) + { + if (!vFilter.empty()) + { + // std::map + for (const auto& infos : prParsedFilters) + { + if (vFilter == infos.filter) + { + prSelectedFilter = infos; + } + else + { + // maybe this ext is in an extention so we will + // explore the collections is they are existing + for (const auto& filter : infos.collectionfilters) + { + if (vFilter == filter) + { + prSelectedFilter = infos; + } + } + } + } + } - if (prSelectedFilter.empty()) - prSelectedFilter = *prParsedFilters.begin(); - } - } + if (prSelectedFilter.empty()) + prSelectedFilter = *prParsedFilters.begin(); + } + } - void IGFD::FilterManager::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const FileStyle& vInfos) - { - std::string _criteria; - if (vCriteria) - _criteria = std::string(vCriteria); - prFilesStyle[vFlags][_criteria] = std::make_shared(vInfos); - prFilesStyle[vFlags][_criteria]->flags = vFlags; - } + void IGFD::FilterManager::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const FileStyle& vInfos) + { + std::string _criteria; + if (vCriteria) + _criteria = std::string(vCriteria); + prFilesStyle[vFlags][_criteria] = std::make_shared(vInfos); + prFilesStyle[vFlags][_criteria]->flags = vFlags; + } - // will be called internally - // will not been exposed to IGFD API - bool IGFD::FilterManager::prFillFileStyle(std::shared_ptr vFileInfos) const - { - if (vFileInfos.use_count() && !prFilesStyle.empty()) - { - for (const auto& _flag : prFilesStyle) - { - for (const auto& _file : _flag.second) - { - if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd') - { - if (_file.first.empty()) // for all dirs - { - vFileInfos->fileStyle = _file.second; - } - else if (_file.first == vFileInfos->fileNameExt) // for dirs who are equal to style criteria - { - vFileInfos->fileStyle = _file.second; - } - } - else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f') - { - if (_file.first.empty()) // for all files - { - vFileInfos->fileStyle = _file.second; - } - else if (_file.first == vFileInfos->fileNameExt) // for files who are equal to style criteria - { - vFileInfos->fileStyle = _file.second; - } - } - else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l') - { - if (_file.first.empty()) // for all links - { - vFileInfos->fileStyle = _file.second; - } - else if (_file.first == vFileInfos->fileNameExt) // for links who are equal to style criteria - { - vFileInfos->fileStyle = _file.second; - } - } + // will be called internally + // will not been exposed to IGFD API + bool IGFD::FilterManager::prFillFileStyle(std::shared_ptr vFileInfos) const + { + if (vFileInfos.use_count() && !prFilesStyle.empty()) + { + for (const auto& _flag : prFilesStyle) + { + for (const auto& _file : _flag.second) + { + if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd') + { + if (_file.first.empty()) // for all dirs + { + vFileInfos->fileStyle = _file.second; + } + else if (_file.first == vFileInfos->fileNameExt) // for dirs who are equal to style criteria + { + vFileInfos->fileStyle = _file.second; + } + } + else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f') + { + if (_file.first.empty()) // for all files + { + vFileInfos->fileStyle = _file.second; + } + else if (_file.first == vFileInfos->fileNameExt) // for files who are equal to style criteria + { + vFileInfos->fileStyle = _file.second; + } + } + else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l') + { + if (_file.first.empty()) // for all links + { + vFileInfos->fileStyle = _file.second; + } + else if (_file.first == vFileInfos->fileNameExt) // for links who are equal to style criteria + { + vFileInfos->fileStyle = _file.second; + } + } - if (_flag.first & IGFD_FileStyleByExtention) - { - if (_file.first == vFileInfos->fileExt) - { - vFileInfos->fileStyle = _file.second; - } + if (_flag.first & IGFD_FileStyleByExtention) + { + if (_file.first == vFileInfos->fileExt) + { + vFileInfos->fileStyle = _file.second; + } - // can make sense for some dirs like the hidden by ex ".git" - if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd') - { - if (_file.first == vFileInfos->fileExt) - { - vFileInfos->fileStyle = _file.second; - } - } - else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f') - { - if (_file.first == vFileInfos->fileExt) - { - vFileInfos->fileStyle = _file.second; - } - } - else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l') - { - if (_file.first == vFileInfos->fileExt) - { - vFileInfos->fileStyle = _file.second; - } - } - } - if (_flag.first & IGFD_FileStyleByFullName) - { - if (_file.first == vFileInfos->fileNameExt) - { - vFileInfos->fileStyle = _file.second; - } + // can make sense for some dirs like the hidden by ex ".git" + if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd') + { + if (_file.first == vFileInfos->fileExt) + { + vFileInfos->fileStyle = _file.second; + } + } + else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f') + { + if (_file.first == vFileInfos->fileExt) + { + vFileInfos->fileStyle = _file.second; + } + } + else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l') + { + if (_file.first == vFileInfos->fileExt) + { + vFileInfos->fileStyle = _file.second; + } + } + } + if (_flag.first & IGFD_FileStyleByFullName) + { + if (_file.first == vFileInfos->fileNameExt) + { + vFileInfos->fileStyle = _file.second; + } - if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd') - { - if (_file.first == vFileInfos->fileNameExt) - { - vFileInfos->fileStyle = _file.second; - } - } - else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f') - { - if (_file.first == vFileInfos->fileNameExt) - { - vFileInfos->fileStyle = _file.second; - } - } - else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l') - { - if (_file.first == vFileInfos->fileNameExt) - { - vFileInfos->fileStyle = _file.second; - } - } - } - if (_flag.first & IGFD_FileStyleByContainedInFullName) - { - if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos) - { - vFileInfos->fileStyle = _file.second; - } + if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd') + { + if (_file.first == vFileInfos->fileNameExt) + { + vFileInfos->fileStyle = _file.second; + } + } + else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f') + { + if (_file.first == vFileInfos->fileNameExt) + { + vFileInfos->fileStyle = _file.second; + } + } + else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l') + { + if (_file.first == vFileInfos->fileNameExt) + { + vFileInfos->fileStyle = _file.second; + } + } + } + if (_flag.first & IGFD_FileStyleByContainedInFullName) + { + if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos) + { + vFileInfos->fileStyle = _file.second; + } - if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd') - { - if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos) - { - vFileInfos->fileStyle = _file.second; - } - } - else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f') - { - if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos) - { - vFileInfos->fileStyle = _file.second; - } - } - else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l') - { - if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos) - { - vFileInfos->fileStyle = _file.second; - } - } - } + if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd') + { + if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos) + { + vFileInfos->fileStyle = _file.second; + } + } + else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f') + { + if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos) + { + vFileInfos->fileStyle = _file.second; + } + } + else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l') + { + if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos) + { + vFileInfos->fileStyle = _file.second; + } + } + } - if (vFileInfos->fileStyle.use_count()) - return true; - } - } - } + if (vFileInfos->fileStyle.use_count()) + return true; + } + } + } - return false; - } + return false; + } - void IGFD::FilterManager::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const ImVec4& vColor, const std::string& vIcon, ImFont* vFont) - { - std::string _criteria; - if (vCriteria) - _criteria = std::string(vCriteria); - prFilesStyle[vFlags][_criteria] = std::make_shared(vColor, vIcon, vFont); - prFilesStyle[vFlags][_criteria]->flags = vFlags; - } + void IGFD::FilterManager::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const ImVec4& vColor, const std::string& vIcon, ImFont* vFont) + { + std::string _criteria; + if (vCriteria) + _criteria = std::string(vCriteria); + prFilesStyle[vFlags][_criteria] = std::make_shared(vColor, vIcon, vFont); + prFilesStyle[vFlags][_criteria]->flags = vFlags; + } - // todo : to refactor this fucking function - bool IGFD::FilterManager::GetFileStyle(const IGFD_FileStyleFlags& vFlags, const std::string& vCriteria, ImVec4* vOutColor, std::string* vOutIcon, ImFont **vOutFont) - { - if (vOutColor) - { - if (!prFilesStyle.empty()) - { - if (prFilesStyle.find(vFlags) != prFilesStyle.end()) // found - { - if (vFlags & IGFD_FileStyleByContainedInFullName) - { - // search for vCriteria who are containing the criteria - for (const auto& _file : prFilesStyle.at(vFlags)) - { - if (vCriteria.find(_file.first) != std::string::npos) - { - if (_file.second.use_count()) - { - *vOutColor = _file.second->color; - if (vOutIcon) - *vOutIcon = _file.second->icon; - if (vOutFont) - *vOutFont = _file.second->font; - return true; - } - } - } - } - else - { - if (prFilesStyle.at(vFlags).find(vCriteria) != prFilesStyle.at(vFlags).end()) // found - { - *vOutColor = prFilesStyle[vFlags][vCriteria]->color; - if (vOutIcon) - *vOutIcon = prFilesStyle[vFlags][vCriteria]->icon; - if (vOutFont) - *vOutFont = prFilesStyle[vFlags][vCriteria]->font; - return true; - } - } - } - else - { - // search for flag composition - for (const auto& _flag : prFilesStyle) - { - if (_flag.first & vFlags) - { - if (_flag.first & IGFD_FileStyleByContainedInFullName) - { - // search for vCriteria who are containing the criteria - for (const auto& _file : prFilesStyle.at(_flag.first)) - { - if (vCriteria.find(_file.first) != std::string::npos) - { - if (_file.second.use_count()) - { - *vOutColor = _file.second->color; - if (vOutIcon) - *vOutIcon = _file.second->icon; - if (vOutFont) - *vOutFont = _file.second->font; - return true; - } - } - } - } - else - { - if (prFilesStyle.at(_flag.first).find(vCriteria) != prFilesStyle.at(_flag.first).end()) // found - { - *vOutColor = prFilesStyle[_flag.first][vCriteria]->color; - if (vOutIcon) - *vOutIcon = prFilesStyle[_flag.first][vCriteria]->icon; - if (vOutFont) - *vOutFont = prFilesStyle[_flag.first][vCriteria]->font; - return true; - } - } - } - } - } - } - } - return false; - } + // todo : to refactor this fucking function + bool IGFD::FilterManager::GetFileStyle(const IGFD_FileStyleFlags& vFlags, const std::string& vCriteria, ImVec4* vOutColor, std::string* vOutIcon, ImFont **vOutFont) + { + if (vOutColor) + { + if (!prFilesStyle.empty()) + { + if (prFilesStyle.find(vFlags) != prFilesStyle.end()) // found + { + if (vFlags & IGFD_FileStyleByContainedInFullName) + { + // search for vCriteria who are containing the criteria + for (const auto& _file : prFilesStyle.at(vFlags)) + { + if (vCriteria.find(_file.first) != std::string::npos) + { + if (_file.second.use_count()) + { + *vOutColor = _file.second->color; + if (vOutIcon) + *vOutIcon = _file.second->icon; + if (vOutFont) + *vOutFont = _file.second->font; + return true; + } + } + } + } + else + { + if (prFilesStyle.at(vFlags).find(vCriteria) != prFilesStyle.at(vFlags).end()) // found + { + *vOutColor = prFilesStyle[vFlags][vCriteria]->color; + if (vOutIcon) + *vOutIcon = prFilesStyle[vFlags][vCriteria]->icon; + if (vOutFont) + *vOutFont = prFilesStyle[vFlags][vCriteria]->font; + return true; + } + } + } + else + { + // search for flag composition + for (const auto& _flag : prFilesStyle) + { + if (_flag.first & vFlags) + { + if (_flag.first & IGFD_FileStyleByContainedInFullName) + { + // search for vCriteria who are containing the criteria + for (const auto& _file : prFilesStyle.at(_flag.first)) + { + if (vCriteria.find(_file.first) != std::string::npos) + { + if (_file.second.use_count()) + { + *vOutColor = _file.second->color; + if (vOutIcon) + *vOutIcon = _file.second->icon; + if (vOutFont) + *vOutFont = _file.second->font; + return true; + } + } + } + } + else + { + if (prFilesStyle.at(_flag.first).find(vCriteria) != prFilesStyle.at(_flag.first).end()) // found + { + *vOutColor = prFilesStyle[_flag.first][vCriteria]->color; + if (vOutIcon) + *vOutIcon = prFilesStyle[_flag.first][vCriteria]->icon; + if (vOutFont) + *vOutFont = prFilesStyle[_flag.first][vCriteria]->font; + return true; + } + } + } + } + } + } + } + return false; + } - void IGFD::FilterManager::ClearFilesStyle() - { - prFilesStyle.clear(); - } - - bool IGFD::FilterManager::IsCoveredByFilters(const std::string& vTag) const - { - if (!puDLGFilters.empty() && !prSelectedFilter.empty()) - { - // check if current file extention is covered by current filter - // we do that here, for avoid doing that during filelist display - // for better fps - if (prSelectedFilter.exist(vTag) || prSelectedFilter.filter == ".*") - { - return true; - } - } + void IGFD::FilterManager::ClearFilesStyle() + { + prFilesStyle.clear(); + } + + bool IGFD::FilterManager::IsCoveredByFilters(const std::string& vTag) const + { + if (!puDLGFilters.empty() && !prSelectedFilter.empty()) + { + // check if current file extention is covered by current filter + // we do that here, for avoid doing that during filelist display + // for better fps + if (prSelectedFilter.exist(vTag) || prSelectedFilter.filter == ".*") + { + return true; + } + } - return false; - } + return false; + } - bool IGFD::FilterManager::DrawFilterComboBox(FileDialogInternal& vFileDialogInternal) - { - // combobox of filters - if (!puDLGFilters.empty()) - { - ImGui::SameLine(); + bool IGFD::FilterManager::DrawFilterComboBox(FileDialogInternal& vFileDialogInternal) + { + // combobox of filters + if (!puDLGFilters.empty()) + { + ImGui::SameLine(); - bool needToApllyNewFilter = false; + bool needToApllyNewFilter = false; - ImGui::PushItemWidth(FILTER_COMBO_WIDTH*FileDialog::Instance()->DpiScale); - if (ImGui::BeginCombo("##Filters", prSelectedFilter.filter.c_str(), ImGuiComboFlags_None)) - { - intptr_t i = 0; - for (const auto& filter : prParsedFilters) - { - const bool item_selected = (filter.filter == prSelectedFilter.filter); - ImGui::PushID((void*)(intptr_t)i++); - if (ImGui::Selectable(filter.filter.c_str(), item_selected)) - { - prSelectedFilter = filter; - needToApllyNewFilter = true; - } - ImGui::PopID(); - } + ImGui::PushItemWidth(FILTER_COMBO_WIDTH*FileDialog::Instance()->DpiScale); + if (ImGui::BeginCombo("##Filters", prSelectedFilter.filter.c_str(), ImGuiComboFlags_None)) + { + intptr_t i = 0; + for (const auto& filter : prParsedFilters) + { + const bool item_selected = (filter.filter == prSelectedFilter.filter); + ImGui::PushID((void*)(intptr_t)i++); + if (ImGui::Selectable(filter.filter.c_str(), item_selected)) + { + prSelectedFilter = filter; + needToApllyNewFilter = true; + } + ImGui::PopID(); + } - ImGui::EndCombo(); - } - ImGui::PopItemWidth(); + ImGui::EndCombo(); + } + ImGui::PopItemWidth(); - if (needToApllyNewFilter) - { - vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal); - } + if (needToApllyNewFilter) + { + vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal); + } - return needToApllyNewFilter; - } + return needToApllyNewFilter; + } - return false; - } + return false; + } - IGFD::FilterManager::FilterInfos IGFD::FilterManager::GetSelectedFilter() - { - return prSelectedFilter; - } + IGFD::FilterManager::FilterInfos IGFD::FilterManager::GetSelectedFilter() + { + return prSelectedFilter; + } - std::string IGFD::FilterManager::ReplaceExtentionWithCurrentFilter(const std::string& vFile) const - { - auto result = vFile; + std::string IGFD::FilterManager::ReplaceExtentionWithCurrentFilter(const std::string& vFile) const + { + auto result = vFile; - if (!result.empty()) - { - // if not a collection we can replace the filter by the extention we want - if (prSelectedFilter.collectionfilters.empty()) - { - size_t lastPoint = vFile.find_last_of('.'); - if (lastPoint != std::string::npos) - { - result = result.substr(0, lastPoint); - } + if (!result.empty()) + { + // if not a collection we can replace the filter by the extention we want + if (prSelectedFilter.collectionfilters.empty()) + { + size_t lastPoint = vFile.find_last_of('.'); + if (lastPoint != std::string::npos) + { + result = result.substr(0, lastPoint); + } - result += prSelectedFilter.filter; - } - } + result += prSelectedFilter.filter; + } + } - return result; - } - - void IGFD::FilterManager::SetDefaultFilterIfNotDefined() - { - if (prSelectedFilter.empty() && // no filter selected - !prParsedFilters.empty()) // filter exist - prSelectedFilter = *prParsedFilters.begin(); // we take the first filter - } + return result; + } + + void IGFD::FilterManager::SetDefaultFilterIfNotDefined() + { + if (prSelectedFilter.empty() && // no filter selected + !prParsedFilters.empty()) // filter exist + prSelectedFilter = *prParsedFilters.begin(); // we take the first filter + } - ///////////////////////////////////////////////////////////////////////////////////// - //// FILE MANAGER /////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + //// FILE MANAGER /////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// - IGFD::FileManager::FileManager() - { - puFsRoot = std::string(1u, PATH_SEP); + IGFD::FileManager::FileManager() + { + puFsRoot = std::string(1u, PATH_SEP); for (int i=0; i<4; i++) { puSortingDirection[i]=true; } - } + } - void IGFD::FileManager::OpenCurrentPath(const FileDialogInternal& vFileDialogInternal) - { - puShowDrives = false; - ClearComposer(); - ClearFileLists(); - if (puDLGDirectoryMode) // directory mode - SetDefaultFileName("."); - else - SetDefaultFileName(""); + void IGFD::FileManager::OpenCurrentPath(const FileDialogInternal& vFileDialogInternal) + { + puShowDrives = false; + ClearComposer(); + ClearFileLists(); + if (puDLGDirectoryMode) // directory mode + SetDefaultFileName("."); + else + SetDefaultFileName(""); logV("IGFD: OpenCurrentPath()"); - ScanDir(vFileDialogInternal, GetCurrentPath()); - } + ScanDir(vFileDialogInternal, GetCurrentPath()); + } - void IGFD::FileManager::SortFields(const FileDialogInternal& vFileDialogInternal, const SortingFieldEnum& vSortingField, const bool vCanChangeOrder) - { + void IGFD::FileManager::SortFields(const FileDialogInternal& vFileDialogInternal, const SortingFieldEnum& vSortingField, const bool vCanChangeOrder) + { logV("IGFD: SortFields()"); - if (vSortingField != SortingFieldEnum::FIELD_NONE) - { - puHeaderFileName = tableHeaderFileNameString; - puHeaderFileType = tableHeaderFileTypeString; - puHeaderFileSize = tableHeaderFileSizeString; - puHeaderFileDate = tableHeaderFileDateString; + if (vSortingField != SortingFieldEnum::FIELD_NONE) + { + puHeaderFileName = tableHeaderFileNameString; + puHeaderFileType = tableHeaderFileTypeString; + puHeaderFileSize = tableHeaderFileSizeString; + puHeaderFileDate = tableHeaderFileDateString; #ifdef USE_THUMBNAILS - puHeaderFileThumbnails = tableHeaderFileThumbnailsString; + puHeaderFileThumbnails = tableHeaderFileThumbnailsString; #endif // #ifdef USE_THUMBNAILS - } else { + } else { logV("IGFD: sorting by NONE!"); } @@ -1175,2790 +1175,2790 @@ namespace IGFD logV("IGFD: with an empty file list?"); } - if (vSortingField == SortingFieldEnum::FIELD_FILENAME) - { + if (vSortingField == SortingFieldEnum::FIELD_FILENAME) + { logV("IGFD: sorting by name"); - if (vCanChangeOrder && puSortingField == vSortingField) { + if (vCanChangeOrder && puSortingField == vSortingField) { //printf("Change the sorting\n"); - puSortingDirection[0] = !puSortingDirection[0]; + puSortingDirection[0] = !puSortingDirection[0]; } - if (puSortingDirection[0]) - { + if (puSortingDirection[0]) + { #ifdef USE_CUSTOM_SORTING_ICON - puHeaderFileName = tableHeaderDescendingIcon + puHeaderFileName; + puHeaderFileName = tableHeaderDescendingIcon + puHeaderFileName; #endif // USE_CUSTOM_SORTING_ICON - std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool - { + std::sort(prFileList.begin(), prFileList.end(), + [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + { if (a==NULL || b==NULL) return false; - if (!a.use_count() || !b.use_count()) - return false; + if (!a.use_count() || !b.use_count()) + return false; - // this code fail in c:\\Users with the link "All users". got a invalid comparator - /* - // use code from https://github.com/jackm97/ImGuiFileDialog/commit/bf40515f5a1de3043e60562dc1a494ee7ecd3571 - // strict ordering for file/directory types beginning in '.' - // common on Linux platforms - if (a->fileNameExt[0] == '.' && b->fileNameExt[0] != '.') - return false; - if (a->fileNameExt[0] != '.' && b->fileNameExt[0] == '.') - return true; - if (a->fileNameExt[0] == '.' && b->fileNameExt[0] == '.') - { - return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) < 0); // sort in insensitive case - } - */ - if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first - return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) < 0); // sort in insensitive case - }); - } - else - { + // this code fail in c:\\Users with the link "All users". got a invalid comparator + /* + // use code from https://github.com/jackm97/ImGuiFileDialog/commit/bf40515f5a1de3043e60562dc1a494ee7ecd3571 + // strict ordering for file/directory types beginning in '.' + // common on Linux platforms + if (a->fileNameExt[0] == '.' && b->fileNameExt[0] != '.') + return false; + if (a->fileNameExt[0] != '.' && b->fileNameExt[0] == '.') + return true; + if (a->fileNameExt[0] == '.' && b->fileNameExt[0] == '.') + { + return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) < 0); // sort in insensitive case + } + */ + if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first + return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) < 0); // sort in insensitive case + }); + } + else + { #ifdef USE_CUSTOM_SORTING_ICON - puHeaderFileName = tableHeaderAscendingIcon + puHeaderFileName; + puHeaderFileName = tableHeaderAscendingIcon + puHeaderFileName; #endif // USE_CUSTOM_SORTING_ICON - std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool - { + std::sort(prFileList.begin(), prFileList.end(), + [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + { if (a==NULL || b==NULL) return false; - if (!a.use_count() || !b.use_count()) - return false; + if (!a.use_count() || !b.use_count()) + return false; - // this code fail in c:\\Users with the link "All users". got a invalid comparator - /* - // use code from https://github.com/jackm97/ImGuiFileDialog/commit/bf40515f5a1de3043e60562dc1a494ee7ecd3571 - // strict ordering for file/directory types beginning in '.' - // common on Linux platforms - if (a->fileNameExt[0] == '.' && b->fileNameExt[0] != '.') - return false; - if (a->fileNameExt[0] != '.' && b->fileNameExt[0] == '.') - return true; - if (a->fileNameExt[0] == '.' && b->fileNameExt[0] == '.') - { - return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) > 0); // sort in insensitive case - } - */ - return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) > 0); // sort in insensitive case - }); - } - } - else if (vSortingField == SortingFieldEnum::FIELD_TYPE) - { + // this code fail in c:\\Users with the link "All users". got a invalid comparator + /* + // use code from https://github.com/jackm97/ImGuiFileDialog/commit/bf40515f5a1de3043e60562dc1a494ee7ecd3571 + // strict ordering for file/directory types beginning in '.' + // common on Linux platforms + if (a->fileNameExt[0] == '.' && b->fileNameExt[0] != '.') + return false; + if (a->fileNameExt[0] != '.' && b->fileNameExt[0] == '.') + return true; + if (a->fileNameExt[0] == '.' && b->fileNameExt[0] == '.') + { + return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) > 0); // sort in insensitive case + } + */ + return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) > 0); // sort in insensitive case + }); + } + } + else if (vSortingField == SortingFieldEnum::FIELD_TYPE) + { logV("IGFD: sorting by type"); - if (vCanChangeOrder && puSortingField == vSortingField) - puSortingDirection[1] = !puSortingDirection[1]; + if (vCanChangeOrder && puSortingField == vSortingField) + puSortingDirection[1] = !puSortingDirection[1]; - if (puSortingDirection[1]) - { + if (puSortingDirection[1]) + { #ifdef USE_CUSTOM_SORTING_ICON - puHeaderFileType = tableHeaderDescendingIcon + puHeaderFileType; + puHeaderFileType = tableHeaderDescendingIcon + puHeaderFileType; #endif // USE_CUSTOM_SORTING_ICON - std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool - { - if (!a.use_count() || !b.use_count()) - return false; + std::sort(prFileList.begin(), prFileList.end(), + [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + { + if (!a.use_count() || !b.use_count()) + return false; - if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first - return (a->fileExt < b->fileExt); // else - }); - } - else - { + if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first + return (a->fileExt < b->fileExt); // else + }); + } + else + { #ifdef USE_CUSTOM_SORTING_ICON - puHeaderFileType = tableHeaderAscendingIcon + puHeaderFileType; + puHeaderFileType = tableHeaderAscendingIcon + puHeaderFileType; #endif // USE_CUSTOM_SORTING_ICON - std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool - { + std::sort(prFileList.begin(), prFileList.end(), + [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + { if (a==NULL || b==NULL) return false; - if (!a.use_count() || !b.use_count()) - return false; + if (!a.use_count() || !b.use_count()) + return false; - if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last - return (a->fileExt > b->fileExt); // else - }); - } - } - else if (vSortingField == SortingFieldEnum::FIELD_SIZE) - { + if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last + return (a->fileExt > b->fileExt); // else + }); + } + } + else if (vSortingField == SortingFieldEnum::FIELD_SIZE) + { logV("IGFD: sorting by size"); - if (vCanChangeOrder && puSortingField == vSortingField) - puSortingDirection[2] = !puSortingDirection[2]; + if (vCanChangeOrder && puSortingField == vSortingField) + puSortingDirection[2] = !puSortingDirection[2]; - if (puSortingDirection[2]) - { + if (puSortingDirection[2]) + { #ifdef USE_CUSTOM_SORTING_ICON - puHeaderFileSize = tableHeaderDescendingIcon + puHeaderFileSize; + puHeaderFileSize = tableHeaderDescendingIcon + puHeaderFileSize; #endif // USE_CUSTOM_SORTING_ICON - std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool - { + std::sort(prFileList.begin(), prFileList.end(), + [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + { if (a==NULL || b==NULL) return false; - if (!a.use_count() || !b.use_count()) - return false; + if (!a.use_count() || !b.use_count()) + return false; - if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first - return (a->fileSize < b->fileSize); // else - }); - } - else - { + if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first + return (a->fileSize < b->fileSize); // else + }); + } + else + { #ifdef USE_CUSTOM_SORTING_ICON - puHeaderFileSize = tableHeaderAscendingIcon + puHeaderFileSize; + puHeaderFileSize = tableHeaderAscendingIcon + puHeaderFileSize; #endif // USE_CUSTOM_SORTING_ICON - std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool - { + std::sort(prFileList.begin(), prFileList.end(), + [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + { if (a==NULL || b==NULL) return false; - if (!a.use_count() || !b.use_count()) - return false; + if (!a.use_count() || !b.use_count()) + return false; - if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last - return (a->fileSize > b->fileSize); // else - }); - } - } - else if (vSortingField == SortingFieldEnum::FIELD_DATE) - { + if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last + return (a->fileSize > b->fileSize); // else + }); + } + } + else if (vSortingField == SortingFieldEnum::FIELD_DATE) + { logV("IGFD: sorting by date"); - if (vCanChangeOrder && puSortingField == vSortingField) - puSortingDirection[3] = !puSortingDirection[3]; + if (vCanChangeOrder && puSortingField == vSortingField) + puSortingDirection[3] = !puSortingDirection[3]; - if (puSortingDirection[3]) - { + if (puSortingDirection[3]) + { #ifdef USE_CUSTOM_SORTING_ICON - puHeaderFileDate = tableHeaderDescendingIcon + puHeaderFileDate; + puHeaderFileDate = tableHeaderDescendingIcon + puHeaderFileDate; #endif // USE_CUSTOM_SORTING_ICON - std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool - { + std::sort(prFileList.begin(), prFileList.end(), + [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + { if (a==NULL || b==NULL) return false; - if (!a.use_count() || !b.use_count()) - return false; + if (!a.use_count() || !b.use_count()) + return false; - if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first - return (a->fileModifDate < b->fileModifDate); // else - }); - } - else - { + if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first + return (a->fileModifDate < b->fileModifDate); // else + }); + } + else + { #ifdef USE_CUSTOM_SORTING_ICON - puHeaderFileDate = tableHeaderAscendingIcon + puHeaderFileDate; + puHeaderFileDate = tableHeaderAscendingIcon + puHeaderFileDate; #endif // USE_CUSTOM_SORTING_ICON - std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool - { + std::sort(prFileList.begin(), prFileList.end(), + [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + { if (a==NULL || b==NULL) return false; - if (!a.use_count() || !b.use_count()) - return false; + if (!a.use_count() || !b.use_count()) + return false; - if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last - return (a->fileModifDate > b->fileModifDate); // else - }); - } - } + if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last + return (a->fileModifDate > b->fileModifDate); // else + }); + } + } #ifdef USE_THUMBNAILS - else if (vSortingField == SortingFieldEnum::FIELD_THUMBNAILS) - { - if (vCanChangeOrder && puSortingField == vSortingField) - puSortingDirection[4] = !puSortingDirection[4]; + else if (vSortingField == SortingFieldEnum::FIELD_THUMBNAILS) + { + if (vCanChangeOrder && puSortingField == vSortingField) + puSortingDirection[4] = !puSortingDirection[4]; - // we will compare thumbnails by : - // 1) width - // 2) height + // we will compare thumbnails by : + // 1) width + // 2) height - if (puSortingDirection[4]) - { + if (puSortingDirection[4]) + { #ifdef USE_CUSTOM_SORTING_ICON - puHeaderFileThumbnails = tableHeaderDescendingIcon + puHeaderFileThumbnails; + puHeaderFileThumbnails = tableHeaderDescendingIcon + puHeaderFileThumbnails; #endif // USE_CUSTOM_SORTING_ICON - std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool - { - if (!a.use_count() || !b.use_count()) - return false; + std::sort(prFileList.begin(), prFileList.end(), + [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + { + if (!a.use_count() || !b.use_count()) + return false; - if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first - if (a->thumbnailInfo.textureWidth == b->thumbnailInfo.textureWidth) - return (a->thumbnailInfo.textureHeight < b->thumbnailInfo.textureHeight); - return (a->thumbnailInfo.textureWidth < b->thumbnailInfo.textureWidth); - }); - } + if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first + if (a->thumbnailInfo.textureWidth == b->thumbnailInfo.textureWidth) + return (a->thumbnailInfo.textureHeight < b->thumbnailInfo.textureHeight); + return (a->thumbnailInfo.textureWidth < b->thumbnailInfo.textureWidth); + }); + } - else - { + else + { #ifdef USE_CUSTOM_SORTING_ICON - puHeaderFileThumbnails = tableHeaderAscendingIcon + puHeaderFileThumbnails; + puHeaderFileThumbnails = tableHeaderAscendingIcon + puHeaderFileThumbnails; #endif // USE_CUSTOM_SORTING_ICON - std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool - { - if (!a.use_count() || !b.use_count()) - return false; + std::sort(prFileList.begin(), prFileList.end(), + [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + { + if (!a.use_count() || !b.use_count()) + return false; - if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last - if (a->thumbnailInfo.textureWidth == b->thumbnailInfo.textureWidth) - return (a->thumbnailInfo.textureHeight > b->thumbnailInfo.textureHeight); - return (a->thumbnailInfo.textureWidth > b->thumbnailInfo.textureWidth); - }); - } - } + if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last + if (a->thumbnailInfo.textureWidth == b->thumbnailInfo.textureWidth) + return (a->thumbnailInfo.textureHeight > b->thumbnailInfo.textureHeight); + return (a->thumbnailInfo.textureWidth > b->thumbnailInfo.textureWidth); + }); + } + } #endif // USE_THUMBNAILS - if (vSortingField != SortingFieldEnum::FIELD_NONE) - { - puSortingField = vSortingField; - } + if (vSortingField != SortingFieldEnum::FIELD_NONE) + { + puSortingField = vSortingField; + } logV("IGFD: applying filtering on file list"); - ApplyFilteringOnFileList(vFileDialogInternal); - } + ApplyFilteringOnFileList(vFileDialogInternal); + } - void IGFD::FileManager::ClearFileLists() - { - prFilteredFileList.clear(); - prFileList.clear(); - } + void IGFD::FileManager::ClearFileLists() + { + prFilteredFileList.clear(); + prFileList.clear(); + } - std::string IGFD::FileManager::prOptimizeFilenameForSearchOperations(const std::string& vFileNameExt) - { - auto fileNameExt = vFileNameExt; - // convert to lower case - for (char& c : fileNameExt) - c = (char)std::tolower(c); - return fileNameExt; - } + std::string IGFD::FileManager::prOptimizeFilenameForSearchOperations(const std::string& vFileNameExt) + { + auto fileNameExt = vFileNameExt; + // convert to lower case + for (char& c : fileNameExt) + c = (char)std::tolower(c); + return fileNameExt; + } - void IGFD::FileManager::AddFile(const FileDialogInternal& vFileDialogInternal, const std::string& vPath, const std::string& vFileName, const char& vFileType) - { - auto infos = std::make_shared(); + void IGFD::FileManager::AddFile(const FileDialogInternal& vFileDialogInternal, const std::string& vPath, const std::string& vFileName, const char& vFileType) + { + auto infos = std::make_shared(); - infos->filePath = vPath; - infos->fileNameExt = vFileName; - infos->fileNameExt_optimized = prOptimizeFilenameForSearchOperations(infos->fileNameExt); - infos->fileType = vFileType; + infos->filePath = vPath; + infos->fileNameExt = vFileName; + infos->fileNameExt_optimized = prOptimizeFilenameForSearchOperations(infos->fileNameExt); + infos->fileType = vFileType; - if (infos->fileNameExt.empty() || ((infos->fileNameExt == "." || infos->fileNameExt == "..") && !vFileDialogInternal.puFilterManager.puDLGFilters.empty())) return; // filename empty or filename is the current dir '.' //-V807 - if (infos->fileNameExt != ".." && (vFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DontShowHiddenFiles) && infos->fileNameExt[0] == '.') // dont show hidden files - if (!vFileDialogInternal.puFilterManager.puDLGFilters.empty() || (vFileDialogInternal.puFilterManager.puDLGFilters.empty() && infos->fileNameExt != ".")) // except "." if in directory mode //-V728 - return; + if (infos->fileNameExt.empty() || ((infos->fileNameExt == "." || infos->fileNameExt == "..") && !vFileDialogInternal.puFilterManager.puDLGFilters.empty())) return; // filename empty or filename is the current dir '.' //-V807 + if (infos->fileNameExt != ".." && (vFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DontShowHiddenFiles) && infos->fileNameExt[0] == '.') // dont show hidden files + if (!vFileDialogInternal.puFilterManager.puDLGFilters.empty() || (vFileDialogInternal.puFilterManager.puDLGFilters.empty() && infos->fileNameExt != ".")) // except "." if in directory mode //-V728 + return; - if (infos->fileType == 'f' || - infos->fileType == 'l') // link can have the same extention of a file - { - size_t lpt = infos->fileNameExt.find_last_of('.'); - if (lpt != std::string::npos) - { - infos->fileExt = infos->fileNameExt.substr(lpt); - } + if (infos->fileType == 'f' || + infos->fileType == 'l') // link can have the same extention of a file + { + size_t lpt = infos->fileNameExt.find_last_of('.'); + if (lpt != std::string::npos) + { + infos->fileExt = infos->fileNameExt.substr(lpt); + } for (char& i: infos->fileExt) { if (i>='A' && i<='Z') i+='a'-'A'; } - if (!vFileDialogInternal.puFilterManager.IsCoveredByFilters(infos->fileExt)) - { - return; - } - } + if (!vFileDialogInternal.puFilterManager.IsCoveredByFilters(infos->fileExt)) + { + return; + } + } - vFileDialogInternal.puFilterManager.prFillFileStyle(infos); + vFileDialogInternal.puFilterManager.prFillFileStyle(infos); - prCompleteFileInfos(infos); - prFileList.push_back(infos); - } + prCompleteFileInfos(infos); + prFileList.push_back(infos); + } - void IGFD::FileManager::ScanDir(const FileDialogInternal& vFileDialogInternal, const std::string& vPath) - { - std::string path = vPath; + void IGFD::FileManager::ScanDir(const FileDialogInternal& vFileDialogInternal, const std::string& vPath) + { + std::string path = vPath; logV("IGFD: ScanDir(%s)",vPath); - if (prCurrentPathDecomposition.empty()) - { + if (prCurrentPathDecomposition.empty()) + { logV("IGFD: the current path decomposition is empty. setting."); - SetCurrentDir(path); - } + SetCurrentDir(path); + } - if (!prCurrentPathDecomposition.empty()) - { + if (!prCurrentPathDecomposition.empty()) + { logV("IGFD: the current path decomposition is not empty. trying."); #ifdef WIN32 - if (path == puFsRoot) - path += std::string(1u, PATH_SEP); + if (path == puFsRoot) + path += std::string(1u, PATH_SEP); #endif // WIN32 - ClearFileLists(); + ClearFileLists(); - struct dirent** files = nullptr; - int n = scandir(path.c_str(), &files, nullptr, inAlphaSort); + struct dirent** files = nullptr; + int n = scandir(path.c_str(), &files, nullptr, inAlphaSort); logV("IGFD: %d entries in directory",n); - if (n>0) - { - int i; + if (n>0) + { + int i; - for (i = 0; i < n; i++) - { - struct dirent* ent = files[i]; - std::string where = path + std::string("/") + std::string(ent->d_name); - char fileType = 0; + for (i = 0; i < n; i++) + { + struct dirent* ent = files[i]; + std::string where = path + std::string("/") + std::string(ent->d_name); + char fileType = 0; #ifdef HAVE_DIRENT_TYPE - if (ent->d_type != DT_UNKNOWN) - { - switch (ent->d_type) - { - case DT_REG: - fileType = 'f'; break; - case DT_DIR: - fileType = 'd'; break; - case DT_LNK: - DIR* dirTest = opendir(where.c_str()); - if (dirTest == NULL) - { - if (errno == ENOTDIR) - { - fileType = 'f'; - } - else - { - fileType = 'l'; - } - } - else - { - fileType = 'd'; - closedir(dirTest); - } - break; - } - } - else + if (ent->d_type != DT_UNKNOWN) + { + switch (ent->d_type) + { + case DT_REG: + fileType = 'f'; break; + case DT_DIR: + fileType = 'd'; break; + case DT_LNK: + DIR* dirTest = opendir(where.c_str()); + if (dirTest == NULL) + { + if (errno == ENOTDIR) + { + fileType = 'f'; + } + else + { + fileType = 'l'; + } + } + else + { + fileType = 'd'; + closedir(dirTest); + } + break; + } + } + else #endif // HAVE_DIRENT_TYPE - { - struct stat filestat; - if (stat(where.c_str(), &filestat) == 0) - { - if (S_ISDIR(filestat.st_mode)) - { - fileType = 'd'; - } - else - { - fileType = 'f'; - } - } - } + { + struct stat filestat; + if (stat(where.c_str(), &filestat) == 0) + { + if (S_ISDIR(filestat.st_mode)) + { + fileType = 'd'; + } + else + { + fileType = 'f'; + } + } + } - auto fileNameExt = ent->d_name; + auto fileNameExt = ent->d_name; - AddFile(vFileDialogInternal, path, fileNameExt, fileType); - } + AddFile(vFileDialogInternal, path, fileNameExt, fileType); + } - for (i = 0; i < n; i++) - { - free(files[i]); - } + for (i = 0; i < n; i++) + { + free(files[i]); + } - free(files); - } else { + free(files); + } else { logV("IGFD: it's empty"); } logV("IGFD: sorting fields..."); - SortFields(vFileDialogInternal, puSortingField, false); - } else { + SortFields(vFileDialogInternal, puSortingField, false); + } else { logE("IGFD: current path decomposition is empty!"); } fileListActuallyEmpty=prFileList.empty(); - } + } - bool IGFD::FileManager::GetDrives() - { - auto drives = IGFD::Utils::GetDrivesList(); - if (!drives.empty()) - { - prCurrentPath.clear(); - prCurrentPathDecomposition.clear(); - ClearFileLists(); - for (auto& drive : drives) - { - auto info = std::make_shared(); - info->fileNameExt = drive; - info->fileNameExt_optimized = prOptimizeFilenameForSearchOperations(drive); - info->fileType = 'd'; + bool IGFD::FileManager::GetDrives() + { + auto drives = IGFD::Utils::GetDrivesList(); + if (!drives.empty()) + { + prCurrentPath.clear(); + prCurrentPathDecomposition.clear(); + ClearFileLists(); + for (auto& drive : drives) + { + auto info = std::make_shared(); + info->fileNameExt = drive; + info->fileNameExt_optimized = prOptimizeFilenameForSearchOperations(drive); + info->fileType = 'd'; - if (!info->fileNameExt.empty()) - { - prFileList.push_back(info); - } - } - puShowDrives = true; - return true; - } - return false; - } + if (!info->fileNameExt.empty()) + { + prFileList.push_back(info); + } + } + puShowDrives = true; + return true; + } + return false; + } - bool IGFD::FileManager::IsComposerEmpty() - { - return prCurrentPathDecomposition.empty(); - } - - size_t IGFD::FileManager::GetComposerSize() - { - return prCurrentPathDecomposition.size(); - } + bool IGFD::FileManager::IsComposerEmpty() + { + return prCurrentPathDecomposition.empty(); + } + + size_t IGFD::FileManager::GetComposerSize() + { + return prCurrentPathDecomposition.size(); + } - bool IGFD::FileManager::IsFileListEmpty() - { - return prFileList.empty(); - } + bool IGFD::FileManager::IsFileListEmpty() + { + return prFileList.empty(); + } - size_t IGFD::FileManager::GetFullFileListSize() - { - return prFileList.size(); - } + size_t IGFD::FileManager::GetFullFileListSize() + { + return prFileList.size(); + } - std::shared_ptr IGFD::FileManager::GetFullFileAt(size_t vIdx) - { - if (vIdx < prFileList.size()) - return prFileList[vIdx]; - return nullptr; - } + std::shared_ptr IGFD::FileManager::GetFullFileAt(size_t vIdx) + { + if (vIdx < prFileList.size()) + return prFileList[vIdx]; + return nullptr; + } - bool IGFD::FileManager::IsFilteredListEmpty() - { - return prFilteredFileList.empty(); - } + bool IGFD::FileManager::IsFilteredListEmpty() + { + return prFilteredFileList.empty(); + } - size_t IGFD::FileManager::GetFilteredListSize() - { - return prFilteredFileList.size(); - } + size_t IGFD::FileManager::GetFilteredListSize() + { + return prFilteredFileList.size(); + } - std::shared_ptr IGFD::FileManager::GetFilteredFileAt(size_t vIdx) - { - if (vIdx < prFilteredFileList.size()) - return prFilteredFileList[vIdx]; - return nullptr; - } + std::shared_ptr IGFD::FileManager::GetFilteredFileAt(size_t vIdx) + { + if (vIdx < prFilteredFileList.size()) + return prFilteredFileList[vIdx]; + return nullptr; + } - bool IGFD::FileManager::IsFileNameSelected(const std::string& vFileName) - { - return prSelectedFileNames.find(vFileName) != prSelectedFileNames.end(); - } + bool IGFD::FileManager::IsFileNameSelected(const std::string& vFileName) + { + return prSelectedFileNames.find(vFileName) != prSelectedFileNames.end(); + } - std::string IGFD::FileManager::GetBack() - { - return prCurrentPathDecomposition.back(); - } + std::string IGFD::FileManager::GetBack() + { + return prCurrentPathDecomposition.back(); + } - void IGFD::FileManager::ClearComposer() - { - prCurrentPathDecomposition.clear(); - } + void IGFD::FileManager::ClearComposer() + { + prCurrentPathDecomposition.clear(); + } - void IGFD::FileManager::ClearAll() - { - ClearComposer(); - ClearFileLists(); - } + void IGFD::FileManager::ClearAll() + { + ClearComposer(); + ClearFileLists(); + } - void IGFD::FileManager::ApplyFilteringOnFileList(const FileDialogInternal& vFileDialogInternal) - { - prFilteredFileList.clear(); - for (const auto& file : prFileList) - { - if (!file.use_count()) - continue; - bool show = true; - if (!file->IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag)) // if search tag - show = false; - if (puDLGDirectoryMode && file->fileType != 'd') // directory mode - show = false; - if (show) - prFilteredFileList.push_back(file); - } - } + void IGFD::FileManager::ApplyFilteringOnFileList(const FileDialogInternal& vFileDialogInternal) + { + prFilteredFileList.clear(); + for (const auto& file : prFileList) + { + if (!file.use_count()) + continue; + bool show = true; + if (!file->IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag)) // if search tag + show = false; + if (puDLGDirectoryMode && file->fileType != 'd') // directory mode + show = false; + if (show) + prFilteredFileList.push_back(file); + } + } - std::string IGFD::FileManager::prRoundNumber(double vvalue, int n) - { - std::stringstream tmp; - tmp << std::setprecision(n) << std::fixed << vvalue; - return tmp.str(); - } + std::string IGFD::FileManager::prRoundNumber(double vvalue, int n) + { + std::stringstream tmp; + tmp << std::setprecision(n) << std::fixed << vvalue; + return tmp.str(); + } - std::string IGFD::FileManager::prFormatFileSize(size_t vByteSize) - { - if (vByteSize != 0) - { - static double lo = 1024.0; - static double ko = 1024.0 * 1024.0; - static double mo = 1024.0 * 1024.0 * 1024.0; + std::string IGFD::FileManager::prFormatFileSize(size_t vByteSize) + { + if (vByteSize != 0) + { + static double lo = 1024.0; + static double ko = 1024.0 * 1024.0; + static double mo = 1024.0 * 1024.0 * 1024.0; - auto v = (double)vByteSize; + auto v = (double)vByteSize; - if (v < lo) - return prRoundNumber(v, 0); // octet - else if (v < ko) - return prRoundNumber(v / lo, 2) + "K"; // ko - else if (v < mo) - return prRoundNumber(v / ko, 2) + "M"; // Mo - else - return prRoundNumber(v / mo, 2) + "G"; // Go - } + if (v < lo) + return prRoundNumber(v, 0); // octet + else if (v < ko) + return prRoundNumber(v / lo, 2) + "K"; // ko + else if (v < mo) + return prRoundNumber(v / ko, 2) + "M"; // Mo + else + return prRoundNumber(v / mo, 2) + "G"; // Go + } - return ""; - } + return ""; + } - void IGFD::FileManager::prCompleteFileInfos(const std::shared_ptr& vInfos) - { - if (!vInfos.use_count()) - return; + void IGFD::FileManager::prCompleteFileInfos(const std::shared_ptr& vInfos) + { + if (!vInfos.use_count()) + return; - if (vInfos->fileNameExt != "." && - vInfos->fileNameExt != "..") - { - // _stat struct : - //dev_t st_dev; /* ID of device containing file */ - //ino_t st_ino; /* inode number */ - //mode_t st_mode; /* protection */ - //nlink_t st_nlink; /* number of hard links */ - //uid_t st_uid; /* user ID of owner */ - //gid_t st_gid; /* group ID of owner */ - //dev_t st_rdev; /* device ID (if special file) */ - //off_t st_size; /* total size, in bytes */ - //blksize_t st_blksize; /* blocksize for file system I/O */ - //blkcnt_t st_blocks; /* number of 512B blocks allocated */ - //time_t st_atime; /* time of last access - not sure out of ntfs */ - //time_t st_mtime; /* time of last modification - not sure out of ntfs */ - //time_t st_ctime; /* time of last status change - not sure out of ntfs */ + if (vInfos->fileNameExt != "." && + vInfos->fileNameExt != "..") + { + // _stat struct : + //dev_t st_dev; /* ID of device containing file */ + //ino_t st_ino; /* inode number */ + //mode_t st_mode; /* protection */ + //nlink_t st_nlink; /* number of hard links */ + //uid_t st_uid; /* user ID of owner */ + //gid_t st_gid; /* group ID of owner */ + //dev_t st_rdev; /* device ID (if special file) */ + //off_t st_size; /* total size, in bytes */ + //blksize_t st_blksize; /* blocksize for file system I/O */ + //blkcnt_t st_blocks; /* number of 512B blocks allocated */ + //time_t st_atime; /* time of last access - not sure out of ntfs */ + //time_t st_mtime; /* time of last modification - not sure out of ntfs */ + //time_t st_ctime; /* time of last status change - not sure out of ntfs */ - std::string fpn; + std::string fpn; - if (vInfos->fileType == 'f' || vInfos->fileType == 'l' || vInfos->fileType == 'd') // file - fpn = vInfos->filePath + std::string(1u, PATH_SEP) + vInfos->fileNameExt; + if (vInfos->fileType == 'f' || vInfos->fileType == 'l' || vInfos->fileType == 'd') // file + fpn = vInfos->filePath + std::string(1u, PATH_SEP) + vInfos->fileNameExt; - struct stat statInfos = {}; - char timebuf[100]; - int result = stat(fpn.c_str(), &statInfos); - if (result!=-1) - { - if (vInfos->fileType != 'd') - { - vInfos->fileSize = (size_t)statInfos.st_size; - vInfos->formatedFileSize = prFormatFileSize(vInfos->fileSize); - } + struct stat statInfos = {}; + char timebuf[100]; + int result = stat(fpn.c_str(), &statInfos); + if (result!=-1) + { + if (vInfos->fileType != 'd') + { + vInfos->fileSize = (size_t)statInfos.st_size; + vInfos->formatedFileSize = prFormatFileSize(vInfos->fileSize); + } - size_t len = 0; + size_t len = 0; #ifdef MSVC - struct tm _tm; - errno_t err = localtime_s(&_tm, &statInfos.st_mtime); - if (!err) len = strftime(timebuf, 99, DateTimeFormat, &_tm); + struct tm _tm; + errno_t err = localtime_s(&_tm, &statInfos.st_mtime); + if (!err) len = strftime(timebuf, 99, DateTimeFormat, &_tm); #else // MSVC - struct tm* _tm = localtime(&statInfos.st_mtime); - if (_tm) len = strftime(timebuf, 99, DateTimeFormat, _tm); + struct tm* _tm = localtime(&statInfos.st_mtime); + if (_tm) len = strftime(timebuf, 99, DateTimeFormat, _tm); #endif // MSVC - if (len) - { - vInfos->fileModifDate = std::string(timebuf, len); - } - } else { + if (len) + { + vInfos->fileModifDate = std::string(timebuf, len); + } + } else { vInfos->fileSize=0; vInfos->formatedFileSize = prFormatFileSize(vInfos->fileSize); vInfos->fileModifDate="???"; } - } - } + } + } - void IGFD::FileManager::prRemoveFileNameInSelection(const std::string& vFileName) - { - prSelectedFileNames.erase(vFileName); + void IGFD::FileManager::prRemoveFileNameInSelection(const std::string& vFileName) + { + prSelectedFileNames.erase(vFileName); - if (prSelectedFileNames.size() == 1) - { - snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%s", vFileName.c_str()); - } - else - { - snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%zu files Selected", prSelectedFileNames.size()); - } - } - - void IGFD::FileManager::prAddFileNameInSelection(const std::string& vFileName, bool vSetLastSelectionFileName) - { - prSelectedFileNames.emplace(vFileName); + if (prSelectedFileNames.size() == 1) + { + snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%s", vFileName.c_str()); + } + else + { + snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%zu files Selected", prSelectedFileNames.size()); + } + } + + void IGFD::FileManager::prAddFileNameInSelection(const std::string& vFileName, bool vSetLastSelectionFileName) + { + prSelectedFileNames.emplace(vFileName); - if (prSelectedFileNames.size() == 1) - { - snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%s", vFileName.c_str()); - } - else - { - snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%zu files Selected", prSelectedFileNames.size()); - } + if (prSelectedFileNames.size() == 1) + { + snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%s", vFileName.c_str()); + } + else + { + snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%zu files Selected", prSelectedFileNames.size()); + } - if (vSetLastSelectionFileName) - prLastSelectedFileName = vFileName; - } + if (vSetLastSelectionFileName) + prLastSelectedFileName = vFileName; + } - void IGFD::FileManager::SetCurrentDir(const std::string& vPath) - { - std::string path = vPath; + void IGFD::FileManager::SetCurrentDir(const std::string& vPath) + { + std::string path = vPath; #ifdef WIN32 - if (puFsRoot == path) - path += std::string(1u, PATH_SEP); + if (puFsRoot == path) + path += std::string(1u, PATH_SEP); #endif // WIN32 - - DIR* dir = opendir(path.c_str()); - if (dir == nullptr) - { - path = "."; - dir = opendir(path.c_str()); - } + + DIR* dir = opendir(path.c_str()); + if (dir == nullptr) + { + path = "."; + dir = opendir(path.c_str()); + } - if (dir != nullptr) - { + if (dir != nullptr) + { #ifdef WIN32 - DWORD numchar = 0; - // numchar = GetFullPathNameA(path.c_str(), PATH_MAX, real_path, nullptr); - std::wstring wpath = IGFD::Utils::string_to_wstring(path); - numchar = GetFullPathNameW(wpath.c_str(), 0, nullptr, nullptr); - std::wstring fpath(numchar, 0); - GetFullPathNameW(wpath.c_str(), numchar, (wchar_t*)fpath.data(), nullptr); - std::string real_path = IGFD::Utils::wstring_to_string(fpath); - if (real_path.back() == '\0') // for fix issue we can have with std::string concatenation.. if there is a \0 at end - real_path = real_path.substr(0, real_path.size() - 1U); - if (!real_path.empty()) + DWORD numchar = 0; + // numchar = GetFullPathNameA(path.c_str(), PATH_MAX, real_path, nullptr); + std::wstring wpath = IGFD::Utils::string_to_wstring(path); + numchar = GetFullPathNameW(wpath.c_str(), 0, nullptr, nullptr); + std::wstring fpath(numchar, 0); + GetFullPathNameW(wpath.c_str(), numchar, (wchar_t*)fpath.data(), nullptr); + std::string real_path = IGFD::Utils::wstring_to_string(fpath); + if (real_path.back() == '\0') // for fix issue we can have with std::string concatenation.. if there is a \0 at end + real_path = real_path.substr(0, real_path.size() - 1U); + if (!real_path.empty()) #elif defined(UNIX) // UNIX is LINUX or APPLE - char real_path[PATH_MAX]; - char* numchar = realpath(path.c_str(), real_path); - if (numchar != nullptr) + char real_path[PATH_MAX]; + char* numchar = realpath(path.c_str(), real_path); + if (numchar != nullptr) #endif // WIN32 - { - prCurrentPath = std::move(real_path); - if (prCurrentPath[prCurrentPath.size() - 1] == PATH_SEP) - { - prCurrentPath = prCurrentPath.substr(0, prCurrentPath.size() - 1); - } - IGFD::Utils::SetBuffer(puInputPathBuffer, MAX_PATH_BUFFER_SIZE, prCurrentPath); - prCurrentPathDecomposition = IGFD::Utils::SplitStringToVector(prCurrentPath, PATH_SEP, false); + { + prCurrentPath = std::move(real_path); + if (prCurrentPath[prCurrentPath.size() - 1] == PATH_SEP) + { + prCurrentPath = prCurrentPath.substr(0, prCurrentPath.size() - 1); + } + IGFD::Utils::SetBuffer(puInputPathBuffer, MAX_PATH_BUFFER_SIZE, prCurrentPath); + prCurrentPathDecomposition = IGFD::Utils::SplitStringToVector(prCurrentPath, PATH_SEP, false); #ifdef UNIX // UNIX is LINUX or APPLE - prCurrentPathDecomposition.insert(prCurrentPathDecomposition.begin(), std::string(1u, PATH_SEP)); + prCurrentPathDecomposition.insert(prCurrentPathDecomposition.begin(), std::string(1u, PATH_SEP)); #endif // UNIX - if (!prCurrentPathDecomposition.empty()) - { + if (!prCurrentPathDecomposition.empty()) + { #ifdef WIN32 - puFsRoot = prCurrentPathDecomposition[0]; + puFsRoot = prCurrentPathDecomposition[0]; #endif // WIN32 - } - } - closedir(dir); - } - } + } + } + closedir(dir); + } + } - bool IGFD::FileManager::CreateDir(const std::string& vPath) - { - bool res = false; + bool IGFD::FileManager::CreateDir(const std::string& vPath) + { + bool res = false; - if (!vPath.empty()) - { - std::string path = prCurrentPath + std::string(1u, PATH_SEP) + vPath; + if (!vPath.empty()) + { + std::string path = prCurrentPath + std::string(1u, PATH_SEP) + vPath; - res = IGFD::Utils::CreateDirectoryIfNotExist(path); - } + res = IGFD::Utils::CreateDirectoryIfNotExist(path); + } - return res; - } + return res; + } - void IGFD::FileManager::ComposeNewPath(std::vector::iterator vIter) - { - std::string res; + void IGFD::FileManager::ComposeNewPath(std::vector::iterator vIter) + { + std::string res; - while (true) - { - if (!res.empty()) - { + while (true) + { + if (!res.empty()) + { #ifdef WIN32 - res = *vIter + std::string(1u, PATH_SEP) + res; + res = *vIter + std::string(1u, PATH_SEP) + res; #elif defined(UNIX) // UNIX is LINUX or APPLE - if (*vIter == puFsRoot) - res = *vIter + res; - else - res = *vIter + PATH_SEP + res; + if (*vIter == puFsRoot) + res = *vIter + res; + else + res = *vIter + PATH_SEP + res; #endif // WIN32 - } - else - res = *vIter; + } + else + res = *vIter; - if (vIter == prCurrentPathDecomposition.begin()) - { + if (vIter == prCurrentPathDecomposition.begin()) + { #if defined(UNIX) // UNIX is LINUX or APPLE - if (res[0] != PATH_SEP) - res = PATH_SEP + res; + if (res[0] != PATH_SEP) + res = PATH_SEP + res; #endif // defined(UNIX) - break; - } + break; + } - --vIter; - } + --vIter; + } - prCurrentPath = std::move(res); - } + prCurrentPath = std::move(res); + } - bool IGFD::FileManager::SetPathOnParentDirectoryIfAny() - { - if (prCurrentPathDecomposition.size() > 1) - { - ComposeNewPath(prCurrentPathDecomposition.end() - 2); - return true; - } - return false; - } + bool IGFD::FileManager::SetPathOnParentDirectoryIfAny() + { + if (prCurrentPathDecomposition.size() > 1) + { + ComposeNewPath(prCurrentPathDecomposition.end() - 2); + return true; + } + return false; + } - std::string IGFD::FileManager::GetCurrentPath() - { - if (prCurrentPath.empty()) - prCurrentPath = "."; - return prCurrentPath; - } + std::string IGFD::FileManager::GetCurrentPath() + { + if (prCurrentPath.empty()) + prCurrentPath = "."; + return prCurrentPath; + } - void IGFD::FileManager::SetCurrentPath(const std::string& vCurrentPath) - { - if (vCurrentPath.empty()) - prCurrentPath = "."; - else - prCurrentPath = vCurrentPath; - } + void IGFD::FileManager::SetCurrentPath(const std::string& vCurrentPath) + { + if (vCurrentPath.empty()) + prCurrentPath = "."; + else + prCurrentPath = vCurrentPath; + } - bool IGFD::FileManager::IsFileExist(const std::string& vFile) - { - std::ifstream docFile(vFile, std::ios::in); - if (docFile.is_open()) - { - docFile.close(); - return true; - } - return false; - } + bool IGFD::FileManager::IsFileExist(const std::string& vFile) + { + std::ifstream docFile(vFile, std::ios::in); + if (docFile.is_open()) + { + docFile.close(); + return true; + } + return false; + } - void IGFD::FileManager::SetDefaultFileName(const std::string& vFileName) - { - puDLGDefaultFileName = vFileName; - IGFD::Utils::SetBuffer(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFileName); - } + void IGFD::FileManager::SetDefaultFileName(const std::string& vFileName) + { + puDLGDefaultFileName = vFileName; + IGFD::Utils::SetBuffer(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFileName); + } - bool IGFD::FileManager::SelectDirectory(const std::shared_ptr& vInfos) - { - if (!vInfos.use_count()) - return false; + bool IGFD::FileManager::SelectDirectory(const std::shared_ptr& vInfos) + { + if (!vInfos.use_count()) + return false; - bool pathClick = false; + bool pathClick = false; - if (vInfos->fileNameExt == "..") - { - pathClick = SetPathOnParentDirectoryIfAny(); - } - else - { - std::string newPath; + if (vInfos->fileNameExt == "..") + { + pathClick = SetPathOnParentDirectoryIfAny(); + } + else + { + std::string newPath; - if (puShowDrives) - { - newPath = vInfos->fileNameExt + std::string(1u, PATH_SEP); - } - else - { + if (puShowDrives) + { + newPath = vInfos->fileNameExt + std::string(1u, PATH_SEP); + } + else + { #ifdef __linux__ - if (puFsRoot == prCurrentPath) - newPath = prCurrentPath + vInfos->fileNameExt; - else + if (puFsRoot == prCurrentPath) + newPath = prCurrentPath + vInfos->fileNameExt; + else #endif // __linux__ - newPath = prCurrentPath + std::string(1u, PATH_SEP) + vInfos->fileNameExt; - } + newPath = prCurrentPath + std::string(1u, PATH_SEP) + vInfos->fileNameExt; + } - if (IGFD::Utils::IsDirectoryExist(newPath)) - { - if (puShowDrives) - { - prCurrentPath = vInfos->fileNameExt; - puFsRoot = prCurrentPath; - } - else - { - prCurrentPath = newPath; //-V820 - } - pathClick = true; - } - } + if (IGFD::Utils::IsDirectoryExist(newPath)) + { + if (puShowDrives) + { + prCurrentPath = vInfos->fileNameExt; + puFsRoot = prCurrentPath; + } + else + { + prCurrentPath = newPath; //-V820 + } + pathClick = true; + } + } - return pathClick; - } + return pathClick; + } - void IGFD::FileManager::SelectFileName(const FileDialogInternal& vFileDialogInternal, const std::shared_ptr& vInfos) - { - if (!vInfos.use_count()) - return; + void IGFD::FileManager::SelectFileName(const FileDialogInternal& vFileDialogInternal, const std::shared_ptr& vInfos) + { + if (!vInfos.use_count()) + return; - if (ImGui::GetIO().KeyCtrl) - { - if (puDLGcountSelectionMax == 0) // infinite selection - { - if (prSelectedFileNames.find(vInfos->fileNameExt) == prSelectedFileNames.end()) // not found +> add - { - prAddFileNameInSelection(vInfos->fileNameExt, true); - } - else // found +> remove - { - prRemoveFileNameInSelection(vInfos->fileNameExt); - } - } - else // selection limited by size - { - if (prSelectedFileNames.size() < puDLGcountSelectionMax) - { - if (prSelectedFileNames.find(vInfos->fileNameExt) == prSelectedFileNames.end()) // not found +> add - { - prAddFileNameInSelection(vInfos->fileNameExt, true); - } - else // found +> remove - { - prRemoveFileNameInSelection(vInfos->fileNameExt); - } - } - } - } - else if (ImGui::GetIO().KeyShift) - { - if (puDLGcountSelectionMax != 1) - { - prSelectedFileNames.clear(); - // we will iterate filelist and get the last selection after the start selection - bool startMultiSelection = false; - std::string fileNameToSelect = vInfos->fileNameExt; - std::string savedLastSelectedFileName; // for invert selection mode - for (const auto& file : prFileList) - { - if (!file.use_count()) - continue; + if (ImGui::GetIO().KeyCtrl) + { + if (puDLGcountSelectionMax == 0) // infinite selection + { + if (prSelectedFileNames.find(vInfos->fileNameExt) == prSelectedFileNames.end()) // not found +> add + { + prAddFileNameInSelection(vInfos->fileNameExt, true); + } + else // found +> remove + { + prRemoveFileNameInSelection(vInfos->fileNameExt); + } + } + else // selection limited by size + { + if (prSelectedFileNames.size() < puDLGcountSelectionMax) + { + if (prSelectedFileNames.find(vInfos->fileNameExt) == prSelectedFileNames.end()) // not found +> add + { + prAddFileNameInSelection(vInfos->fileNameExt, true); + } + else // found +> remove + { + prRemoveFileNameInSelection(vInfos->fileNameExt); + } + } + } + } + else if (ImGui::GetIO().KeyShift) + { + if (puDLGcountSelectionMax != 1) + { + prSelectedFileNames.clear(); + // we will iterate filelist and get the last selection after the start selection + bool startMultiSelection = false; + std::string fileNameToSelect = vInfos->fileNameExt; + std::string savedLastSelectedFileName; // for invert selection mode + for (const auto& file : prFileList) + { + if (!file.use_count()) + continue; - bool canTake = true; - if (!file->IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag)) canTake = false; - if (canTake) // if not filtered, we will take files who are filtered by the dialog - { - if (file->fileNameExt == prLastSelectedFileName) - { - startMultiSelection = true; - prAddFileNameInSelection(prLastSelectedFileName, false); - } - else if (startMultiSelection) - { - if (puDLGcountSelectionMax == 0) // infinite selection - { - prAddFileNameInSelection(file->fileNameExt, false); - } - else // selection limited by size - { - if (prSelectedFileNames.size() < puDLGcountSelectionMax) - { - prAddFileNameInSelection(file->fileNameExt, false); - } - else - { - startMultiSelection = false; - if (!savedLastSelectedFileName.empty()) - prLastSelectedFileName = savedLastSelectedFileName; - break; - } - } - } + bool canTake = true; + if (!file->IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag)) canTake = false; + if (canTake) // if not filtered, we will take files who are filtered by the dialog + { + if (file->fileNameExt == prLastSelectedFileName) + { + startMultiSelection = true; + prAddFileNameInSelection(prLastSelectedFileName, false); + } + else if (startMultiSelection) + { + if (puDLGcountSelectionMax == 0) // infinite selection + { + prAddFileNameInSelection(file->fileNameExt, false); + } + else // selection limited by size + { + if (prSelectedFileNames.size() < puDLGcountSelectionMax) + { + prAddFileNameInSelection(file->fileNameExt, false); + } + else + { + startMultiSelection = false; + if (!savedLastSelectedFileName.empty()) + prLastSelectedFileName = savedLastSelectedFileName; + break; + } + } + } - if (file->fileNameExt == fileNameToSelect) - { - if (!startMultiSelection) // we are before the last Selected FileName, so we must inverse - { - savedLastSelectedFileName = prLastSelectedFileName; - prLastSelectedFileName = fileNameToSelect; - fileNameToSelect = savedLastSelectedFileName; - startMultiSelection = true; - prAddFileNameInSelection(prLastSelectedFileName, false); - } - else - { - startMultiSelection = false; - if (!savedLastSelectedFileName.empty()) - prLastSelectedFileName = savedLastSelectedFileName; - break; - } - } - } - } - } - } - else - { - prSelectedFileNames.clear(); - IGFD::Utils::ResetBuffer(puFileNameBuffer); - prAddFileNameInSelection(vInfos->fileNameExt, true); - } - } + if (file->fileNameExt == fileNameToSelect) + { + if (!startMultiSelection) // we are before the last Selected FileName, so we must inverse + { + savedLastSelectedFileName = prLastSelectedFileName; + prLastSelectedFileName = fileNameToSelect; + fileNameToSelect = savedLastSelectedFileName; + startMultiSelection = true; + prAddFileNameInSelection(prLastSelectedFileName, false); + } + else + { + startMultiSelection = false; + if (!savedLastSelectedFileName.empty()) + prLastSelectedFileName = savedLastSelectedFileName; + break; + } + } + } + } + } + } + else + { + prSelectedFileNames.clear(); + IGFD::Utils::ResetBuffer(puFileNameBuffer); + prAddFileNameInSelection(vInfos->fileNameExt, true); + } + } - void IGFD::FileManager::DrawDirectoryCreation(const FileDialogInternal& vFileDialogInternal) - { - if (vFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DisableCreateDirectoryButton) - return; + void IGFD::FileManager::DrawDirectoryCreation(const FileDialogInternal& vFileDialogInternal) + { + if (vFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DisableCreateDirectoryButton) + return; - if (IMGUI_BUTTON(createDirButtonString)) - { - if (!prCreateDirectoryMode) - { - prCreateDirectoryMode = true; - IGFD::Utils::ResetBuffer(puDirectoryNameBuffer); - } - } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip(buttonCreateDirString); + if (IMGUI_BUTTON(createDirButtonString)) + { + if (!prCreateDirectoryMode) + { + prCreateDirectoryMode = true; + IGFD::Utils::ResetBuffer(puDirectoryNameBuffer); + } + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip(buttonCreateDirString); - if (prCreateDirectoryMode) - { - ImGui::SameLine(); + if (prCreateDirectoryMode) + { + ImGui::SameLine(); - ImGui::PushItemWidth(100.0f); - ImGui::InputText("##DirectoryFileName", puDirectoryNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER); - ImGui::PopItemWidth(); + ImGui::PushItemWidth(100.0f); + ImGui::InputText("##DirectoryFileName", puDirectoryNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER); + ImGui::PopItemWidth(); - ImGui::SameLine(); + ImGui::SameLine(); - if (IMGUI_BUTTON(okButtonString)) - { - std::string newDir = std::string(puDirectoryNameBuffer); - if (CreateDir(newDir)) - { - SetCurrentPath(prCurrentPath + std::string(1u, PATH_SEP) + newDir); - OpenCurrentPath(vFileDialogInternal); - } + if (IMGUI_BUTTON(okButtonString)) + { + std::string newDir = std::string(puDirectoryNameBuffer); + if (CreateDir(newDir)) + { + SetCurrentPath(prCurrentPath + std::string(1u, PATH_SEP) + newDir); + OpenCurrentPath(vFileDialogInternal); + } - prCreateDirectoryMode = false; - } + prCreateDirectoryMode = false; + } - ImGui::SameLine(); + ImGui::SameLine(); - if (IMGUI_BUTTON(cancelButtonString)) - { - prCreateDirectoryMode = false; - } - } - } + if (IMGUI_BUTTON(cancelButtonString)) + { + prCreateDirectoryMode = false; + } + } + } - void IGFD::FileManager::DrawPathComposer(const FileDialogInternal& vFileDialogInternal) - { - if (IMGUI_BUTTON(homeButtonString)) - { - SetCurrentPath(FileDialog::Instance()->homePath); - OpenCurrentPath(vFileDialogInternal); - } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip(buttonResetPathString); + void IGFD::FileManager::DrawPathComposer(const FileDialogInternal& vFileDialogInternal) + { + if (IMGUI_BUTTON(homeButtonString)) + { + SetCurrentPath(FileDialog::Instance()->homePath); + OpenCurrentPath(vFileDialogInternal); + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip(buttonResetPathString); ImGui::SameLine(); if (IMGUI_BUTTON(parentDirString)) - { - if (SetPathOnParentDirectoryIfAny()) { - OpenCurrentPath(vFileDialogInternal); + { + if (SetPathOnParentDirectoryIfAny()) { + OpenCurrentPath(vFileDialogInternal); } - } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip(buttonParentDirString); + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip(buttonParentDirString); #ifdef WIN32 - ImGui::SameLine(); + ImGui::SameLine(); - if (IMGUI_BUTTON(drivesButtonString)) - { - puDrivesClicked = true; - } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip(buttonDriveString); + if (IMGUI_BUTTON(drivesButtonString)) + { + puDrivesClicked = true; + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip(buttonDriveString); #endif // WIN32 - ImGui::SameLine(); - - if (IMGUI_BUTTON(editPathButtonString)) - { - puInputPathActivated = true; - } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip(buttonEditPathString); + ImGui::SameLine(); + + if (IMGUI_BUTTON(editPathButtonString)) + { + puInputPathActivated = true; + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip(buttonEditPathString); - ImGui::SameLine(); + ImGui::SameLine(); - ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); - // show current path - if (!prCurrentPathDecomposition.empty()) - { - ImGui::SameLine(); + // show current path + if (!prCurrentPathDecomposition.empty()) + { + ImGui::SameLine(); - if (puInputPathActivated) - { - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - ImGui::InputText("##pathedition", puInputPathBuffer, MAX_PATH_BUFFER_SIZE); - ImGui::PopItemWidth(); - } - else - { - int _id = 0; - for (auto itPathDecomp = prCurrentPathDecomposition.begin(); - itPathDecomp != prCurrentPathDecomposition.end(); ++itPathDecomp) - { - if (itPathDecomp != prCurrentPathDecomposition.begin()) - ImGui::SameLine(); - ImGui::PushID(_id++); - bool click = IMGUI_PATH_BUTTON((*itPathDecomp).c_str()); - ImGui::PopID(); - if (click) - { - ComposeNewPath(itPathDecomp); - puPathClicked = true; - break; - } - // activate input for path - if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) - { - ComposeNewPath(itPathDecomp); - IGFD::Utils::SetBuffer(puInputPathBuffer, MAX_PATH_BUFFER_SIZE, prCurrentPath); - puInputPathActivated = true; - break; - } - } - } - } - } + if (puInputPathActivated) + { + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + ImGui::InputText("##pathedition", puInputPathBuffer, MAX_PATH_BUFFER_SIZE); + ImGui::PopItemWidth(); + } + else + { + int _id = 0; + for (auto itPathDecomp = prCurrentPathDecomposition.begin(); + itPathDecomp != prCurrentPathDecomposition.end(); ++itPathDecomp) + { + if (itPathDecomp != prCurrentPathDecomposition.begin()) + ImGui::SameLine(); + ImGui::PushID(_id++); + bool click = IMGUI_PATH_BUTTON((*itPathDecomp).c_str()); + ImGui::PopID(); + if (click) + { + ComposeNewPath(itPathDecomp); + puPathClicked = true; + break; + } + // activate input for path + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) + { + ComposeNewPath(itPathDecomp); + IGFD::Utils::SetBuffer(puInputPathBuffer, MAX_PATH_BUFFER_SIZE, prCurrentPath); + puInputPathActivated = true; + break; + } + } + } + } + } - std::string IGFD::FileManager::GetResultingPath() - { - std::string path = prCurrentPath; + std::string IGFD::FileManager::GetResultingPath() + { + std::string path = prCurrentPath; - if (puDLGDirectoryMode) // if directory mode - { - std::string selectedDirectory = puFileNameBuffer; - if (!selectedDirectory.empty() && - selectedDirectory != ".") - path += std::string(1u, PATH_SEP) + selectedDirectory; - } + if (puDLGDirectoryMode) // if directory mode + { + std::string selectedDirectory = puFileNameBuffer; + if (!selectedDirectory.empty() && + selectedDirectory != ".") + path += std::string(1u, PATH_SEP) + selectedDirectory; + } - return path; - } + return path; + } - std::string IGFD::FileManager::GetResultingFileName(FileDialogInternal& vFileDialogInternal) - { - if (!puDLGDirectoryMode) // if not directory mode - { - return puFileNameBuffer; //vFileDialogInternal.puFilterManager.ReplaceExtentionWithCurrentFilter(std::string(puFileNameBuffer)); - } + std::string IGFD::FileManager::GetResultingFileName(FileDialogInternal& vFileDialogInternal) + { + if (!puDLGDirectoryMode) // if not directory mode + { + return puFileNameBuffer; //vFileDialogInternal.puFilterManager.ReplaceExtentionWithCurrentFilter(std::string(puFileNameBuffer)); + } - return ""; // directory mode - } + return ""; // directory mode + } - std::string IGFD::FileManager::GetResultingFilePathName(FileDialogInternal& vFileDialogInternal) - { - std::string result = GetResultingPath(); + std::string IGFD::FileManager::GetResultingFilePathName(FileDialogInternal& vFileDialogInternal) + { + std::string result = GetResultingPath(); - std::string filename = GetResultingFileName(vFileDialogInternal); - if (!filename.empty()) - { + std::string filename = GetResultingFileName(vFileDialogInternal); + if (!filename.empty()) + { #ifdef UNIX - if (puFsRoot != result) + if (puFsRoot != result) #endif // UNIX - result += std::string(1u, PATH_SEP); + result += std::string(1u, PATH_SEP); - result += filename; - } + result += filename; + } - return result; - } + return result; + } - std::map IGFD::FileManager::GetResultingSelection() - { - std::map res; + std::map IGFD::FileManager::GetResultingSelection() + { + std::map res; - for (auto& selectedFileName : prSelectedFileNames) - { - std::string result = GetResultingPath(); + for (auto& selectedFileName : prSelectedFileNames) + { + std::string result = GetResultingPath(); #ifdef UNIX - if (puFsRoot != result) + if (puFsRoot != result) #endif // UNIX - result += std::string(1u, PATH_SEP); + result += std::string(1u, PATH_SEP); - result += selectedFileName; + result += selectedFileName; - res[selectedFileName] = result; - } + res[selectedFileName] = result; + } - return res; - } + return res; + } - ///////////////////////////////////////////////////////////////////////////////////// - //// FILE DIALOG INTERNAL /////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + //// FILE DIALOG INTERNAL /////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// - void IGFD::FileDialogInternal::NewFrame() - { - puCanWeContinue = true; // reset flag for possibily validate the dialog - puIsOk = false; // reset dialog result - puFileManager.puDrivesClicked = false; - puFileManager.puPathClicked = false; - - puNeedToExitDialog = false; + void IGFD::FileDialogInternal::NewFrame() + { + puCanWeContinue = true; // reset flag for possibily validate the dialog + puIsOk = false; // reset dialog result + puFileManager.puDrivesClicked = false; + puFileManager.puPathClicked = false; + + puNeedToExitDialog = false; #ifdef USE_DIALOG_EXIT_WITH_KEY - if (ImGui::IsKeyPressed(IGFD_EXIT_KEY)) - { - // we do that here with the data's defined at the last frame - // because escape key can quit input activation and at the end of the frame all flag will be false - // so we will detect nothing - if (!(puFileManager.puInputPathActivated || - puSearchManager.puSearchInputIsActive || - puFileInputIsActive || - puFileListViewIsActive)) - { - puNeedToExitDialog = true; // need to quit dialog - } - } - else + if (ImGui::IsKeyPressed(IGFD_EXIT_KEY)) + { + // we do that here with the data's defined at the last frame + // because escape key can quit input activation and at the end of the frame all flag will be false + // so we will detect nothing + if (!(puFileManager.puInputPathActivated || + puSearchManager.puSearchInputIsActive || + puFileInputIsActive || + puFileListViewIsActive)) + { + puNeedToExitDialog = true; // need to quit dialog + } + } + else #endif - { - puSearchManager.puSearchInputIsActive = false; - puFileInputIsActive = false; - puFileListViewIsActive = false; - } - } + { + puSearchManager.puSearchInputIsActive = false; + puFileInputIsActive = false; + puFileListViewIsActive = false; + } + } - void IGFD::FileDialogInternal::EndFrame() - { - // directory change - if (puFileManager.puPathClicked) - { - puFileManager.OpenCurrentPath(*this); - } + void IGFD::FileDialogInternal::EndFrame() + { + // directory change + if (puFileManager.puPathClicked) + { + puFileManager.OpenCurrentPath(*this); + } - if (puFileManager.puDrivesClicked) - { - if (puFileManager.GetDrives()) - { - puFileManager.ApplyFilteringOnFileList(*this); - } - } + if (puFileManager.puDrivesClicked) + { + if (puFileManager.GetDrives()) + { + puFileManager.ApplyFilteringOnFileList(*this); + } + } - if (puFileManager.puInputPathActivated) - { - auto gio = ImGui::GetIO(); - if (ImGui::IsKeyReleased(ImGuiKey_Enter)) - { - puFileManager.SetCurrentPath(std::string(puFileManager.puInputPathBuffer)); - puFileManager.OpenCurrentPath(*this); - puFileManager.puInputPathActivated = false; - } - if (ImGui::IsKeyReleased(ImGuiKey_Escape)) - { - puFileManager.puInputPathActivated = false; - } - } - } + if (puFileManager.puInputPathActivated) + { + auto gio = ImGui::GetIO(); + if (ImGui::IsKeyReleased(ImGuiKey_Enter)) + { + puFileManager.SetCurrentPath(std::string(puFileManager.puInputPathBuffer)); + puFileManager.OpenCurrentPath(*this); + puFileManager.puInputPathActivated = false; + } + if (ImGui::IsKeyReleased(ImGuiKey_Escape)) + { + puFileManager.puInputPathActivated = false; + } + } + } - void IGFD::FileDialogInternal::ResetForNewDialog() - { - puFileManager.fileListActuallyEmpty=false; - } + void IGFD::FileDialogInternal::ResetForNewDialog() + { + puFileManager.fileListActuallyEmpty=false; + } - ///////////////////////////////////////////////////////////////////////////////////// - //// THUMBNAIL FEATURE ////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + //// THUMBNAIL FEATURE ////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// - IGFD::ThumbnailFeature::ThumbnailFeature() - { + IGFD::ThumbnailFeature::ThumbnailFeature() + { #ifdef USE_THUMBNAILS - prDisplayMode = DisplayModeEnum::FILE_LIST; + prDisplayMode = DisplayModeEnum::FILE_LIST; #endif - } + } - IGFD::ThumbnailFeature::~ThumbnailFeature() = default; + IGFD::ThumbnailFeature::~ThumbnailFeature() = default; - void IGFD::ThumbnailFeature::NewThumbnailFrame(FileDialogInternal& vFileDialogInternal) - { - (void)vFileDialogInternal; + void IGFD::ThumbnailFeature::NewThumbnailFrame(FileDialogInternal& vFileDialogInternal) + { + (void)vFileDialogInternal; #ifdef USE_THUMBNAILS - prStartThumbnailFileDatasExtraction(); + prStartThumbnailFileDatasExtraction(); #endif - } + } - void IGFD::ThumbnailFeature::EndThumbnailFrame(FileDialogInternal& vFileDialogInternal) - { + void IGFD::ThumbnailFeature::EndThumbnailFrame(FileDialogInternal& vFileDialogInternal) + { #ifdef USE_THUMBNAILS - prClearThumbnails(vFileDialogInternal); + prClearThumbnails(vFileDialogInternal); #endif - } + } - void IGFD::ThumbnailFeature::QuitThumbnailFrame(FileDialogInternal& vFileDialogInternal) - { + void IGFD::ThumbnailFeature::QuitThumbnailFrame(FileDialogInternal& vFileDialogInternal) + { #ifdef USE_THUMBNAILS - prStopThumbnailFileDatasExtraction(); - prClearThumbnails(vFileDialogInternal); + prStopThumbnailFileDatasExtraction(); + prClearThumbnails(vFileDialogInternal); #endif - } + } #ifdef USE_THUMBNAILS - void IGFD::ThumbnailFeature::prStartThumbnailFileDatasExtraction() - { - const bool res = prThumbnailGenerationThread.use_count() && prThumbnailGenerationThread->joinable(); - if (!res) - { - prIsWorking = true; - prCountFiles = 0U; - prThumbnailGenerationThread = std::shared_ptr( - new std::thread(&IGFD::ThumbnailFeature::prThreadThumbnailFileDatasExtractionFunc, this), - [this](std::thread* obj) - { - prIsWorking = false; - if (obj) - obj->join(); - }); - } - } + void IGFD::ThumbnailFeature::prStartThumbnailFileDatasExtraction() + { + const bool res = prThumbnailGenerationThread.use_count() && prThumbnailGenerationThread->joinable(); + if (!res) + { + prIsWorking = true; + prCountFiles = 0U; + prThumbnailGenerationThread = std::shared_ptr( + new std::thread(&IGFD::ThumbnailFeature::prThreadThumbnailFileDatasExtractionFunc, this), + [this](std::thread* obj) + { + prIsWorking = false; + if (obj) + obj->join(); + }); + } + } - bool IGFD::ThumbnailFeature::prStopThumbnailFileDatasExtraction() - { - const bool res = prThumbnailGenerationThread.use_count() && prThumbnailGenerationThread->joinable(); - if (res) - { - prThumbnailGenerationThread.reset(); - } + bool IGFD::ThumbnailFeature::prStopThumbnailFileDatasExtraction() + { + const bool res = prThumbnailGenerationThread.use_count() && prThumbnailGenerationThread->joinable(); + if (res) + { + prThumbnailGenerationThread.reset(); + } - return res; - } - - void IGFD::ThumbnailFeature::prThreadThumbnailFileDatasExtractionFunc() - { - prCountFiles = 0U; - prIsWorking = true; + return res; + } + + void IGFD::ThumbnailFeature::prThreadThumbnailFileDatasExtractionFunc() + { + prCountFiles = 0U; + prIsWorking = true; - // infinite loop while is thread working - while(prIsWorking) - { - if (!prThumbnailFileDatasToGet.empty()) - { - std::shared_ptr file = nullptr; - prThumbnailFileDatasToGetMutex.lock(); - //get the first file in the list - file = (*prThumbnailFileDatasToGet.begin()); - prThumbnailFileDatasToGetMutex.unlock(); + // infinite loop while is thread working + while(prIsWorking) + { + if (!prThumbnailFileDatasToGet.empty()) + { + std::shared_ptr file = nullptr; + prThumbnailFileDatasToGetMutex.lock(); + //get the first file in the list + file = (*prThumbnailFileDatasToGet.begin()); + prThumbnailFileDatasToGetMutex.unlock(); - // retrieve datas of the texture file if its an image file - if (file.use_count()) - { - if (file->fileType == 'f') //-V522 - { - if (file->fileExt == ".png" - || file->fileExt == ".bmp" - || file->fileExt == ".tga" - || file->fileExt == ".jpg" || file->fileExt == ".jpeg" - || file->fileExt == ".gif" - || file->fileExt == ".psd" - || file->fileExt == ".pic" - || file->fileExt == ".ppm" || file->fileExt == ".pgm" - //|| file->fileExt == ".hdr" => format float so in few times - ) - { - auto fpn = file->filePath + std::string(1u, PATH_SEP) + file->fileNameExt; + // retrieve datas of the texture file if its an image file + if (file.use_count()) + { + if (file->fileType == 'f') //-V522 + { + if (file->fileExt == ".png" + || file->fileExt == ".bmp" + || file->fileExt == ".tga" + || file->fileExt == ".jpg" || file->fileExt == ".jpeg" + || file->fileExt == ".gif" + || file->fileExt == ".psd" + || file->fileExt == ".pic" + || file->fileExt == ".ppm" || file->fileExt == ".pgm" + //|| file->fileExt == ".hdr" => format float so in few times + ) + { + auto fpn = file->filePath + std::string(1u, PATH_SEP) + file->fileNameExt; - int w = 0; - int h = 0; - int chans = 0; - uint8_t *datas = stbi_load(fpn.c_str(), &w, &h, &chans, STBI_rgb_alpha); - if (datas) - { - if (w && h) - { - // resize with respect to glyph ratio - const float ratioX = (float)w / (float)h; - const float newX = DisplayMode_ThumbailsList_ImageHeight * ratioX; - float newY = w / ratioX; - if (newX < w) - newY = DisplayMode_ThumbailsList_ImageHeight; + int w = 0; + int h = 0; + int chans = 0; + uint8_t *datas = stbi_load(fpn.c_str(), &w, &h, &chans, STBI_rgb_alpha); + if (datas) + { + if (w && h) + { + // resize with respect to glyph ratio + const float ratioX = (float)w / (float)h; + const float newX = DisplayMode_ThumbailsList_ImageHeight * ratioX; + float newY = w / ratioX; + if (newX < w) + newY = DisplayMode_ThumbailsList_ImageHeight; - const auto newWidth = (int)newX; - const auto newHeight = (int)newY; - const auto newBufSize = (size_t)(newWidth * newHeight * 4U); //-V112 //-V1028 - auto resizedData = new uint8_t[newBufSize]; - - const int resizeSucceeded = stbir_resize_uint8( - datas, w, h, 0, - resizedData, newWidth, newHeight, 0, - 4); //-V112 + const auto newWidth = (int)newX; + const auto newHeight = (int)newY; + const auto newBufSize = (size_t)(newWidth * newHeight * 4U); //-V112 //-V1028 + auto resizedData = new uint8_t[newBufSize]; + + const int resizeSucceeded = stbir_resize_uint8( + datas, w, h, 0, + resizedData, newWidth, newHeight, 0, + 4); //-V112 - if (resizeSucceeded) - { - auto th = &file->thumbnailInfo; + if (resizeSucceeded) + { + auto th = &file->thumbnailInfo; - th->textureFileDatas = resizedData; - th->textureWidth = newWidth; - th->textureHeight = newHeight; - th->textureChannels = 4; //-V112 + th->textureFileDatas = resizedData; + th->textureWidth = newWidth; + th->textureHeight = newHeight; + th->textureChannels = 4; //-V112 - // we set that at least, because will launch the gpu creation of the texture in the main thread - th->isReadyToUpload = true; + // we set that at least, because will launch the gpu creation of the texture in the main thread + th->isReadyToUpload = true; - // need gpu loading - prAddThumbnailToCreate(file); - } - } - else - { - printf("image loading fail : w:%i h:%i c:%i\n", w, h, 4); //-V112 - } + // need gpu loading + prAddThumbnailToCreate(file); + } + } + else + { + printf("image loading fail : w:%i h:%i c:%i\n", w, h, 4); //-V112 + } - stbi_image_free(datas); - } - } - } + stbi_image_free(datas); + } + } + } - // peu importe le resultat on vire le fichicer - // remove form this list - // write => thread concurency issues - prThumbnailFileDatasToGetMutex.lock(); - prThumbnailFileDatasToGet.pop_front(); - prThumbnailFileDatasToGetMutex.unlock(); - } - } - } - } + // peu importe le resultat on vire le fichicer + // remove form this list + // write => thread concurency issues + prThumbnailFileDatasToGetMutex.lock(); + prThumbnailFileDatasToGet.pop_front(); + prThumbnailFileDatasToGetMutex.unlock(); + } + } + } + } - inline void inVariadicProgressBar(float fraction, const ImVec2& size_arg, const char* fmt, ...) - { - va_list args; - va_start(args, fmt); - char TempBuffer[512]; - const int w = vsnprintf(TempBuffer, 511, fmt, args); - va_end(args); - if (w) - { - ImGui::ProgressBar(fraction, size_arg, TempBuffer); - } - } + inline void inVariadicProgressBar(float fraction, const ImVec2& size_arg, const char* fmt, ...) + { + va_list args; + va_start(args, fmt); + char TempBuffer[512]; + const int w = vsnprintf(TempBuffer, 511, fmt, args); + va_end(args); + if (w) + { + ImGui::ProgressBar(fraction, size_arg, TempBuffer); + } + } - void IGFD::ThumbnailFeature::prDrawThumbnailGenerationProgress() - { - if (prThumbnailGenerationThread.use_count() && prThumbnailGenerationThread->joinable()) - { - if (!prThumbnailFileDatasToGet.empty()) - { - const auto p = (float)((double)prCountFiles / (double)prThumbnailFileDatasToGet.size()); // read => no thread concurency issues - inVariadicProgressBar(p, ImVec2(50, 0), "%u/%u", prCountFiles, (uint32_t)prThumbnailFileDatasToGet.size()); // read => no thread concurency issues - ImGui::SameLine(); - } - } - } + void IGFD::ThumbnailFeature::prDrawThumbnailGenerationProgress() + { + if (prThumbnailGenerationThread.use_count() && prThumbnailGenerationThread->joinable()) + { + if (!prThumbnailFileDatasToGet.empty()) + { + const auto p = (float)((double)prCountFiles / (double)prThumbnailFileDatasToGet.size()); // read => no thread concurency issues + inVariadicProgressBar(p, ImVec2(50, 0), "%u/%u", prCountFiles, (uint32_t)prThumbnailFileDatasToGet.size()); // read => no thread concurency issues + ImGui::SameLine(); + } + } + } - void IGFD::ThumbnailFeature::prAddThumbnailToLoad(const std::shared_ptr& vFileInfos) - { - if (vFileInfos.use_count()) - { - if (vFileInfos->fileType == 'f') - { - if (vFileInfos->fileExt == ".png" - || vFileInfos->fileExt == ".bmp" - || vFileInfos->fileExt == ".tga" - || vFileInfos->fileExt == ".jpg" || vFileInfos->fileExt == ".jpeg" - || vFileInfos->fileExt == ".gif" - || vFileInfos->fileExt == ".psd" - || vFileInfos->fileExt == ".pic" - || vFileInfos->fileExt == ".ppm" || vFileInfos->fileExt == ".pgm" - //|| file->fileExt == ".hdr" => format float so in few times - ) - { - // write => thread concurency issues - prThumbnailFileDatasToGetMutex.lock(); - prThumbnailFileDatasToGet.push_back(vFileInfos); - vFileInfos->thumbnailInfo.isLoadingOrLoaded = true; - prThumbnailFileDatasToGetMutex.unlock(); - } - } - } - } - - void IGFD::ThumbnailFeature::prAddThumbnailToCreate(const std::shared_ptr& vFileInfos) - { - if (vFileInfos.use_count()) - { - // write => thread concurency issues - prThumbnailToCreateMutex.lock(); - prThumbnailToCreate.push_back(vFileInfos); - prThumbnailToCreateMutex.unlock(); - } - } + void IGFD::ThumbnailFeature::prAddThumbnailToLoad(const std::shared_ptr& vFileInfos) + { + if (vFileInfos.use_count()) + { + if (vFileInfos->fileType == 'f') + { + if (vFileInfos->fileExt == ".png" + || vFileInfos->fileExt == ".bmp" + || vFileInfos->fileExt == ".tga" + || vFileInfos->fileExt == ".jpg" || vFileInfos->fileExt == ".jpeg" + || vFileInfos->fileExt == ".gif" + || vFileInfos->fileExt == ".psd" + || vFileInfos->fileExt == ".pic" + || vFileInfos->fileExt == ".ppm" || vFileInfos->fileExt == ".pgm" + //|| file->fileExt == ".hdr" => format float so in few times + ) + { + // write => thread concurency issues + prThumbnailFileDatasToGetMutex.lock(); + prThumbnailFileDatasToGet.push_back(vFileInfos); + vFileInfos->thumbnailInfo.isLoadingOrLoaded = true; + prThumbnailFileDatasToGetMutex.unlock(); + } + } + } + } + + void IGFD::ThumbnailFeature::prAddThumbnailToCreate(const std::shared_ptr& vFileInfos) + { + if (vFileInfos.use_count()) + { + // write => thread concurency issues + prThumbnailToCreateMutex.lock(); + prThumbnailToCreate.push_back(vFileInfos); + prThumbnailToCreateMutex.unlock(); + } + } - void IGFD::ThumbnailFeature::prAddThumbnailToDestroy(const IGFD_Thumbnail_Info& vIGFD_Thumbnail_Info) - { - // write => thread concurency issues - prThumbnailToDestroyMutex.lock(); - prThumbnailToDestroy.push_back(vIGFD_Thumbnail_Info); - prThumbnailToDestroyMutex.unlock(); - } + void IGFD::ThumbnailFeature::prAddThumbnailToDestroy(const IGFD_Thumbnail_Info& vIGFD_Thumbnail_Info) + { + // write => thread concurency issues + prThumbnailToDestroyMutex.lock(); + prThumbnailToDestroy.push_back(vIGFD_Thumbnail_Info); + prThumbnailToDestroyMutex.unlock(); + } - void IGFD::ThumbnailFeature::prDrawDisplayModeToolBar() - { - if (IMGUI_RADIO_BUTTON(DisplayMode_FilesList_ButtonString, - prDisplayMode == DisplayModeEnum::FILE_LIST)) - prDisplayMode = DisplayModeEnum::FILE_LIST; - if (ImGui::IsItemHovered()) ImGui::SetTooltip(DisplayMode_FilesList_ButtonHelp); - ImGui::SameLine(); - if (IMGUI_RADIO_BUTTON(DisplayMode_ThumbailsList_ButtonString, - prDisplayMode == DisplayModeEnum::THUMBNAILS_LIST)) - prDisplayMode = DisplayModeEnum::THUMBNAILS_LIST; - if (ImGui::IsItemHovered()) ImGui::SetTooltip(DisplayMode_ThumbailsList_ButtonHelp); - ImGui::SameLine(); - /* todo - if (IMGUI_RADIO_BUTTON(DisplayMode_ThumbailsGrid_ButtonString, - prDisplayMode == DisplayModeEnum::THUMBNAILS_GRID)) - prDisplayMode = DisplayModeEnum::THUMBNAILS_GRID; - if (ImGui::IsItemHovered()) ImGui::SetTooltip(DisplayMode_ThumbailsGrid_ButtonHelp); - ImGui::SameLine(); - */ - prDrawThumbnailGenerationProgress(); - } + void IGFD::ThumbnailFeature::prDrawDisplayModeToolBar() + { + if (IMGUI_RADIO_BUTTON(DisplayMode_FilesList_ButtonString, + prDisplayMode == DisplayModeEnum::FILE_LIST)) + prDisplayMode = DisplayModeEnum::FILE_LIST; + if (ImGui::IsItemHovered()) ImGui::SetTooltip(DisplayMode_FilesList_ButtonHelp); + ImGui::SameLine(); + if (IMGUI_RADIO_BUTTON(DisplayMode_ThumbailsList_ButtonString, + prDisplayMode == DisplayModeEnum::THUMBNAILS_LIST)) + prDisplayMode = DisplayModeEnum::THUMBNAILS_LIST; + if (ImGui::IsItemHovered()) ImGui::SetTooltip(DisplayMode_ThumbailsList_ButtonHelp); + ImGui::SameLine(); + /* todo + if (IMGUI_RADIO_BUTTON(DisplayMode_ThumbailsGrid_ButtonString, + prDisplayMode == DisplayModeEnum::THUMBNAILS_GRID)) + prDisplayMode = DisplayModeEnum::THUMBNAILS_GRID; + if (ImGui::IsItemHovered()) ImGui::SetTooltip(DisplayMode_ThumbailsGrid_ButtonHelp); + ImGui::SameLine(); + */ + prDrawThumbnailGenerationProgress(); + } - void IGFD::ThumbnailFeature::prClearThumbnails(FileDialogInternal& vFileDialogInternal) - { - // directory wil be changed so the file list will be erased - if (vFileDialogInternal.puFileManager.puPathClicked) - { - size_t count = vFileDialogInternal.puFileManager.GetFullFileListSize(); - for (size_t idx = 0U; idx < count; idx++) - { - auto file = vFileDialogInternal.puFileManager.GetFullFileAt(idx); - if (file.use_count()) - { - if (file->thumbnailInfo.isReadyToDisplay) //-V522 - { - prAddThumbnailToDestroy(file->thumbnailInfo); - } - } - } - } - } + void IGFD::ThumbnailFeature::prClearThumbnails(FileDialogInternal& vFileDialogInternal) + { + // directory wil be changed so the file list will be erased + if (vFileDialogInternal.puFileManager.puPathClicked) + { + size_t count = vFileDialogInternal.puFileManager.GetFullFileListSize(); + for (size_t idx = 0U; idx < count; idx++) + { + auto file = vFileDialogInternal.puFileManager.GetFullFileAt(idx); + if (file.use_count()) + { + if (file->thumbnailInfo.isReadyToDisplay) //-V522 + { + prAddThumbnailToDestroy(file->thumbnailInfo); + } + } + } + } + } - void IGFD::ThumbnailFeature::SetCreateThumbnailCallback(const CreateThumbnailFun& vCreateThumbnailFun) - { - prCreateThumbnailFun = vCreateThumbnailFun; - } + void IGFD::ThumbnailFeature::SetCreateThumbnailCallback(const CreateThumbnailFun& vCreateThumbnailFun) + { + prCreateThumbnailFun = vCreateThumbnailFun; + } - void IGFD::ThumbnailFeature::SetDestroyThumbnailCallback(const DestroyThumbnailFun& vCreateThumbnailFun) - { - prDestroyThumbnailFun = vCreateThumbnailFun; - } + void IGFD::ThumbnailFeature::SetDestroyThumbnailCallback(const DestroyThumbnailFun& vCreateThumbnailFun) + { + prDestroyThumbnailFun = vCreateThumbnailFun; + } - void IGFD::ThumbnailFeature::ManageGPUThumbnails() - { - if (prCreateThumbnailFun) - { - if (!prThumbnailToCreate.empty()) - { - for (const auto& file : prThumbnailToCreate) - { - if (file.use_count()) - { - prCreateThumbnailFun(&file->thumbnailInfo); - } - } - prThumbnailToCreateMutex.lock(); - prThumbnailToCreate.clear(); - prThumbnailToCreateMutex.unlock(); - } - } - else - { - printf("No Callback found for create texture\nYou need to define the callback with a call to SetCreateThumbnailCallback\n"); - } + void IGFD::ThumbnailFeature::ManageGPUThumbnails() + { + if (prCreateThumbnailFun) + { + if (!prThumbnailToCreate.empty()) + { + for (const auto& file : prThumbnailToCreate) + { + if (file.use_count()) + { + prCreateThumbnailFun(&file->thumbnailInfo); + } + } + prThumbnailToCreateMutex.lock(); + prThumbnailToCreate.clear(); + prThumbnailToCreateMutex.unlock(); + } + } + else + { + printf("No Callback found for create texture\nYou need to define the callback with a call to SetCreateThumbnailCallback\n"); + } - if (prDestroyThumbnailFun) - { - if (!prThumbnailToDestroy.empty()) - { - for (auto thumbnail : prThumbnailToDestroy) - { - prDestroyThumbnailFun(&thumbnail); - } - prThumbnailToDestroyMutex.lock(); - prThumbnailToDestroy.clear(); - prThumbnailToDestroyMutex.unlock(); - } - } - else - { - printf("No Callback found for destroy texture\nYou need to define the callback with a call to SetCreateThumbnailCallback\n"); - } - } + if (prDestroyThumbnailFun) + { + if (!prThumbnailToDestroy.empty()) + { + for (auto thumbnail : prThumbnailToDestroy) + { + prDestroyThumbnailFun(&thumbnail); + } + prThumbnailToDestroyMutex.lock(); + prThumbnailToDestroy.clear(); + prThumbnailToDestroyMutex.unlock(); + } + } + else + { + printf("No Callback found for destroy texture\nYou need to define the callback with a call to SetCreateThumbnailCallback\n"); + } + } #endif // USE_THUMBNAILS - ///////////////////////////////////////////////////////////////////////////////////// - //// BOOKMARK FEATURE /////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + //// BOOKMARK FEATURE /////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// - IGFD::BookMarkFeature::BookMarkFeature() - { + IGFD::BookMarkFeature::BookMarkFeature() + { #ifdef USE_BOOKMARK - prBookmarkWidth = defaultBookmarkPaneWith; + prBookmarkWidth = defaultBookmarkPaneWith; #endif // USE_BOOKMARK - } + } #ifdef USE_BOOKMARK - void IGFD::BookMarkFeature::prDrawBookmarkButton() - { - IMGUI_TOGGLE_BUTTON(bookmarksButtonString, &prBookmarkPaneShown); + void IGFD::BookMarkFeature::prDrawBookmarkButton() + { + IMGUI_TOGGLE_BUTTON(bookmarksButtonString, &prBookmarkPaneShown); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip(bookmarksButtonHelpString); - } - bool IGFD::BookMarkFeature::prDrawBookmarkPane(FileDialogInternal& vFileDialogInternal, const ImVec2& vSize) - { - bool res = false; + if (ImGui::IsItemHovered()) + ImGui::SetTooltip(bookmarksButtonHelpString); + } + bool IGFD::BookMarkFeature::prDrawBookmarkPane(FileDialogInternal& vFileDialogInternal, const ImVec2& vSize) + { + bool res = false; - ImGui::BeginChild("##bookmarkpane", vSize); + ImGui::BeginChild("##bookmarkpane", vSize); - static int selectedBookmarkForEdition = -1; + static int selectedBookmarkForEdition = -1; - if (IMGUI_BUTTON(addBookmarkButtonString "##ImGuiFileDialogAddBookmark")) - { - if (!vFileDialogInternal.puFileManager.IsComposerEmpty()) - { - BookmarkStruct bookmark; - bookmark.name = vFileDialogInternal.puFileManager.GetBack(); - bookmark.path = vFileDialogInternal.puFileManager.GetCurrentPath(); - prBookmarks.push_back(bookmark); - } - } - if (selectedBookmarkForEdition >= 0 && - selectedBookmarkForEdition < (int)prBookmarks.size()) - { - ImGui::SameLine(); - if (IMGUI_BUTTON(removeBookmarkButtonString "##ImGuiFileDialogAddBookmark")) - { - prBookmarks.erase(prBookmarks.begin() + selectedBookmarkForEdition); - if (selectedBookmarkForEdition == (int)prBookmarks.size()) - selectedBookmarkForEdition--; - } + if (IMGUI_BUTTON(addBookmarkButtonString "##ImGuiFileDialogAddBookmark")) + { + if (!vFileDialogInternal.puFileManager.IsComposerEmpty()) + { + BookmarkStruct bookmark; + bookmark.name = vFileDialogInternal.puFileManager.GetBack(); + bookmark.path = vFileDialogInternal.puFileManager.GetCurrentPath(); + prBookmarks.push_back(bookmark); + } + } + if (selectedBookmarkForEdition >= 0 && + selectedBookmarkForEdition < (int)prBookmarks.size()) + { + ImGui::SameLine(); + if (IMGUI_BUTTON(removeBookmarkButtonString "##ImGuiFileDialogAddBookmark")) + { + prBookmarks.erase(prBookmarks.begin() + selectedBookmarkForEdition); + if (selectedBookmarkForEdition == (int)prBookmarks.size()) + selectedBookmarkForEdition--; + } - if (selectedBookmarkForEdition >= 0 && - selectedBookmarkForEdition < (int)prBookmarks.size()) - { - ImGui::SameLine(); + if (selectedBookmarkForEdition >= 0 && + selectedBookmarkForEdition < (int)prBookmarks.size()) + { + ImGui::SameLine(); - ImGui::PushItemWidth(vSize.x - ImGui::GetCursorPosX()); - if (ImGui::InputText("##ImGuiFileDialogBookmarkEdit", prBookmarkEditBuffer, MAX_FILE_DIALOG_NAME_BUFFER)) - { - prBookmarks[(size_t)selectedBookmarkForEdition].name = std::string(prBookmarkEditBuffer); - } - ImGui::PopItemWidth(); - } - } + ImGui::PushItemWidth(vSize.x - ImGui::GetCursorPosX()); + if (ImGui::InputText("##ImGuiFileDialogBookmarkEdit", prBookmarkEditBuffer, MAX_FILE_DIALOG_NAME_BUFFER)) + { + prBookmarks[(size_t)selectedBookmarkForEdition].name = std::string(prBookmarkEditBuffer); + } + ImGui::PopItemWidth(); + } + } - ImGui::Separator(); + ImGui::Separator(); - if (!prBookmarks.empty()) - { - prBookmarkClipper.Begin((int)prBookmarks.size(), ImGui::GetTextLineHeightWithSpacing()); - while (prBookmarkClipper.Step()) - { - for (int i = prBookmarkClipper.DisplayStart; i < prBookmarkClipper.DisplayEnd; i++) - { - if (i < 0) continue; - const BookmarkStruct& bookmark = prBookmarks[(size_t)i]; - ImGui::PushID(i); - if (ImGui::Selectable(bookmark.name.c_str(), selectedBookmarkForEdition == i, - ImGuiSelectableFlags_AllowDoubleClick) | - (selectedBookmarkForEdition == -1 && - bookmark.path == vFileDialogInternal.puFileManager.GetCurrentPath())) // select if path is current - { - selectedBookmarkForEdition = i; - IGFD::Utils::ResetBuffer(prBookmarkEditBuffer); - IGFD::Utils::AppendToBuffer(prBookmarkEditBuffer, MAX_FILE_DIALOG_NAME_BUFFER, bookmark.name); + if (!prBookmarks.empty()) + { + prBookmarkClipper.Begin((int)prBookmarks.size(), ImGui::GetTextLineHeightWithSpacing()); + while (prBookmarkClipper.Step()) + { + for (int i = prBookmarkClipper.DisplayStart; i < prBookmarkClipper.DisplayEnd; i++) + { + if (i < 0) continue; + const BookmarkStruct& bookmark = prBookmarks[(size_t)i]; + ImGui::PushID(i); + if (ImGui::Selectable(bookmark.name.c_str(), selectedBookmarkForEdition == i, + ImGuiSelectableFlags_AllowDoubleClick) | + (selectedBookmarkForEdition == -1 && + bookmark.path == vFileDialogInternal.puFileManager.GetCurrentPath())) // select if path is current + { + selectedBookmarkForEdition = i; + IGFD::Utils::ResetBuffer(prBookmarkEditBuffer); + IGFD::Utils::AppendToBuffer(prBookmarkEditBuffer, MAX_FILE_DIALOG_NAME_BUFFER, bookmark.name); - if (ImGui::IsMouseDoubleClicked(0)) // apply path - { - vFileDialogInternal.puFileManager.SetCurrentPath(bookmark.path); - vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal); - res = true; - } - } - ImGui::PopID(); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", bookmark.path.c_str()); //-V111 - } - } - prBookmarkClipper.End(); - } + if (ImGui::IsMouseDoubleClicked(0)) // apply path + { + vFileDialogInternal.puFileManager.SetCurrentPath(bookmark.path); + vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal); + res = true; + } + } + ImGui::PopID(); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", bookmark.path.c_str()); //-V111 + } + } + prBookmarkClipper.End(); + } - ImGui::EndChild(); + ImGui::EndChild(); - return res; - } + return res; + } - std::string IGFD::BookMarkFeature::SerializeBookmarks() - { - std::string res; + std::string IGFD::BookMarkFeature::SerializeBookmarks() + { + std::string res; - size_t idx = 0; - for (auto& it : prBookmarks) - { - if (idx++ != 0) - res += "##"; // ## because reserved by imgui, so an input text cant have ## - res += it.name + "##" + it.path; - } + size_t idx = 0; + for (auto& it : prBookmarks) + { + if (idx++ != 0) + res += "##"; // ## because reserved by imgui, so an input text cant have ## + res += it.name + "##" + it.path; + } - return res; - } + return res; + } - void IGFD::BookMarkFeature::DeserializeBookmarks(const std::string& vBookmarks) - { - if (!vBookmarks.empty()) - { - prBookmarks.clear(); - auto arr = IGFD::Utils::SplitStringToVector(vBookmarks, '#', false); - for (size_t i = 0; i < arr.size(); i += 2) - { - BookmarkStruct bookmark; - bookmark.name = arr[i]; - if (i + 1 < arr.size()) // for avoid crash if arr size is impair due to user mistake after edition - { - // if bad format we jump this bookmark - bookmark.path = arr[i + 1]; - prBookmarks.push_back(bookmark); - } - } - } - } + void IGFD::BookMarkFeature::DeserializeBookmarks(const std::string& vBookmarks) + { + if (!vBookmarks.empty()) + { + prBookmarks.clear(); + auto arr = IGFD::Utils::SplitStringToVector(vBookmarks, '#', false); + for (size_t i = 0; i < arr.size(); i += 2) + { + BookmarkStruct bookmark; + bookmark.name = arr[i]; + if (i + 1 < arr.size()) // for avoid crash if arr size is impair due to user mistake after edition + { + // if bad format we jump this bookmark + bookmark.path = arr[i + 1]; + prBookmarks.push_back(bookmark); + } + } + } + } #endif // USE_BOOKMARK - ///////////////////////////////////////////////////////////////////////////////////// - //// KEY EXPLORER FEATURE /////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + //// KEY EXPLORER FEATURE /////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// - KeyExplorerFeature::KeyExplorerFeature() = default; + KeyExplorerFeature::KeyExplorerFeature() = default; #ifdef USE_EXPLORATION_BY_KEYS - bool IGFD::KeyExplorerFeature::prLocateItem_Loop(FileDialogInternal& vFileDialogInternal, ImWchar vC) - { - bool found = false; + bool IGFD::KeyExplorerFeature::prLocateItem_Loop(FileDialogInternal& vFileDialogInternal, ImWchar vC) + { + bool found = false; - auto& fdi = vFileDialogInternal.puFileManager; - if (!fdi.IsFilteredListEmpty()) - { - auto countFiles = fdi.GetFilteredListSize(); - for (size_t i = prLocateFileByInputChar_lastFileIdx; i < countFiles; i++) - { - auto nfo = fdi.GetFilteredFileAt(i); - if (nfo.use_count()) - { - if (nfo->fileNameExt_optimized[0] == vC || // lower case search //-V522 - nfo->fileNameExt[0] == vC) // maybe upper case search - { - //float p = ((float)i) * ImGui::GetTextLineHeightWithSpacing(); - float p = (float)((double)i / (double)countFiles) * ImGui::GetScrollMaxY(); - ImGui::SetScrollY(p); - prLocateFileByInputChar_lastFound = true; - prLocateFileByInputChar_lastFileIdx = i; - prStartFlashItem(prLocateFileByInputChar_lastFileIdx); + auto& fdi = vFileDialogInternal.puFileManager; + if (!fdi.IsFilteredListEmpty()) + { + auto countFiles = fdi.GetFilteredListSize(); + for (size_t i = prLocateFileByInputChar_lastFileIdx; i < countFiles; i++) + { + auto nfo = fdi.GetFilteredFileAt(i); + if (nfo.use_count()) + { + if (nfo->fileNameExt_optimized[0] == vC || // lower case search //-V522 + nfo->fileNameExt[0] == vC) // maybe upper case search + { + //float p = ((float)i) * ImGui::GetTextLineHeightWithSpacing(); + float p = (float)((double)i / (double)countFiles) * ImGui::GetScrollMaxY(); + ImGui::SetScrollY(p); + prLocateFileByInputChar_lastFound = true; + prLocateFileByInputChar_lastFileIdx = i; + prStartFlashItem(prLocateFileByInputChar_lastFileIdx); - auto infos = fdi.GetFilteredFileAt(prLocateFileByInputChar_lastFileIdx); - if (infos.use_count()) - { - if (infos->fileType == 'd') //-V522 - { - if (fdi.puDLGDirectoryMode) // directory chooser - { - fdi.SelectFileName(vFileDialogInternal, infos); - } - } - else - { - fdi.SelectFileName(vFileDialogInternal, infos); - } + auto infos = fdi.GetFilteredFileAt(prLocateFileByInputChar_lastFileIdx); + if (infos.use_count()) + { + if (infos->fileType == 'd') //-V522 + { + if (fdi.puDLGDirectoryMode) // directory chooser + { + fdi.SelectFileName(vFileDialogInternal, infos); + } + } + else + { + fdi.SelectFileName(vFileDialogInternal, infos); + } - found = true; - break; - } - } - } - } - } + found = true; + break; + } + } + } + } + } - return found; - } + return found; + } - void IGFD::KeyExplorerFeature::prLocateByInputKey(FileDialogInternal& vFileDialogInternal) - { - ImGuiContext& g = *GImGui; - auto& fdi = vFileDialogInternal.puFileManager; - if (!g.ActiveId && !fdi.IsFilteredListEmpty()) - { - auto& queueChar = ImGui::GetIO().InputQueueCharacters; - auto countFiles = fdi.GetFilteredListSize(); + void IGFD::KeyExplorerFeature::prLocateByInputKey(FileDialogInternal& vFileDialogInternal) + { + ImGuiContext& g = *GImGui; + auto& fdi = vFileDialogInternal.puFileManager; + if (!g.ActiveId && !fdi.IsFilteredListEmpty()) + { + auto& queueChar = ImGui::GetIO().InputQueueCharacters; + auto countFiles = fdi.GetFilteredListSize(); - // point by char - if (!queueChar.empty()) - { - ImWchar c = queueChar.back(); - if (prLocateFileByInputChar_InputQueueCharactersSize != queueChar.size()) - { - if (c == prLocateFileByInputChar_lastChar) // next file starting with same char until - { - if (prLocateFileByInputChar_lastFileIdx < countFiles - 1U) - prLocateFileByInputChar_lastFileIdx++; - else - prLocateFileByInputChar_lastFileIdx = 0; - } + // point by char + if (!queueChar.empty()) + { + ImWchar c = queueChar.back(); + if (prLocateFileByInputChar_InputQueueCharactersSize != queueChar.size()) + { + if (c == prLocateFileByInputChar_lastChar) // next file starting with same char until + { + if (prLocateFileByInputChar_lastFileIdx < countFiles - 1U) + prLocateFileByInputChar_lastFileIdx++; + else + prLocateFileByInputChar_lastFileIdx = 0; + } - if (!prLocateItem_Loop(vFileDialogInternal, c)) - { - // not found, loop again from 0 this time - prLocateFileByInputChar_lastFileIdx = 0; - prLocateItem_Loop(vFileDialogInternal, c); - } + if (!prLocateItem_Loop(vFileDialogInternal, c)) + { + // not found, loop again from 0 this time + prLocateFileByInputChar_lastFileIdx = 0; + prLocateItem_Loop(vFileDialogInternal, c); + } - prLocateFileByInputChar_lastChar = c; - } - } + prLocateFileByInputChar_lastChar = c; + } + } - prLocateFileByInputChar_InputQueueCharactersSize = queueChar.size(); - } - } + prLocateFileByInputChar_InputQueueCharactersSize = queueChar.size(); + } + } - void IGFD::KeyExplorerFeature::prExploreWithkeys(FileDialogInternal& vFileDialogInternal, ImGuiID vListViewID) - { - auto& fdi = vFileDialogInternal.puFileManager; - if (!fdi.IsFilteredListEmpty()) - { - bool canWeExplore = false; - bool hasNav = (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); - - ImGuiContext& g = *GImGui; - if (!hasNav && !g.ActiveId) // no nav and no activated inputs - canWeExplore = true; + void IGFD::KeyExplorerFeature::prExploreWithkeys(FileDialogInternal& vFileDialogInternal, ImGuiID vListViewID) + { + auto& fdi = vFileDialogInternal.puFileManager; + if (!fdi.IsFilteredListEmpty()) + { + bool canWeExplore = false; + bool hasNav = (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); + + ImGuiContext& g = *GImGui; + if (!hasNav && !g.ActiveId) // no nav and no activated inputs + canWeExplore = true; - if (g.NavId && g.NavId == vListViewID) - { - if (ImGui::IsKeyPressedMap(IGFD_KEY_ENTER) || - ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter) || - ImGui::IsKeyPressedMap(ImGuiKey_Space)) - { - ImGui::ActivateItem(vListViewID); - ImGui::SetActiveID(vListViewID, g.CurrentWindow); - } - } - - if (vListViewID == g.LastActiveId-1) // if listview id is the last acticated nav id (ImGui::ActivateItem(vListViewID);) - canWeExplore = true; + if (g.NavId && g.NavId == vListViewID) + { + if (ImGui::IsKeyPressedMap(IGFD_KEY_ENTER) || + ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter) || + ImGui::IsKeyPressedMap(ImGuiKey_Space)) + { + ImGui::ActivateItem(vListViewID); + ImGui::SetActiveID(vListViewID, g.CurrentWindow); + } + } + + if (vListViewID == g.LastActiveId-1) // if listview id is the last acticated nav id (ImGui::ActivateItem(vListViewID);) + canWeExplore = true; - if (canWeExplore) - { - if (ImGui::IsKeyPressedMap(ImGuiKey_Escape)) - { - ImGui::ClearActiveID(); - g.LastActiveId = 0; - } + if (canWeExplore) + { + if (ImGui::IsKeyPressedMap(ImGuiKey_Escape)) + { + ImGui::ClearActiveID(); + g.LastActiveId = 0; + } - auto countFiles = fdi.GetFilteredListSize(); + auto countFiles = fdi.GetFilteredListSize(); - // explore - bool exploreByKey = false; - bool enterInDirectory = false; - bool exitDirectory = false; + // explore + bool exploreByKey = false; + bool enterInDirectory = false; + bool exitDirectory = false; - if ((hasNav && ImGui::IsKeyPressedMap(ImGuiKey_UpArrow)) || (!hasNav && ImGui::IsKeyPressed(IGFD_KEY_UP))) - { - exploreByKey = true; - if (prLocateFileByInputChar_lastFileIdx > 0) - prLocateFileByInputChar_lastFileIdx--; - else - prLocateFileByInputChar_lastFileIdx = countFiles - 1U; - } - else if ((hasNav && ImGui::IsKeyPressedMap(ImGuiKey_DownArrow)) || (!hasNav && ImGui::IsKeyPressed(IGFD_KEY_DOWN))) - { - exploreByKey = true; - if (prLocateFileByInputChar_lastFileIdx < countFiles - 1U) - prLocateFileByInputChar_lastFileIdx++; - else - prLocateFileByInputChar_lastFileIdx = 0U; - } - else if (ImGui::IsKeyReleased(IGFD_KEY_ENTER)) - { - exploreByKey = true; - enterInDirectory = true; - } - else if (ImGui::IsKeyReleased(IGFD_KEY_BACKSPACE)) - { - exploreByKey = true; - exitDirectory = true; - } + if ((hasNav && ImGui::IsKeyPressedMap(ImGuiKey_UpArrow)) || (!hasNav && ImGui::IsKeyPressed(IGFD_KEY_UP))) + { + exploreByKey = true; + if (prLocateFileByInputChar_lastFileIdx > 0) + prLocateFileByInputChar_lastFileIdx--; + else + prLocateFileByInputChar_lastFileIdx = countFiles - 1U; + } + else if ((hasNav && ImGui::IsKeyPressedMap(ImGuiKey_DownArrow)) || (!hasNav && ImGui::IsKeyPressed(IGFD_KEY_DOWN))) + { + exploreByKey = true; + if (prLocateFileByInputChar_lastFileIdx < countFiles - 1U) + prLocateFileByInputChar_lastFileIdx++; + else + prLocateFileByInputChar_lastFileIdx = 0U; + } + else if (ImGui::IsKeyReleased(IGFD_KEY_ENTER)) + { + exploreByKey = true; + enterInDirectory = true; + } + else if (ImGui::IsKeyReleased(IGFD_KEY_BACKSPACE)) + { + exploreByKey = true; + exitDirectory = true; + } - if (exploreByKey) - { - //float totalHeight = prFilteredFileList.size() * ImGui::GetTextLineHeightWithSpacing(); - float p = (float)((double)prLocateFileByInputChar_lastFileIdx / (double)(countFiles - 1U)) * ImGui::GetScrollMaxY();// seems not udpated in tables version outside tables - //float p = ((float)locateFileByInputChar_lastFileIdx) * ImGui::GetTextLineHeightWithSpacing(); - ImGui::SetScrollY(p); - prStartFlashItem(prLocateFileByInputChar_lastFileIdx); + if (exploreByKey) + { + //float totalHeight = prFilteredFileList.size() * ImGui::GetTextLineHeightWithSpacing(); + float p = (float)((double)prLocateFileByInputChar_lastFileIdx / (double)(countFiles - 1U)) * ImGui::GetScrollMaxY();// seems not udpated in tables version outside tables + //float p = ((float)locateFileByInputChar_lastFileIdx) * ImGui::GetTextLineHeightWithSpacing(); + ImGui::SetScrollY(p); + prStartFlashItem(prLocateFileByInputChar_lastFileIdx); - auto infos = fdi.GetFilteredFileAt(prLocateFileByInputChar_lastFileIdx); - if (infos.use_count()) - { - if (infos->fileType == 'd') //-V522 - { - if (!fdi.puDLGDirectoryMode || enterInDirectory) - { - if (enterInDirectory) - { - if (fdi.SelectDirectory(infos)) - { - // changement de repertoire - vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal); - if (prLocateFileByInputChar_lastFileIdx > countFiles - 1U) - { - prLocateFileByInputChar_lastFileIdx = 0; - } - } - } - } - else // directory chooser - { - fdi.SelectFileName(vFileDialogInternal, infos); - } - } - else - { - fdi.SelectFileName(vFileDialogInternal, infos); - } + auto infos = fdi.GetFilteredFileAt(prLocateFileByInputChar_lastFileIdx); + if (infos.use_count()) + { + if (infos->fileType == 'd') //-V522 + { + if (!fdi.puDLGDirectoryMode || enterInDirectory) + { + if (enterInDirectory) + { + if (fdi.SelectDirectory(infos)) + { + // changement de repertoire + vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal); + if (prLocateFileByInputChar_lastFileIdx > countFiles - 1U) + { + prLocateFileByInputChar_lastFileIdx = 0; + } + } + } + } + else // directory chooser + { + fdi.SelectFileName(vFileDialogInternal, infos); + } + } + else + { + fdi.SelectFileName(vFileDialogInternal, infos); + } - if (exitDirectory) - { - auto nfo = std::make_shared(); - nfo->fileNameExt = ".."; + if (exitDirectory) + { + auto nfo = std::make_shared(); + nfo->fileNameExt = ".."; - if (fdi.SelectDirectory(nfo)) - { - // changement de repertoire - vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal); - if (prLocateFileByInputChar_lastFileIdx > countFiles - 1U) - { - prLocateFileByInputChar_lastFileIdx = 0; - } - } + if (fdi.SelectDirectory(nfo)) + { + // changement de repertoire + vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal); + if (prLocateFileByInputChar_lastFileIdx > countFiles - 1U) + { + prLocateFileByInputChar_lastFileIdx = 0; + } + } #ifdef WIN32 - else - { - if (fdi.GetComposerSize() == 1U) - { - if (fdi.GetDrives()) - { - fdi.ApplyFilteringOnFileList(vFileDialogInternal); - } - } - } + else + { + if (fdi.GetComposerSize() == 1U) + { + if (fdi.GetDrives()) + { + fdi.ApplyFilteringOnFileList(vFileDialogInternal); + } + } + } #endif // WIN32 - } - } - } - } - } - } + } + } + } + } + } + } - bool IGFD::KeyExplorerFeature::prFlashableSelectable(const char* label, bool selected, - ImGuiSelectableFlags flags, bool vFlashing, const ImVec2& size_arg) - { - using namespace ImGui; + bool IGFD::KeyExplorerFeature::prFlashableSelectable(const char* label, bool selected, + ImGuiSelectableFlags flags, bool vFlashing, const ImVec2& size_arg) + { + using namespace ImGui; - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; - // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle. - ImGuiID id = window->GetID(label); - ImVec2 label_size = CalcTextSize(label, nullptr, true); - ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); //-V550 - ImVec2 pos = window->DC.CursorPos; - pos.y += window->DC.CurrLineTextBaseOffset; - ItemSize(size, 0.0f); + // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle. + ImGuiID id = window->GetID(label); + ImVec2 label_size = CalcTextSize(label, nullptr, true); + ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); //-V550 + ImVec2 pos = window->DC.CursorPos; + pos.y += window->DC.CurrLineTextBaseOffset; + ItemSize(size, 0.0f); - // Fill horizontal space - // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match other widgets. - const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0; - const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x; - const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x; - if (fabs(size_arg.x) < FLT_EPSILON || (flags & ImGuiSelectableFlags_SpanAvailWidth)) - size.x = ImMax(label_size.x, max_x - min_x); + // Fill horizontal space + // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match other widgets. + const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0; + const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x; + const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x; + if (fabs(size_arg.x) < FLT_EPSILON || (flags & ImGuiSelectableFlags_SpanAvailWidth)) + size.x = ImMax(label_size.x, max_x - min_x); - // Text stays at the submission position, but bounding box may be extended on both sides - const ImVec2 text_min = pos; - const ImVec2 text_max(min_x + size.x, pos.y + size.y); + // Text stays at the submission position, but bounding box may be extended on both sides + const ImVec2 text_min = pos; + const ImVec2 text_max(min_x + size.x, pos.y + size.y); - // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable. - ImRect bb(min_x, pos.y, text_max.x, text_max.y); - if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) - { - const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x; - const float spacing_y = style.ItemSpacing.y; - const float spacing_L = IM_FLOOR(spacing_x * 0.50f); - const float spacing_U = IM_FLOOR(spacing_y * 0.50f); - bb.Min.x -= spacing_L; - bb.Min.y -= spacing_U; - bb.Max.x += (spacing_x - spacing_L); - bb.Max.y += (spacing_y - spacing_U); - } - //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); } + // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable. + ImRect bb(min_x, pos.y, text_max.x, text_max.y); + if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) + { + const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x; + const float spacing_y = style.ItemSpacing.y; + const float spacing_L = IM_FLOOR(spacing_x * 0.50f); + const float spacing_U = IM_FLOOR(spacing_y * 0.50f); + bb.Min.x -= spacing_L; + bb.Min.y -= spacing_U; + bb.Max.x += (spacing_x - spacing_L); + bb.Max.y += (spacing_y - spacing_U); + } + //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); } - // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackground for every Selectable.. - const float backup_clip_rect_min_x = window->ClipRect.Min.x; - const float backup_clip_rect_max_x = window->ClipRect.Max.x; - if (span_all_columns) - { - window->ClipRect.Min.x = window->ParentWorkRect.Min.x; - window->ClipRect.Max.x = window->ParentWorkRect.Max.x; - } + // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackground for every Selectable.. + const float backup_clip_rect_min_x = window->ClipRect.Min.x; + const float backup_clip_rect_max_x = window->ClipRect.Max.x; + if (span_all_columns) + { + window->ClipRect.Min.x = window->ParentWorkRect.Min.x; + window->ClipRect.Max.x = window->ParentWorkRect.Max.x; + } - bool item_add; - const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; - if (disabled_item) - { - ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; - g.CurrentItemFlags |= ImGuiItemFlags_Disabled; - item_add = ItemAdd(bb, id); - g.CurrentItemFlags = backup_item_flags; - } - else - { - item_add = ItemAdd(bb, id); - } + bool item_add; + const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; + if (disabled_item) + { + ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_Disabled; + item_add = ItemAdd(bb, id); + g.CurrentItemFlags = backup_item_flags; + } + else + { + item_add = ItemAdd(bb, id); + } - if (span_all_columns) - { - window->ClipRect.Min.x = backup_clip_rect_min_x; - window->ClipRect.Max.x = backup_clip_rect_max_x; - } + if (span_all_columns) + { + window->ClipRect.Min.x = backup_clip_rect_min_x; + window->ClipRect.Max.x = backup_clip_rect_max_x; + } - if (!item_add) - return false; + if (!item_add) + return false; - const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; - if (disabled_item && !disabled_global) // Only testing this as an optimization - BeginDisabled(true); + const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; + if (disabled_item && !disabled_global) // Only testing this as an optimization + BeginDisabled(true); - // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only, - // which would be advantageous since most selectable are not selected. - if (span_all_columns && window->DC.CurrentColumns) - PushColumnsBackground(); - else if (span_all_columns && g.CurrentTable) - TablePushBackgroundChannel(); + // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only, + // which would be advantageous since most selectable are not selected. + if (span_all_columns && window->DC.CurrentColumns) + PushColumnsBackground(); + else if (span_all_columns && g.CurrentTable) + TablePushBackgroundChannel(); - // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries - ImGuiButtonFlags button_flags = 0; - if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; } - if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; } - if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; } - if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } - if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; } + // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries + ImGuiButtonFlags button_flags = 0; + if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; } + if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; } + if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; } + if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } + if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; } - const bool was_selected = selected; - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + const bool was_selected = selected; + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); - // Auto-select when moved into - // - This will be more fully fleshed in the range-select branch - // - This is not exposed as it won't nicely work with some user side handling of shift/control - // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons - // - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope()) - // - (2) usage will fail with clipped items - // The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API. - if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == window->DC.NavFocusScopeIdCurrent) - if (g.NavJustMovedToId == id) - selected = pressed = true; + // Auto-select when moved into + // - This will be more fully fleshed in the range-select branch + // - This is not exposed as it won't nicely work with some user side handling of shift/control + // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons + // - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope()) + // - (2) usage will fail with clipped items + // The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API. + if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == window->DC.NavFocusScopeIdCurrent) + if (g.NavJustMovedToId == id) + selected = pressed = true; - // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard - if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover))) - { - if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) - { - SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, ImRect(bb.Min - window->Pos, bb.Max - window->Pos)); - g.NavDisableHighlight = true; - } - } - if (pressed) - MarkItemEdited(id); + // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard + if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover))) + { + if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) + { + SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, ImRect(bb.Min - window->Pos, bb.Max - window->Pos)); + g.NavDisableHighlight = true; + } + } + if (pressed) + MarkItemEdited(id); - if (flags & ImGuiSelectableFlags_AllowItemOverlap) - SetItemAllowOverlap(); + if (flags & ImGuiSelectableFlags_AllowItemOverlap) + SetItemAllowOverlap(); - // In this branch, Selectable() cannot toggle the selection so this will never trigger. - if (selected != was_selected) //-V547 - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; + // In this branch, Selectable() cannot toggle the selection so this will never trigger. + if (selected != was_selected) //-V547 + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; - // Render - if ((held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld)) || vFlashing) - hovered = true; - if (hovered || selected) - { - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(bb.Min, bb.Max, col, false, 0.0f); - } - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); + // Render + if ((held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld)) || vFlashing) + hovered = true; + if (hovered || selected) + { + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + RenderFrame(bb.Min, bb.Max, col, false, 0.0f); + } + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); - if (span_all_columns && window->DC.CurrentColumns) - PopColumnsBackground(); - else if (span_all_columns && g.CurrentTable) - TablePopBackgroundChannel(); + if (span_all_columns && window->DC.CurrentColumns) + PopColumnsBackground(); + else if (span_all_columns && g.CurrentTable) + TablePopBackgroundChannel(); - RenderTextClipped(text_min, text_max, label, nullptr, &label_size, style.SelectableTextAlign, &bb); + RenderTextClipped(text_min, text_max, label, nullptr, &label_size, style.SelectableTextAlign, &bb); - // Automatically close popups - if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.LastItemData.InFlags & ImGuiItemFlags_SelectableDontClosePopup)) - CloseCurrentPopup(); + // Automatically close popups + if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.LastItemData.InFlags & ImGuiItemFlags_SelectableDontClosePopup)) + CloseCurrentPopup(); - if (disabled_item && !disabled_global) - EndDisabled(); + if (disabled_item && !disabled_global) + EndDisabled(); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); - return pressed; //-V1020 - } + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); + return pressed; //-V1020 + } - void IGFD::KeyExplorerFeature::prStartFlashItem(size_t vIdx) - { - prFlashAlpha = 1.0f; - prFlashedItem = vIdx; - } + void IGFD::KeyExplorerFeature::prStartFlashItem(size_t vIdx) + { + prFlashAlpha = 1.0f; + prFlashedItem = vIdx; + } - bool IGFD::KeyExplorerFeature::prBeginFlashItem(size_t vIdx) - { - bool res = false; + bool IGFD::KeyExplorerFeature::prBeginFlashItem(size_t vIdx) + { + bool res = false; - if (prFlashedItem == vIdx && - std::abs(prFlashAlpha - 0.0f) > 0.00001f) - { - prFlashAlpha -= prFlashAlphaAttenInSecs * ImGui::GetIO().DeltaTime; - if (prFlashAlpha < 0.0f) prFlashAlpha = 0.0f; + if (prFlashedItem == vIdx && + std::abs(prFlashAlpha - 0.0f) > 0.00001f) + { + prFlashAlpha -= prFlashAlphaAttenInSecs * ImGui::GetIO().DeltaTime; + if (prFlashAlpha < 0.0f) prFlashAlpha = 0.0f; - ImVec4 hov = ImGui::GetStyleColorVec4(ImGuiCol_HeaderHovered); - hov.w = prFlashAlpha; - ImGui::PushStyleColor(ImGuiCol_HeaderHovered, hov); - res = true; - } + ImVec4 hov = ImGui::GetStyleColorVec4(ImGuiCol_HeaderHovered); + hov.w = prFlashAlpha; + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, hov); + res = true; + } - return res; - } + return res; + } - void IGFD::KeyExplorerFeature::prEndFlashItem() - { - ImGui::PopStyleColor(); - } + void IGFD::KeyExplorerFeature::prEndFlashItem() + { + ImGui::PopStyleColor(); + } - void IGFD::KeyExplorerFeature::SetFlashingAttenuationInSeconds(float vAttenValue) - { - prFlashAlphaAttenInSecs = 1.0f / ImMax(vAttenValue, 0.01f); - } + void IGFD::KeyExplorerFeature::SetFlashingAttenuationInSeconds(float vAttenValue) + { + prFlashAlphaAttenInSecs = 1.0f / ImMax(vAttenValue, 0.01f); + } #endif // USE_EXPLORATION_BY_KEYS - ///////////////////////////////////////////////////////////////////////////////////// - //// FILE DIALOG CONSTRUCTOR / DESTRUCTOR /////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + //// FILE DIALOG CONSTRUCTOR / DESTRUCTOR /////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// - IGFD::FileDialog::FileDialog() : BookMarkFeature(), KeyExplorerFeature(), ThumbnailFeature() {DpiScale=1.0f; singleClickSel=false; mobileMode=false;} - IGFD::FileDialog::~FileDialog() = default; + IGFD::FileDialog::FileDialog() : BookMarkFeature(), KeyExplorerFeature(), ThumbnailFeature() {DpiScale=1.0f; singleClickSel=false; mobileMode=false;} + IGFD::FileDialog::~FileDialog() = default; - ////////////////////////////////////////////////////////////////////////////////////////////////// - ///// FILE DIALOG STANDARD DIALOG //////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////// + ///// FILE DIALOG STANDARD DIALOG //////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////// - // path and fileNameExt can be specified - void IGFD::FileDialog::OpenDialog( - const std::string& vKey, - const std::string& vTitle, - const char* vFilters, - const std::string& vPath, - const std::string& vFileName, - const int& vCountSelectionMax, - UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags, + // path and fileNameExt can be specified + void IGFD::FileDialog::OpenDialog( + const std::string& vKey, + const std::string& vTitle, + const char* vFilters, + const std::string& vPath, + const std::string& vFileName, + const int& vCountSelectionMax, + UserDatas vUserDatas, + ImGuiFileDialogFlags vFlags, SelectFun vSelectFun) - { - if (prFileDialogInternal.puShowDialog) // if already opened, quit - return; + { + if (prFileDialogInternal.puShowDialog) // if already opened, quit + return; - prFileDialogInternal.ResetForNewDialog(); + prFileDialogInternal.ResetForNewDialog(); - prFileDialogInternal.puDLGkey = vKey; - prFileDialogInternal.puDLGtitle = vTitle; - prFileDialogInternal.puDLGuserDatas = vUserDatas; - prFileDialogInternal.puDLGflags = vFlags; + prFileDialogInternal.puDLGkey = vKey; + prFileDialogInternal.puDLGtitle = vTitle; + prFileDialogInternal.puDLGuserDatas = vUserDatas; + prFileDialogInternal.puDLGflags = vFlags; prFileDialogInternal.puDLGselFun = vSelectFun; - prFileDialogInternal.puDLGoptionsPane = nullptr; - prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f; - prFileDialogInternal.puDLGmodal = false; + prFileDialogInternal.puDLGoptionsPane = nullptr; + prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f; + prFileDialogInternal.puDLGmodal = false; - prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear(); - prFileDialogInternal.puFilterManager.ParseFilters(vFilters); + prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear(); + prFileDialogInternal.puFilterManager.ParseFilters(vFilters); - prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr); - if (vPath.empty()) - prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath(); - else - prFileDialogInternal.puFileManager.puDLGpath = vPath; - prFileDialogInternal.puFileManager.SetCurrentPath(vPath); - prFileDialogInternal.puFileManager.puDLGcountSelectionMax = (size_t)vCountSelectionMax; - prFileDialogInternal.puFileManager.SetDefaultFileName(vFileName); + prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr); + if (vPath.empty()) + prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath(); + else + prFileDialogInternal.puFileManager.puDLGpath = vPath; + prFileDialogInternal.puFileManager.SetCurrentPath(vPath); + prFileDialogInternal.puFileManager.puDLGcountSelectionMax = (size_t)vCountSelectionMax; + prFileDialogInternal.puFileManager.SetDefaultFileName(vFileName); - prFileDialogInternal.puFileManager.ClearAll(); - - prFileDialogInternal.puShowDialog = true; // open dialog - } + prFileDialogInternal.puFileManager.ClearAll(); + + prFileDialogInternal.puShowDialog = true; // open dialog + } - // path and filename are obtained from filePathName - void IGFD::FileDialog::OpenDialog( - const std::string& vKey, - const std::string& vTitle, - const char* vFilters, - const std::string& vFilePathName, - const int& vCountSelectionMax, - UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags, + // path and filename are obtained from filePathName + void IGFD::FileDialog::OpenDialog( + const std::string& vKey, + const std::string& vTitle, + const char* vFilters, + const std::string& vFilePathName, + const int& vCountSelectionMax, + UserDatas vUserDatas, + ImGuiFileDialogFlags vFlags, SelectFun vSelectFun) - { - if (prFileDialogInternal.puShowDialog) // if already opened, quit - return; + { + if (prFileDialogInternal.puShowDialog) // if already opened, quit + return; - prFileDialogInternal.ResetForNewDialog(); + prFileDialogInternal.ResetForNewDialog(); - prFileDialogInternal.puDLGkey = vKey; - prFileDialogInternal.puDLGtitle = vTitle; - prFileDialogInternal.puDLGoptionsPane = nullptr; - prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f; - prFileDialogInternal.puDLGuserDatas = vUserDatas; - prFileDialogInternal.puDLGflags = vFlags; + prFileDialogInternal.puDLGkey = vKey; + prFileDialogInternal.puDLGtitle = vTitle; + prFileDialogInternal.puDLGoptionsPane = nullptr; + prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f; + prFileDialogInternal.puDLGuserDatas = vUserDatas; + prFileDialogInternal.puDLGflags = vFlags; prFileDialogInternal.puDLGselFun = vSelectFun; - prFileDialogInternal.puDLGmodal = false; + prFileDialogInternal.puDLGmodal = false; - auto ps = IGFD::Utils::ParsePathFileName(vFilePathName); - if (ps.isOk) - { - prFileDialogInternal.puFileManager.puDLGpath = ps.path; - prFileDialogInternal.puFileManager.SetDefaultFileName(""); - prFileDialogInternal.puFilterManager.puDLGdefaultExt = "." + ps.ext; - } - else - { - prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath(); - prFileDialogInternal.puFileManager.SetDefaultFileName(""); - prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear(); - } + auto ps = IGFD::Utils::ParsePathFileName(vFilePathName); + if (ps.isOk) + { + prFileDialogInternal.puFileManager.puDLGpath = ps.path; + prFileDialogInternal.puFileManager.SetDefaultFileName(""); + prFileDialogInternal.puFilterManager.puDLGdefaultExt = "." + ps.ext; + } + else + { + prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath(); + prFileDialogInternal.puFileManager.SetDefaultFileName(""); + prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear(); + } - prFileDialogInternal.puFilterManager.ParseFilters(vFilters); - prFileDialogInternal.puFilterManager.SetSelectedFilterWithExt( - prFileDialogInternal.puFilterManager.puDLGdefaultExt); - - prFileDialogInternal.puFileManager.SetCurrentPath(prFileDialogInternal.puFileManager.puDLGpath); + prFileDialogInternal.puFilterManager.ParseFilters(vFilters); + prFileDialogInternal.puFilterManager.SetSelectedFilterWithExt( + prFileDialogInternal.puFilterManager.puDLGdefaultExt); + + prFileDialogInternal.puFileManager.SetCurrentPath(prFileDialogInternal.puFileManager.puDLGpath); - prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr); - prFileDialogInternal.puFileManager.puDLGcountSelectionMax = vCountSelectionMax; //-V101 + prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr); + prFileDialogInternal.puFileManager.puDLGcountSelectionMax = vCountSelectionMax; //-V101 - prFileDialogInternal.puFileManager.ClearAll(); - - prFileDialogInternal.puShowDialog = true; - } + prFileDialogInternal.puFileManager.ClearAll(); + + prFileDialogInternal.puShowDialog = true; + } - // with pane - // path and fileNameExt can be specified - void IGFD::FileDialog::OpenDialog( - const std::string& vKey, - const std::string& vTitle, - const char* vFilters, - const std::string& vPath, - const std::string& vFileName, - const PaneFun& vSidePane, - const float& vSidePaneWidth, - const int& vCountSelectionMax, - UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags, + // with pane + // path and fileNameExt can be specified + void IGFD::FileDialog::OpenDialog( + const std::string& vKey, + const std::string& vTitle, + const char* vFilters, + const std::string& vPath, + const std::string& vFileName, + const PaneFun& vSidePane, + const float& vSidePaneWidth, + const int& vCountSelectionMax, + UserDatas vUserDatas, + ImGuiFileDialogFlags vFlags, SelectFun vSelectFun) - { - if (prFileDialogInternal.puShowDialog) // if already opened, quit - return; + { + if (prFileDialogInternal.puShowDialog) // if already opened, quit + return; - prFileDialogInternal.ResetForNewDialog(); + prFileDialogInternal.ResetForNewDialog(); - prFileDialogInternal.puDLGkey = vKey; - prFileDialogInternal.puDLGtitle = vTitle; - prFileDialogInternal.puDLGuserDatas = vUserDatas; - prFileDialogInternal.puDLGflags = vFlags; + prFileDialogInternal.puDLGkey = vKey; + prFileDialogInternal.puDLGtitle = vTitle; + prFileDialogInternal.puDLGuserDatas = vUserDatas; + prFileDialogInternal.puDLGflags = vFlags; prFileDialogInternal.puDLGselFun = vSelectFun; - prFileDialogInternal.puDLGoptionsPane = vSidePane; - prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth; - prFileDialogInternal.puDLGmodal = false; + prFileDialogInternal.puDLGoptionsPane = vSidePane; + prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth; + prFileDialogInternal.puDLGmodal = false; - prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear(); - prFileDialogInternal.puFilterManager.ParseFilters(vFilters); + prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear(); + prFileDialogInternal.puFilterManager.ParseFilters(vFilters); - prFileDialogInternal.puFileManager.puDLGcountSelectionMax = (size_t)vCountSelectionMax; - prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr); - if (vPath.empty()) - prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath(); - else - prFileDialogInternal.puFileManager.puDLGpath = vPath; + prFileDialogInternal.puFileManager.puDLGcountSelectionMax = (size_t)vCountSelectionMax; + prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr); + if (vPath.empty()) + prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath(); + else + prFileDialogInternal.puFileManager.puDLGpath = vPath; - prFileDialogInternal.puFileManager.SetCurrentPath(prFileDialogInternal.puFileManager.puDLGpath); + prFileDialogInternal.puFileManager.SetCurrentPath(prFileDialogInternal.puFileManager.puDLGpath); - prFileDialogInternal.puFileManager.SetDefaultFileName(vFileName); + prFileDialogInternal.puFileManager.SetDefaultFileName(vFileName); - prFileDialogInternal.puFileManager.ClearAll(); - - prFileDialogInternal.puShowDialog = true; // open dialog - } + prFileDialogInternal.puFileManager.ClearAll(); + + prFileDialogInternal.puShowDialog = true; // open dialog + } - // with pane - // path and filename are obtained from filePathName - void IGFD::FileDialog::OpenDialog( - const std::string& vKey, - const std::string& vTitle, - const char* vFilters, - const std::string& vFilePathName, - const PaneFun& vSidePane, - const float& vSidePaneWidth, - const int& vCountSelectionMax, - UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags, + // with pane + // path and filename are obtained from filePathName + void IGFD::FileDialog::OpenDialog( + const std::string& vKey, + const std::string& vTitle, + const char* vFilters, + const std::string& vFilePathName, + const PaneFun& vSidePane, + const float& vSidePaneWidth, + const int& vCountSelectionMax, + UserDatas vUserDatas, + ImGuiFileDialogFlags vFlags, SelectFun vSelectFun) - { - if (prFileDialogInternal.puShowDialog) // if already opened, quit - return; + { + if (prFileDialogInternal.puShowDialog) // if already opened, quit + return; - prFileDialogInternal.ResetForNewDialog(); + prFileDialogInternal.ResetForNewDialog(); - prFileDialogInternal.puDLGkey = vKey; - prFileDialogInternal.puDLGtitle = vTitle; - prFileDialogInternal.puDLGoptionsPane = vSidePane; - prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth; - prFileDialogInternal.puDLGuserDatas = vUserDatas; - prFileDialogInternal.puDLGflags = vFlags; + prFileDialogInternal.puDLGkey = vKey; + prFileDialogInternal.puDLGtitle = vTitle; + prFileDialogInternal.puDLGoptionsPane = vSidePane; + prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth; + prFileDialogInternal.puDLGuserDatas = vUserDatas; + prFileDialogInternal.puDLGflags = vFlags; prFileDialogInternal.puDLGselFun = vSelectFun; - prFileDialogInternal.puDLGmodal = false; + prFileDialogInternal.puDLGmodal = false; - auto ps = IGFD::Utils::ParsePathFileName(vFilePathName); - if (ps.isOk) - { - prFileDialogInternal.puFileManager.puDLGpath = ps.path; - prFileDialogInternal.puFileManager.SetDefaultFileName(vFilePathName); - prFileDialogInternal.puFilterManager.puDLGdefaultExt = "." + ps.ext; - } - else - { - prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath(); - prFileDialogInternal.puFileManager.SetDefaultFileName(""); - prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear(); - } + auto ps = IGFD::Utils::ParsePathFileName(vFilePathName); + if (ps.isOk) + { + prFileDialogInternal.puFileManager.puDLGpath = ps.path; + prFileDialogInternal.puFileManager.SetDefaultFileName(vFilePathName); + prFileDialogInternal.puFilterManager.puDLGdefaultExt = "." + ps.ext; + } + else + { + prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath(); + prFileDialogInternal.puFileManager.SetDefaultFileName(""); + prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear(); + } - prFileDialogInternal.puFileManager.SetCurrentPath(prFileDialogInternal.puFileManager.puDLGpath); + prFileDialogInternal.puFileManager.SetCurrentPath(prFileDialogInternal.puFileManager.puDLGpath); - prFileDialogInternal.puFileManager.puDLGcountSelectionMax = vCountSelectionMax; //-V101 - prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr); - prFileDialogInternal.puFilterManager.ParseFilters(vFilters); - prFileDialogInternal.puFilterManager.SetSelectedFilterWithExt( - prFileDialogInternal.puFilterManager.puDLGdefaultExt); + prFileDialogInternal.puFileManager.puDLGcountSelectionMax = vCountSelectionMax; //-V101 + prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr); + prFileDialogInternal.puFilterManager.ParseFilters(vFilters); + prFileDialogInternal.puFilterManager.SetSelectedFilterWithExt( + prFileDialogInternal.puFilterManager.puDLGdefaultExt); - prFileDialogInternal.puFileManager.ClearAll(); + prFileDialogInternal.puFileManager.ClearAll(); - prFileDialogInternal.puShowDialog = true; - } + prFileDialogInternal.puShowDialog = true; + } - ////////////////////////////////////////////////////////////////////////////////////////////////// - ///// FILE DIALOG MODAL DIALOG /////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////// + ///// FILE DIALOG MODAL DIALOG /////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////// - void IGFD::FileDialog::OpenModal( - const std::string& vKey, - const std::string& vTitle, - const char* vFilters, - const std::string& vPath, - const std::string& vFileName, - const int& vCountSelectionMax, - UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags, + void IGFD::FileDialog::OpenModal( + const std::string& vKey, + const std::string& vTitle, + const char* vFilters, + const std::string& vPath, + const std::string& vFileName, + const int& vCountSelectionMax, + UserDatas vUserDatas, + ImGuiFileDialogFlags vFlags, SelectFun vSelectFun) - { - if (prFileDialogInternal.puShowDialog) // if already opened, quit - return; + { + if (prFileDialogInternal.puShowDialog) // if already opened, quit + return; - OpenDialog( - vKey, vTitle, vFilters, - vPath, vFileName, - vCountSelectionMax, vUserDatas, vFlags, vSelectFun); + OpenDialog( + vKey, vTitle, vFilters, + vPath, vFileName, + vCountSelectionMax, vUserDatas, vFlags, vSelectFun); - prFileDialogInternal.puDLGmodal = true; - } + prFileDialogInternal.puDLGmodal = true; + } - void IGFD::FileDialog::OpenModal( - const std::string& vKey, - const std::string& vTitle, - const char* vFilters, - const std::string& vFilePathName, - const int& vCountSelectionMax, - UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags, + void IGFD::FileDialog::OpenModal( + const std::string& vKey, + const std::string& vTitle, + const char* vFilters, + const std::string& vFilePathName, + const int& vCountSelectionMax, + UserDatas vUserDatas, + ImGuiFileDialogFlags vFlags, SelectFun vSelectFun) - { - if (prFileDialogInternal.puShowDialog) // if already opened, quit - return; + { + if (prFileDialogInternal.puShowDialog) // if already opened, quit + return; - OpenDialog( - vKey, vTitle, vFilters, - vFilePathName, - vCountSelectionMax, vUserDatas, vFlags, vSelectFun); + OpenDialog( + vKey, vTitle, vFilters, + vFilePathName, + vCountSelectionMax, vUserDatas, vFlags, vSelectFun); - prFileDialogInternal.puDLGmodal = true; - } + prFileDialogInternal.puDLGmodal = true; + } - // with pane - // path and fileNameExt can be specified - void IGFD::FileDialog::OpenModal( - const std::string& vKey, - const std::string& vTitle, - const char* vFilters, - const std::string& vPath, - const std::string& vFileName, - const PaneFun& vSidePane, - const float& vSidePaneWidth, - const int& vCountSelectionMax, - UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags, + // with pane + // path and fileNameExt can be specified + void IGFD::FileDialog::OpenModal( + const std::string& vKey, + const std::string& vTitle, + const char* vFilters, + const std::string& vPath, + const std::string& vFileName, + const PaneFun& vSidePane, + const float& vSidePaneWidth, + const int& vCountSelectionMax, + UserDatas vUserDatas, + ImGuiFileDialogFlags vFlags, SelectFun vSelectFun) - { - if (prFileDialogInternal.puShowDialog) // if already opened, quit - return; + { + if (prFileDialogInternal.puShowDialog) // if already opened, quit + return; - OpenDialog( - vKey, vTitle, vFilters, - vPath, vFileName, - vSidePane, vSidePaneWidth, - vCountSelectionMax, vUserDatas, vFlags, vSelectFun); + OpenDialog( + vKey, vTitle, vFilters, + vPath, vFileName, + vSidePane, vSidePaneWidth, + vCountSelectionMax, vUserDatas, vFlags, vSelectFun); - prFileDialogInternal.puDLGmodal = true; - } + prFileDialogInternal.puDLGmodal = true; + } - // with pane - // path and filename are obtained from filePathName - void IGFD::FileDialog::OpenModal( - const std::string& vKey, - const std::string& vTitle, - const char* vFilters, - const std::string& vFilePathName, - const PaneFun& vSidePane, - const float& vSidePaneWidth, - const int& vCountSelectionMax, - UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags, + // with pane + // path and filename are obtained from filePathName + void IGFD::FileDialog::OpenModal( + const std::string& vKey, + const std::string& vTitle, + const char* vFilters, + const std::string& vFilePathName, + const PaneFun& vSidePane, + const float& vSidePaneWidth, + const int& vCountSelectionMax, + UserDatas vUserDatas, + ImGuiFileDialogFlags vFlags, SelectFun vSelectFun) - { - if (prFileDialogInternal.puShowDialog) // if already opened, quit - return; + { + if (prFileDialogInternal.puShowDialog) // if already opened, quit + return; - OpenDialog( - vKey, vTitle, vFilters, - vFilePathName, - vSidePane, vSidePaneWidth, - vCountSelectionMax, vUserDatas, vFlags, vSelectFun); + OpenDialog( + vKey, vTitle, vFilters, + vFilePathName, + vSidePane, vSidePaneWidth, + vCountSelectionMax, vUserDatas, vFlags, vSelectFun); - prFileDialogInternal.puDLGmodal = true; - } + prFileDialogInternal.puDLGmodal = true; + } - ////////////////////////////////////////////////////////////////////////////////////////////////// - ///// FILE DIALOG DISPLAY FUNCTION /////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////// + ///// FILE DIALOG DISPLAY FUNCTION /////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////// - bool IGFD::FileDialog::Display(const std::string& vKey, ImGuiWindowFlags vFlags, ImVec2 vMinSize, ImVec2 vMaxSize) - { - bool res = false; + bool IGFD::FileDialog::Display(const std::string& vKey, ImGuiWindowFlags vFlags, ImVec2 vMinSize, ImVec2 vMaxSize) + { + bool res = false; - if (prFileDialogInternal.puShowDialog && prFileDialogInternal.puDLGkey == vKey) - { - if (prFileDialogInternal.puUseCustomLocale) - setlocale(prFileDialogInternal.puLocaleCategory, prFileDialogInternal.puLocaleBegin.c_str()); + if (prFileDialogInternal.puShowDialog && prFileDialogInternal.puDLGkey == vKey) + { + if (prFileDialogInternal.puUseCustomLocale) + setlocale(prFileDialogInternal.puLocaleCategory, prFileDialogInternal.puLocaleBegin.c_str()); - auto& fdFile = prFileDialogInternal.puFileManager; - auto& fdFilter = prFileDialogInternal.puFilterManager; + auto& fdFile = prFileDialogInternal.puFileManager; + auto& fdFilter = prFileDialogInternal.puFilterManager; - static ImGuiWindowFlags flags; + static ImGuiWindowFlags flags; - // to be sure than only one dialog is displayed per frame - ImGuiContext& g = *GImGui; - if (g.FrameCount == prFileDialogInternal.puLastImGuiFrameCount) // one instance was displayed this frame before for this key +> quit - return res; - prFileDialogInternal.puLastImGuiFrameCount = g.FrameCount; // mark this instance as used this frame + // to be sure than only one dialog is displayed per frame + ImGuiContext& g = *GImGui; + if (g.FrameCount == prFileDialogInternal.puLastImGuiFrameCount) // one instance was displayed this frame before for this key +> quit + return res; + prFileDialogInternal.puLastImGuiFrameCount = g.FrameCount; // mark this instance as used this frame - std::string name = prFileDialogInternal.puDLGtitle + "##" + prFileDialogInternal.puDLGkey; - if (prFileDialogInternal.puName != name) - { - fdFile.ClearComposer(); - fdFile.ClearFileLists(); - flags = vFlags; - } + std::string name = prFileDialogInternal.puDLGtitle + "##" + prFileDialogInternal.puDLGkey; + if (prFileDialogInternal.puName != name) + { + fdFile.ClearComposer(); + fdFile.ClearFileLists(); + flags = vFlags; + } - NewFrame(); + NewFrame(); #ifdef IMGUI_HAS_VIEWPORT - if (!ImGui::GetIO().ConfigViewportsNoDecoration) - { - // https://github.com/ocornut/imgui/issues/4534 - ImGuiWindowClass window_class; - window_class.ViewportFlagsOverrideClear = ImGuiViewportFlags_NoDecoration; - ImGui::SetNextWindowClass(&window_class); - } + if (!ImGui::GetIO().ConfigViewportsNoDecoration) + { + // https://github.com/ocornut/imgui/issues/4534 + ImGuiWindowClass window_class; + window_class.ViewportFlagsOverrideClear = ImGuiViewportFlags_NoDecoration; + ImGui::SetNextWindowClass(&window_class); + } #endif // IMGUI_HAS_VIEWPORT - ImGui::SetNextWindowSizeConstraints(vMinSize, vMaxSize); + ImGui::SetNextWindowSizeConstraints(vMinSize, vMaxSize); - bool beg = false; - if (prFileDialogInternal.puDLGmodal && - !prFileDialogInternal.puOkResultToConfirm) // disable modal because the confirm dialog for overwrite is a new modal - { - ImGui::OpenPopup(name.c_str()); - beg = ImGui::BeginPopupModal(name.c_str(), (bool*)nullptr, - flags | ImGuiWindowFlags_NoScrollbar); - } - else - { - beg = ImGui::Begin(name.c_str(), (bool*)nullptr, flags | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoDocking); - } - if (beg) - { + bool beg = false; + if (prFileDialogInternal.puDLGmodal && + !prFileDialogInternal.puOkResultToConfirm) // disable modal because the confirm dialog for overwrite is a new modal + { + ImGui::OpenPopup(name.c_str()); + beg = ImGui::BeginPopupModal(name.c_str(), (bool*)nullptr, + flags | ImGuiWindowFlags_NoScrollbar); + } + else + { + beg = ImGui::Begin(name.c_str(), (bool*)nullptr, flags | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoDocking); + } + if (beg) + { ImGui::SetWindowPos(ImVec2((ImGui::GetMainViewport()->Size.x-ImGui::GetWindowWidth())*0.5f,(ImGui::GetMainViewport()->Size.y-ImGui::GetWindowHeight())*0.5f)); if (ImGui::GetWindowSize().xViewport->Idx != 0) - flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar; - else - flags = vFlags; - } + // if decoration is enabled we disable the resizing feature of imgui for avoid crash with SDL2 and GLFW3 + if (ImGui::GetIO().ConfigViewportsNoDecoration) + { + flags = vFlags; + } + else + { + auto win = ImGui::GetCurrentWindowRead(); + if (win->Viewport->Idx != 0) + flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar; + else + flags = vFlags; + } #endif // IMGUI_HAS_VIEWPORT - prFileDialogInternal.puName = name; //-V820 - puAnyWindowsHovered |= ImGui::IsWindowHovered(); + prFileDialogInternal.puName = name; //-V820 + puAnyWindowsHovered |= ImGui::IsWindowHovered(); - if (fdFile.puDLGpath.empty()) - fdFile.puDLGpath = "."; // defaut path is '.' + if (fdFile.puDLGpath.empty()) + fdFile.puDLGpath = "."; // defaut path is '.' - fdFilter.SetDefaultFilterIfNotDefined(); + fdFilter.SetDefaultFilterIfNotDefined(); - // init list of files - if (fdFile.IsFileListEmpty() && !fdFile.puShowDrives && !fdFile.fileListActuallyEmpty) - { - IGFD::Utils::ReplaceString(fdFile.puDLGDefaultFileName, fdFile.puDLGpath, ""); // local path - if (!fdFile.puDLGDefaultFileName.empty()) - { - fdFile.SetDefaultFileName(fdFile.puDLGDefaultFileName); - fdFilter.SetSelectedFilterWithExt(fdFilter.puDLGdefaultExt); - } else if (fdFile.puDLGDirectoryMode) { // directory mode - fdFile.SetDefaultFileName("."); + // init list of files + if (fdFile.IsFileListEmpty() && !fdFile.puShowDrives && !fdFile.fileListActuallyEmpty) + { + IGFD::Utils::ReplaceString(fdFile.puDLGDefaultFileName, fdFile.puDLGpath, ""); // local path + if (!fdFile.puDLGDefaultFileName.empty()) + { + fdFile.SetDefaultFileName(fdFile.puDLGDefaultFileName); + fdFilter.SetSelectedFilterWithExt(fdFilter.puDLGdefaultExt); + } else if (fdFile.puDLGDirectoryMode) { // directory mode + fdFile.SetDefaultFileName("."); } logV("IGFD: fdFile.IsFileListEmpty() and !fdFile.puShowDrives"); - fdFile.ScanDir(prFileDialogInternal, fdFile.puDLGpath); - } + fdFile.ScanDir(prFileDialogInternal, fdFile.puDLGpath); + } - // draw dialog parts - prDrawHeader(); // bookmark, directory, path - res = prDrawContent(); // bookmark, files view, side pane - bool res1 = prDrawFooter(); // file field, filter combobox, ok/cancel buttons + // draw dialog parts + prDrawHeader(); // bookmark, directory, path + res = prDrawContent(); // bookmark, files view, side pane + bool res1 = prDrawFooter(); // file field, filter combobox, ok/cancel buttons if (!res) res=res1; - EndFrame(); + EndFrame(); - // for display in dialog center, the confirm to overwrite dlg - prFileDialogInternal.puDialogCenterPos = ImGui::GetCurrentWindowRead()->ContentRegionRect.GetCenter(); + // for display in dialog center, the confirm to overwrite dlg + prFileDialogInternal.puDialogCenterPos = ImGui::GetCurrentWindowRead()->ContentRegionRect.GetCenter(); - // when the confirm to overwrite dialog will appear we need to - // disable the modal mode of the main file dialog - // see prOkResultToConfirm under - if (prFileDialogInternal.puDLGmodal && - !prFileDialogInternal.puOkResultToConfirm) - ImGui::EndPopup(); - } + // when the confirm to overwrite dialog will appear we need to + // disable the modal mode of the main file dialog + // see prOkResultToConfirm under + if (prFileDialogInternal.puDLGmodal && + !prFileDialogInternal.puOkResultToConfirm) + ImGui::EndPopup(); + } - // same things here regarding prOkResultToConfirm - if (!prFileDialogInternal.puDLGmodal || prFileDialogInternal.puOkResultToConfirm) - ImGui::End(); + // same things here regarding prOkResultToConfirm + if (!prFileDialogInternal.puDLGmodal || prFileDialogInternal.puOkResultToConfirm) + ImGui::End(); - // confirm the result and show the confirm to overwrite dialog if needed - res = prConfirm_Or_OpenOverWriteFileDialog_IfNeeded(res, vFlags); - - if (prFileDialogInternal.puUseCustomLocale) - setlocale(prFileDialogInternal.puLocaleCategory, prFileDialogInternal.puLocaleEnd.c_str()); - } + // confirm the result and show the confirm to overwrite dialog if needed + res = prConfirm_Or_OpenOverWriteFileDialog_IfNeeded(res, vFlags); + + if (prFileDialogInternal.puUseCustomLocale) + setlocale(prFileDialogInternal.puLocaleCategory, prFileDialogInternal.puLocaleEnd.c_str()); + } - return res; - } + return res; + } - void IGFD::FileDialog::NewFrame() - { - prFileDialogInternal.NewFrame(); - NewThumbnailFrame(prFileDialogInternal); - } - - void IGFD::FileDialog::EndFrame() - { - EndThumbnailFrame(prFileDialogInternal); - prFileDialogInternal.EndFrame(); - - } - void IGFD::FileDialog::QuitFrame() - { - QuitThumbnailFrame(prFileDialogInternal); - } + void IGFD::FileDialog::NewFrame() + { + prFileDialogInternal.NewFrame(); + NewThumbnailFrame(prFileDialogInternal); + } + + void IGFD::FileDialog::EndFrame() + { + EndThumbnailFrame(prFileDialogInternal); + prFileDialogInternal.EndFrame(); + + } + void IGFD::FileDialog::QuitFrame() + { + QuitThumbnailFrame(prFileDialogInternal); + } - void IGFD::FileDialog::prDrawHeader() - { + void IGFD::FileDialog::prDrawHeader() + { #ifdef USE_BOOKMARK - prDrawBookmarkButton(); - ImGui::SameLine(); + prDrawBookmarkButton(); + ImGui::SameLine(); #endif // USE_BOOKMARK - prFileDialogInternal.puFileManager.DrawDirectoryCreation(prFileDialogInternal); - ImGui::SameLine(); - ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); - ImGui::SameLine(); - prFileDialogInternal.puFileManager.DrawPathComposer(prFileDialogInternal); + prFileDialogInternal.puFileManager.DrawDirectoryCreation(prFileDialogInternal); + ImGui::SameLine(); + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); + ImGui::SameLine(); + prFileDialogInternal.puFileManager.DrawPathComposer(prFileDialogInternal); #ifdef USE_THUMBNAILS - if (!(prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DisableThumbnailMode)) - { - prDrawDisplayModeToolBar(); - ImGui::SameLine(); - ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); - ImGui::SameLine(); - } + if (!(prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DisableThumbnailMode)) + { + prDrawDisplayModeToolBar(); + ImGui::SameLine(); + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); + ImGui::SameLine(); + } #endif // USE_THUMBNAILS - prFileDialogInternal.puSearchManager.DrawSearchBar(prFileDialogInternal); - } + prFileDialogInternal.puSearchManager.DrawSearchBar(prFileDialogInternal); + } - bool IGFD::FileDialog::prDrawContent() - { + bool IGFD::FileDialog::prDrawContent() + { bool escape = false; - ImVec2 size = ImGui::GetContentRegionAvail() - ImVec2(0.0f, prFileDialogInternal.puFooterHeight); + ImVec2 size = ImGui::GetContentRegionAvail() - ImVec2(0.0f, prFileDialogInternal.puFooterHeight); #ifdef USE_BOOKMARK - if (prBookmarkPaneShown) - { - //size.x -= prBookmarkWidth; - float otherWidth = size.x - prBookmarkWidth; - ImGui::PushID("##splitterbookmark"); - IGFD::Utils::Splitter(true, 4.0f, - &prBookmarkWidth, &otherWidth, 10.0f, - 10.0f + prFileDialogInternal.puDLGoptionsPaneWidth, size.y); - ImGui::PopID(); - size.x -= otherWidth; - prDrawBookmarkPane(prFileDialogInternal, size); - ImGui::SameLine(); - } + if (prBookmarkPaneShown) + { + //size.x -= prBookmarkWidth; + float otherWidth = size.x - prBookmarkWidth; + ImGui::PushID("##splitterbookmark"); + IGFD::Utils::Splitter(true, 4.0f, + &prBookmarkWidth, &otherWidth, 10.0f, + 10.0f + prFileDialogInternal.puDLGoptionsPaneWidth, size.y); + ImGui::PopID(); + size.x -= otherWidth; + prDrawBookmarkPane(prFileDialogInternal, size); + ImGui::SameLine(); + } #endif // USE_BOOKMARK - size.x = ImGui::GetContentRegionAvail().x - prFileDialogInternal.puDLGoptionsPaneWidth; + size.x = ImGui::GetContentRegionAvail().x - prFileDialogInternal.puDLGoptionsPaneWidth; - if (prFileDialogInternal.puDLGoptionsPane) - { - ImGui::PushID("##splittersidepane"); - IGFD::Utils::Splitter(true, 4.0f, &size.x, &prFileDialogInternal.puDLGoptionsPaneWidth, 10.0f, 10.0f, size.y); - ImGui::PopID(); - } + if (prFileDialogInternal.puDLGoptionsPane) + { + ImGui::PushID("##splittersidepane"); + IGFD::Utils::Splitter(true, 4.0f, &size.x, &prFileDialogInternal.puDLGoptionsPaneWidth, 10.0f, 10.0f, size.y); + ImGui::PopID(); + } #ifdef USE_THUMBNAILS - if (prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DisableThumbnailMode) - { - prDrawFileListView(size); - } - else - { - switch (prDisplayMode) - { - case DisplayModeEnum::FILE_LIST: - prDrawFileListView(size); - break; - case DisplayModeEnum::THUMBNAILS_LIST: - prDrawThumbnailsListView(size); - break; - case DisplayModeEnum::THUMBNAILS_GRID: - prDrawThumbnailsGridView(size); - } - } + if (prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DisableThumbnailMode) + { + prDrawFileListView(size); + } + else + { + switch (prDisplayMode) + { + case DisplayModeEnum::FILE_LIST: + prDrawFileListView(size); + break; + case DisplayModeEnum::THUMBNAILS_LIST: + prDrawThumbnailsListView(size); + break; + case DisplayModeEnum::THUMBNAILS_GRID: + prDrawThumbnailsGridView(size); + } + } #else - escape = prDrawFileListView(size); + escape = prDrawFileListView(size); #endif // USE_THUMBNAILS - if (prFileDialogInternal.puDLGoptionsPane) - { - prDrawSidePane(size.y); - } + if (prFileDialogInternal.puDLGoptionsPane) + { + prDrawSidePane(size.y); + } return escape; - } + } - bool IGFD::FileDialog::prDrawFooter() - { - auto& fdFile = prFileDialogInternal.puFileManager; - - float posY = ImGui::GetCursorPos().y; // height of last bar calc + bool IGFD::FileDialog::prDrawFooter() + { + auto& fdFile = prFileDialogInternal.puFileManager; + + float posY = ImGui::GetCursorPos().y; // height of last bar calc - if (!fdFile.puDLGDirectoryMode) - ImGui::Text(fileNameString); - else // directory chooser - ImGui::Text(dirNameString); + if (!fdFile.puDLGDirectoryMode) + ImGui::Text(fileNameString); + else // directory chooser + ImGui::Text(dirNameString); - ImGui::SameLine(); + ImGui::SameLine(); - // Input file fields - float width = ImGui::GetContentRegionAvail().x; + // Input file fields + float width = ImGui::GetContentRegionAvail().x; // fix this! fix this! fix this! - if (!fdFile.puDLGDirectoryMode) - width -= FILTER_COMBO_WIDTH*DpiScale; - ImGui::PushItemWidth(width); - ImGui::InputText("##FileName", fdFile.puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER); - if (ImGui::GetItemID() == ImGui::GetActiveID()) - prFileDialogInternal.puFileInputIsActive = true; - ImGui::PopItemWidth(); + if (!fdFile.puDLGDirectoryMode) + width -= FILTER_COMBO_WIDTH*DpiScale; + ImGui::PushItemWidth(width); + ImGui::InputText("##FileName", fdFile.puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER); + if (ImGui::GetItemID() == ImGui::GetActiveID()) + prFileDialogInternal.puFileInputIsActive = true; + ImGui::PopItemWidth(); - // combobox of filters - prFileDialogInternal.puFilterManager.DrawFilterComboBox(prFileDialogInternal); + // combobox of filters + prFileDialogInternal.puFilterManager.DrawFilterComboBox(prFileDialogInternal); - bool res = false; + bool res = false; - // OK Button - if (prFileDialogInternal.puCanWeContinue && strlen(fdFile.puFileNameBuffer)) - { - if (IMGUI_BUTTON(okButtonString "##validationdialog")) - { - prFileDialogInternal.puIsOk = true; - res = true; - } + // OK Button + if (prFileDialogInternal.puCanWeContinue && strlen(fdFile.puFileNameBuffer)) + { + if (IMGUI_BUTTON(okButtonString "##validationdialog")) + { + prFileDialogInternal.puIsOk = true; + res = true; + } - ImGui::SameLine(); - } + ImGui::SameLine(); + } - // Cancel Button - if (IMGUI_BUTTON(cancelButtonString "##validationdialog") || - prFileDialogInternal.puNeedToExitDialog) // dialog exit asked - { - prFileDialogInternal.puIsOk = false; - res = true; - } + // Cancel Button + if (IMGUI_BUTTON(cancelButtonString "##validationdialog") || + prFileDialogInternal.puNeedToExitDialog) // dialog exit asked + { + prFileDialogInternal.puIsOk = false; + res = true; + } - prFileDialogInternal.puFooterHeight = ImGui::GetCursorPosY() - posY; + prFileDialogInternal.puFooterHeight = ImGui::GetCursorPosY() - posY; - return res; - } + return res; + } // returns 0 if not break loop, 1 if break loop, 2 if exit dialog - int IGFD::FileDialog::prSelectableItem(int vidx, std::shared_ptr vInfos, bool vSelected, const char* vFmt, ...) - { - if (!vInfos.use_count()) - return 0; + int IGFD::FileDialog::prSelectableItem(int vidx, std::shared_ptr vInfos, bool vSelected, const char* vFmt, ...) + { + if (!vInfos.use_count()) + return 0; - auto& fdi = prFileDialogInternal.puFileManager; + auto& fdi = prFileDialogInternal.puFileManager; - static ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags_AllowDoubleClick | - ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SpanAvailWidth; + static ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags_AllowDoubleClick | + ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SpanAvailWidth; // TODO BUG?! // YES BUG: THIS JUST CRASHED FOR SOME REASON - va_list args; - va_start(args, vFmt); - vsnprintf(fdi.puVariadicBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFmt, args); - va_end(args); + va_list args; + va_start(args, vFmt); + vsnprintf(fdi.puVariadicBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFmt, args); + va_end(args); - float h = /*mobileMode?(ImGui::GetFontSize()+10.0f*DpiScale):*/0.0f; + float h = /*mobileMode?(ImGui::GetFontSize()+10.0f*DpiScale):*/0.0f; #ifdef USE_THUMBNAILS - if (prDisplayMode == DisplayModeEnum::THUMBNAILS_LIST) - h = DisplayMode_ThumbailsList_ImageHeight; + if (prDisplayMode == DisplayModeEnum::THUMBNAILS_LIST) + h = DisplayMode_ThumbailsList_ImageHeight; #endif // USE_THUMBNAILS #ifdef USE_EXPLORATION_BY_KEYS - bool flashed = prBeginFlashItem((size_t)vidx); - bool res = prFlashableSelectable(fdi.puVariadicBuffer, vSelected, selectableFlags, - flashed, ImVec2(-1.0f, h)); - if (flashed) - prEndFlashItem(); + bool flashed = prBeginFlashItem((size_t)vidx); + bool res = prFlashableSelectable(fdi.puVariadicBuffer, vSelected, selectableFlags, + flashed, ImVec2(-1.0f, h)); + if (flashed) + prEndFlashItem(); #else // USE_EXPLORATION_BY_KEYS - (void)vidx; // remove a warnings ofr unused var + (void)vidx; // remove a warnings ofr unused var - bool res = ImGui::Selectable(fdi.puVariadicBuffer, vSelected, selectableFlags, ImVec2(-1.0f, h)); + bool res = ImGui::Selectable(fdi.puVariadicBuffer, vSelected, selectableFlags, ImVec2(-1.0f, h)); #endif // USE_EXPLORATION_BY_KEYS - if (res) - { - if (vInfos->fileType == 'd') - { + if (res) + { + if (vInfos->fileType == 'd') + { bool isSelectingDir=false; - // nav system, selectebale cause open directory or select directory - if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) - { - if (fdi.puDLGDirectoryMode) // directory chooser - { - fdi.SelectFileName(prFileDialogInternal, vInfos); - } - else - { - fdi.puPathClicked = fdi.SelectDirectory(vInfos); + // nav system, selectebale cause open directory or select directory + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) + { + if (fdi.puDLGDirectoryMode) // directory chooser + { + fdi.SelectFileName(prFileDialogInternal, vInfos); + } + else + { + fdi.puPathClicked = fdi.SelectDirectory(vInfos); isSelectingDir=true; - } - } - else // no nav system => classic behavior - { - if (DOUBLE_CLICKED) // 0 -> left mouse button double click - { + } + } + else // no nav system => classic behavior + { + if (DOUBLE_CLICKED) // 0 -> left mouse button double click + { isSelectingDir=true; - fdi.puPathClicked = fdi.SelectDirectory(vInfos); - } - else if (fdi.puDLGDirectoryMode) // directory chooser - { - fdi.SelectFileName(prFileDialogInternal, vInfos); - } - } + fdi.puPathClicked = fdi.SelectDirectory(vInfos); + } + else if (fdi.puDLGDirectoryMode) // directory chooser + { + fdi.SelectFileName(prFileDialogInternal, vInfos); + } + } - return isSelectingDir; // needToBreakTheloop - } - else - { + return isSelectingDir; // needToBreakTheloop + } + else + { if (DOUBLE_CLICKED) { fdi.SelectFileName(prFileDialogInternal, vInfos); prFileDialogInternal.puIsOk = true; return 2; } else { - fdi.SelectFileName(prFileDialogInternal, vInfos); + fdi.SelectFileName(prFileDialogInternal, vInfos); if (prFileDialogInternal.puDLGselFun!=NULL) { std::string argPath; for (auto& i: GetSelection()) { @@ -3969,581 +3969,581 @@ namespace IGFD } } } - } - } + } + } - return 0; - } + return 0; + } - void IGFD::FileDialog::prBeginFileColorIconStyle(std::shared_ptr vFileInfos, bool& vOutShowColor, std::string& vOutStr, ImFont** vOutFont) - { - vOutStr.clear(); - vOutShowColor = false; + void IGFD::FileDialog::prBeginFileColorIconStyle(std::shared_ptr vFileInfos, bool& vOutShowColor, std::string& vOutStr, ImFont** vOutFont) + { + vOutStr.clear(); + vOutShowColor = false; - if (vFileInfos->fileStyle.use_count()) //-V807 //-V522 - { - vOutShowColor = true; + if (vFileInfos->fileStyle.use_count()) //-V807 //-V522 + { + vOutShowColor = true; - *vOutFont = vFileInfos->fileStyle->font; - } + *vOutFont = vFileInfos->fileStyle->font; + } - if (vOutShowColor && !vFileInfos->fileStyle->icon.empty()) vOutStr = vFileInfos->fileStyle->icon; - else if (vFileInfos->fileType == 'd') vOutStr = dirEntryString; - else if (vFileInfos->fileType == 'l') vOutStr = linkEntryString; - else if (vFileInfos->fileType == 'f') vOutStr = fileEntryString; + if (vOutShowColor && !vFileInfos->fileStyle->icon.empty()) vOutStr = vFileInfos->fileStyle->icon; + else if (vFileInfos->fileType == 'd') vOutStr = dirEntryString; + else if (vFileInfos->fileType == 'l') vOutStr = linkEntryString; + else if (vFileInfos->fileType == 'f') vOutStr = fileEntryString; - vOutStr += " " + vFileInfos->fileNameExt; + vOutStr += " " + vFileInfos->fileNameExt; - if (vOutShowColor) - ImGui::PushStyleColor(ImGuiCol_Text, vFileInfos->fileStyle->color); - if (*vOutFont) - ImGui::PushFont(*vOutFont); - } + if (vOutShowColor) + ImGui::PushStyleColor(ImGuiCol_Text, vFileInfos->fileStyle->color); + if (*vOutFont) + ImGui::PushFont(*vOutFont); + } - void IGFD::FileDialog::prEndFileColorIconStyle(const bool& vShowColor, ImFont* vFont) - { - if (vFont) - ImGui::PopFont(); - if (vShowColor) - ImGui::PopStyleColor(); - } + void IGFD::FileDialog::prEndFileColorIconStyle(const bool& vShowColor, ImFont* vFont) + { + if (vFont) + ImGui::PopFont(); + if (vShowColor) + ImGui::PopStyleColor(); + } - bool IGFD::FileDialog::prDrawFileListView(ImVec2 vSize) - { + bool IGFD::FileDialog::prDrawFileListView(ImVec2 vSize) + { bool escape = false; - auto& fdi = prFileDialogInternal.puFileManager; + auto& fdi = prFileDialogInternal.puFileManager; - ImGui::PushID(this); + ImGui::PushID(this); - static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | - ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY | - ImGuiTableFlags_NoHostExtendY + static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | + ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY | + ImGuiTableFlags_NoHostExtendY #ifndef USE_CUSTOM_SORTING_ICON - | ImGuiTableFlags_Sortable + | ImGuiTableFlags_Sortable #endif // USE_CUSTOM_SORTING_ICON - ; - auto listViewID = ImGui::GetID("##FileDialog_fileTable"); - if (ImGui::BeginTableEx("##FileDialog_fileTable", listViewID, 4, flags, vSize, 0.0f)) //-V112 - { - ImGui::TableSetupScrollFreeze(0, 1); // Make header always visible - ImGui::TableSetupColumn(fdi.puHeaderFileName.c_str(), ImGuiTableColumnFlags_WidthStretch, -1, 0); - ImGui::TableSetupColumn(fdi.puHeaderFileType.c_str(), ImGuiTableColumnFlags_WidthFixed | - ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnType) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 1); - ImGui::TableSetupColumn(fdi.puHeaderFileSize.c_str(), ImGuiTableColumnFlags_WidthFixed | - ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnSize) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 2); - ImGui::TableSetupColumn(fdi.puHeaderFileDate.c_str(), ImGuiTableColumnFlags_WidthFixed | - ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnDate) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 3); + ; + auto listViewID = ImGui::GetID("##FileDialog_fileTable"); + if (ImGui::BeginTableEx("##FileDialog_fileTable", listViewID, 4, flags, vSize, 0.0f)) //-V112 + { + ImGui::TableSetupScrollFreeze(0, 1); // Make header always visible + ImGui::TableSetupColumn(fdi.puHeaderFileName.c_str(), ImGuiTableColumnFlags_WidthStretch, -1, 0); + ImGui::TableSetupColumn(fdi.puHeaderFileType.c_str(), ImGuiTableColumnFlags_WidthFixed | + ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnType) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 1); + ImGui::TableSetupColumn(fdi.puHeaderFileSize.c_str(), ImGuiTableColumnFlags_WidthFixed | + ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnSize) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 2); + ImGui::TableSetupColumn(fdi.puHeaderFileDate.c_str(), ImGuiTableColumnFlags_WidthFixed | + ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnDate) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 3); #ifndef USE_CUSTOM_SORTING_ICON - // Sort our data if sort specs have been changed! - if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs()) - { - if (sorts_specs->SpecsDirty && !fdi.IsFileListEmpty()) - { - if (sorts_specs->Specs->ColumnUserID == 0) - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true); - else if (sorts_specs->Specs->ColumnUserID == 1) - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true); - else if (sorts_specs->Specs->ColumnUserID == 2) - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true); - else //if (sorts_specs->Specs->ColumnUserID == 3) => alwayd true for the moment, to uncomment if we add a fourth column - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true); + // Sort our data if sort specs have been changed! + if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs()) + { + if (sorts_specs->SpecsDirty && !fdi.IsFileListEmpty()) + { + if (sorts_specs->Specs->ColumnUserID == 0) + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true); + else if (sorts_specs->Specs->ColumnUserID == 1) + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true); + else if (sorts_specs->Specs->ColumnUserID == 2) + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true); + else //if (sorts_specs->Specs->ColumnUserID == 3) => alwayd true for the moment, to uncomment if we add a fourth column + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true); - sorts_specs->SpecsDirty = false; - } - } + sorts_specs->SpecsDirty = false; + } + } - ImGui::TableHeadersRow(); + ImGui::TableHeadersRow(); #else // USE_CUSTOM_SORTING_ICON - ImGui::TableNextRow(ImGuiTableRowFlags_Headers); - for (int column = 0; column < 4; column++) //-V112 - { - ImGui::TableSetColumnIndex(column); - const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn() - ImGui::PushID(column); - ImGui::TableHeader(column_name); - ImGui::PopID(); - if (ImGui::IsItemClicked()) - { - if (column == 0) - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true); - else if (column == 1) - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true); - else if (column == 2) - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true); - else //if (column == 3) => alwayd true for the moment, to uncomment if we add a fourth column - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true); - } - } + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + for (int column = 0; column < 4; column++) //-V112 + { + ImGui::TableSetColumnIndex(column); + const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn() + ImGui::PushID(column); + ImGui::TableHeader(column_name); + ImGui::PopID(); + if (ImGui::IsItemClicked()) + { + if (column == 0) + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true); + else if (column == 1) + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true); + else if (column == 2) + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true); + else //if (column == 3) => alwayd true for the moment, to uncomment if we add a fourth column + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true); + } + } #endif // USE_CUSTOM_SORTING_ICON - if (!fdi.IsFilteredListEmpty()) - { - std::string _str; - ImFont* _font = nullptr; - bool _showColor = false; - - prFileListClipper.Begin((int)fdi.GetFilteredListSize(), ImGui::GetTextLineHeightWithSpacing()); - while (prFileListClipper.Step()) - { - for (int i = prFileListClipper.DisplayStart; i < prFileListClipper.DisplayEnd; i++) - { - if (i < 0) continue; + if (!fdi.IsFilteredListEmpty()) + { + std::string _str; + ImFont* _font = nullptr; + bool _showColor = false; + + prFileListClipper.Begin((int)fdi.GetFilteredListSize(), ImGui::GetTextLineHeightWithSpacing()); + while (prFileListClipper.Step()) + { + for (int i = prFileListClipper.DisplayStart; i < prFileListClipper.DisplayEnd; i++) + { + if (i < 0) continue; - auto infos = fdi.GetFilteredFileAt((size_t)i); - if (!infos.use_count()) - continue; + auto infos = fdi.GetFilteredFileAt((size_t)i); + if (!infos.use_count()) + continue; - prBeginFileColorIconStyle(infos, _showColor, _str, &_font); - - bool selected = fdi.IsFileNameSelected(infos->fileNameExt); // found + prBeginFileColorIconStyle(infos, _showColor, _str, &_font); + + bool selected = fdi.IsFileNameSelected(infos->fileNameExt); // found - ImGui::TableNextRow(); + ImGui::TableNextRow(); - int needToBreakTheloop = false; + int needToBreakTheloop = false; - if (ImGui::TableNextColumn()) // file name - { + if (ImGui::TableNextColumn()) // file name + { // TODO BUG?!?!?! // YES BUG - needToBreakTheloop = prSelectableItem(i, infos, selected, "%s", _str.c_str()); + needToBreakTheloop = prSelectableItem(i, infos, selected, "%s", _str.c_str()); if (needToBreakTheloop==2) escape=true; - } - if (ImGui::TableNextColumn()) // file type - { - ImGui::Text("%s", infos->fileExt.c_str()); - } - if (ImGui::TableNextColumn()) // file size - { - if (infos->fileType != 'd') - { - ImGui::Text("%s ", infos->formatedFileSize.c_str()); - } - else - { - ImGui::Text("%s",""); - } - } - if (ImGui::TableNextColumn()) // file date + time - { - ImGui::Text("%s", infos->fileModifDate.c_str()); - } + } + if (ImGui::TableNextColumn()) // file type + { + ImGui::Text("%s", infos->fileExt.c_str()); + } + if (ImGui::TableNextColumn()) // file size + { + if (infos->fileType != 'd') + { + ImGui::Text("%s ", infos->formatedFileSize.c_str()); + } + else + { + ImGui::Text("%s",""); + } + } + if (ImGui::TableNextColumn()) // file date + time + { + ImGui::Text("%s", infos->fileModifDate.c_str()); + } - prEndFileColorIconStyle(_showColor, _font); + prEndFileColorIconStyle(_showColor, _font); - if (needToBreakTheloop==1) - break; - } - } - prFileListClipper.End(); - } + if (needToBreakTheloop==1) + break; + } + } + prFileListClipper.End(); + } #ifdef USE_EXPLORATION_BY_KEYS - if (!fdi.puInputPathActivated) - { - prLocateByInputKey(prFileDialogInternal); - prExploreWithkeys(prFileDialogInternal, listViewID); - } + if (!fdi.puInputPathActivated) + { + prLocateByInputKey(prFileDialogInternal); + prExploreWithkeys(prFileDialogInternal, listViewID); + } #endif // USE_EXPLORATION_BY_KEYS - ImGuiContext& g = *GImGui; - if (g.LastActiveId - 1 == listViewID || g.LastActiveId == listViewID) - { - prFileDialogInternal.puFileListViewIsActive = true; - } + ImGuiContext& g = *GImGui; + if (g.LastActiveId - 1 == listViewID || g.LastActiveId == listViewID) + { + prFileDialogInternal.puFileListViewIsActive = true; + } - ImGui::EndTable(); - } + ImGui::EndTable(); + } - ImGui::PopID(); + ImGui::PopID(); return escape; - } + } #ifdef USE_THUMBNAILS - void IGFD::FileDialog::prDrawThumbnailsListView(ImVec2 vSize) - { - auto& fdi = prFileDialogInternal.puFileManager; + void IGFD::FileDialog::prDrawThumbnailsListView(ImVec2 vSize) + { + auto& fdi = prFileDialogInternal.puFileManager; - ImGui::PushID(this); + ImGui::PushID(this); - static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | - ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY | - ImGuiTableFlags_NoHostExtendY + static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | + ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY | + ImGuiTableFlags_NoHostExtendY #ifndef USE_CUSTOM_SORTING_ICON - | ImGuiTableFlags_Sortable + | ImGuiTableFlags_Sortable #endif // USE_CUSTOM_SORTING_ICON - ; - auto listViewID = ImGui::GetID("##FileDialog_fileTable"); - if (ImGui::BeginTableEx("##FileDialog_fileTable", listViewID, 5, flags, vSize, 0.0f)) - { - ImGui::TableSetupScrollFreeze(0, 1); // Make header always visible - ImGui::TableSetupColumn(fdi.puHeaderFileName.c_str(), ImGuiTableColumnFlags_WidthStretch, -1, 0); - ImGui::TableSetupColumn(fdi.puHeaderFileType.c_str(), ImGuiTableColumnFlags_WidthFixed | - ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnType) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 1); - ImGui::TableSetupColumn(fdi.puHeaderFileSize.c_str(), ImGuiTableColumnFlags_WidthFixed | - ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnSize) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 2); - ImGui::TableSetupColumn(fdi.puHeaderFileDate.c_str(), ImGuiTableColumnFlags_WidthFixed | - ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnDate) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 3); - // not needed to have an option for hide the thumbnails since this is why this view is used - ImGui::TableSetupColumn(fdi.puHeaderFileThumbnails.c_str(), ImGuiTableColumnFlags_WidthFixed, -1, 4); //-V112 + ; + auto listViewID = ImGui::GetID("##FileDialog_fileTable"); + if (ImGui::BeginTableEx("##FileDialog_fileTable", listViewID, 5, flags, vSize, 0.0f)) + { + ImGui::TableSetupScrollFreeze(0, 1); // Make header always visible + ImGui::TableSetupColumn(fdi.puHeaderFileName.c_str(), ImGuiTableColumnFlags_WidthStretch, -1, 0); + ImGui::TableSetupColumn(fdi.puHeaderFileType.c_str(), ImGuiTableColumnFlags_WidthFixed | + ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnType) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 1); + ImGui::TableSetupColumn(fdi.puHeaderFileSize.c_str(), ImGuiTableColumnFlags_WidthFixed | + ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnSize) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 2); + ImGui::TableSetupColumn(fdi.puHeaderFileDate.c_str(), ImGuiTableColumnFlags_WidthFixed | + ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnDate) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 3); + // not needed to have an option for hide the thumbnails since this is why this view is used + ImGui::TableSetupColumn(fdi.puHeaderFileThumbnails.c_str(), ImGuiTableColumnFlags_WidthFixed, -1, 4); //-V112 #ifndef USE_CUSTOM_SORTING_ICON - // Sort our data if sort specs have been changed! - if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs()) - { - if (sorts_specs->SpecsDirty && !fdi.IsFileListEmpty()) - { - if (sorts_specs->Specs->ColumnUserID == 0) - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true); - else if (sorts_specs->Specs->ColumnUserID == 1) - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true); - else if (sorts_specs->Specs->ColumnUserID == 2) - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true); - else if (sorts_specs->Specs->ColumnUserID == 3) - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true); - else // if (sorts_specs->Specs->ColumnUserID == 4) = > always true for the moment, to uncomment if we add another column - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_THUMBNAILS, true); - sorts_specs->SpecsDirty = false; - } - } + // Sort our data if sort specs have been changed! + if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs()) + { + if (sorts_specs->SpecsDirty && !fdi.IsFileListEmpty()) + { + if (sorts_specs->Specs->ColumnUserID == 0) + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true); + else if (sorts_specs->Specs->ColumnUserID == 1) + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true); + else if (sorts_specs->Specs->ColumnUserID == 2) + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true); + else if (sorts_specs->Specs->ColumnUserID == 3) + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true); + else // if (sorts_specs->Specs->ColumnUserID == 4) = > always true for the moment, to uncomment if we add another column + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_THUMBNAILS, true); + sorts_specs->SpecsDirty = false; + } + } - ImGui::TableHeadersRow(); + ImGui::TableHeadersRow(); #else // USE_CUSTOM_SORTING_ICON - ImGui::TableNextRow(ImGuiTableRowFlags_Headers); - for (int column = 0; column < 5; column++) - { - ImGui::TableSetColumnIndex(column); - const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn() - ImGui::PushID(column); - ImGui::TableHeader(column_name); - ImGui::PopID(); - if (ImGui::IsItemClicked()) - { - if (column == 0) - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true); - else if (column == 1) - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true); - else if (column == 2) - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true); - else if (column == 3) - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true); - else // if (column == 4) = > always true for the moment, to uncomment if we add another column - fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_THUMBNAILS, true); - } - } + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + for (int column = 0; column < 5; column++) + { + ImGui::TableSetColumnIndex(column); + const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn() + ImGui::PushID(column); + ImGui::TableHeader(column_name); + ImGui::PopID(); + if (ImGui::IsItemClicked()) + { + if (column == 0) + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true); + else if (column == 1) + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true); + else if (column == 2) + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true); + else if (column == 3) + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true); + else // if (column == 4) = > always true for the moment, to uncomment if we add another column + fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_THUMBNAILS, true); + } + } #endif // USE_CUSTOM_SORTING_ICON - if (!fdi.IsFilteredListEmpty()) - { - std::string _str; - ImFont* _font = nullptr; - bool _showColor = false; + if (!fdi.IsFilteredListEmpty()) + { + std::string _str; + ImFont* _font = nullptr; + bool _showColor = false; - ImGuiContext& g = *GImGui; - const float itemHeight = ImMax(g.FontSize, DisplayMode_ThumbailsList_ImageHeight) + g.Style.ItemSpacing.y; + ImGuiContext& g = *GImGui; + const float itemHeight = ImMax(g.FontSize, DisplayMode_ThumbailsList_ImageHeight) + g.Style.ItemSpacing.y; - prFileListClipper.Begin((int)fdi.GetFilteredListSize(), itemHeight); - while (prFileListClipper.Step()) - { - for (int i = prFileListClipper.DisplayStart; i < prFileListClipper.DisplayEnd; i++) - { - if (i < 0) continue; + prFileListClipper.Begin((int)fdi.GetFilteredListSize(), itemHeight); + while (prFileListClipper.Step()) + { + for (int i = prFileListClipper.DisplayStart; i < prFileListClipper.DisplayEnd; i++) + { + if (i < 0) continue; - auto infos = fdi.GetFilteredFileAt((size_t)i); - if (!infos.use_count()) - continue; + auto infos = fdi.GetFilteredFileAt((size_t)i); + if (!infos.use_count()) + continue; - prBeginFileColorIconStyle(infos, _showColor, _str, &_font); + prBeginFileColorIconStyle(infos, _showColor, _str, &_font); - bool selected = fdi.IsFileNameSelected(infos->fileNameExt); // found + bool selected = fdi.IsFileNameSelected(infos->fileNameExt); // found - ImGui::TableNextRow(); + ImGui::TableNextRow(); - bool needToBreakTheloop = false; + bool needToBreakTheloop = false; - if (ImGui::TableNextColumn()) // file name - { - needToBreakTheloop = prSelectableItem(i, infos, selected, _str.c_str()); - } - if (ImGui::TableNextColumn()) // file type - { - ImGui::Text("%s", infos->fileExt.c_str()); - } - if (ImGui::TableNextColumn()) // file size - { - if (infos->fileType != 'd') - { - ImGui::Text("%s ", infos->formatedFileSize.c_str()); - } - else - { - ImGui::Text(""); - } - } - if (ImGui::TableNextColumn()) // file date + time - { - ImGui::Text("%s", infos->fileModifDate.c_str()); - } - if (ImGui::TableNextColumn()) // file thumbnails - { - auto th = &infos->thumbnailInfo; + if (ImGui::TableNextColumn()) // file name + { + needToBreakTheloop = prSelectableItem(i, infos, selected, _str.c_str()); + } + if (ImGui::TableNextColumn()) // file type + { + ImGui::Text("%s", infos->fileExt.c_str()); + } + if (ImGui::TableNextColumn()) // file size + { + if (infos->fileType != 'd') + { + ImGui::Text("%s ", infos->formatedFileSize.c_str()); + } + else + { + ImGui::Text(""); + } + } + if (ImGui::TableNextColumn()) // file date + time + { + ImGui::Text("%s", infos->fileModifDate.c_str()); + } + if (ImGui::TableNextColumn()) // file thumbnails + { + auto th = &infos->thumbnailInfo; - if (!th->isLoadingOrLoaded) - { - prAddThumbnailToLoad(infos); - } - if (th->isReadyToDisplay && - th->textureID) - { - ImGui::Image((ImTextureID)th->textureID, - ImVec2((float)th->textureWidth, - (float)th->textureHeight)); - } - } + if (!th->isLoadingOrLoaded) + { + prAddThumbnailToLoad(infos); + } + if (th->isReadyToDisplay && + th->textureID) + { + ImGui::Image((ImTextureID)th->textureID, + ImVec2((float)th->textureWidth, + (float)th->textureHeight)); + } + } - prEndFileColorIconStyle(_showColor, _font); + prEndFileColorIconStyle(_showColor, _font); - if (needToBreakTheloop) - break; - } - } - prFileListClipper.End(); - } + if (needToBreakTheloop) + break; + } + } + prFileListClipper.End(); + } #ifdef USE_EXPLORATION_BY_KEYS - if (!fdi.puInputPathActivated) - { - prLocateByInputKey(prFileDialogInternal); - prExploreWithkeys(prFileDialogInternal, listViewID); - } + if (!fdi.puInputPathActivated) + { + prLocateByInputKey(prFileDialogInternal); + prExploreWithkeys(prFileDialogInternal, listViewID); + } #endif // USE_EXPLORATION_BY_KEYS - ImGuiContext& g = *GImGui; - if (g.LastActiveId - 1 == listViewID || g.LastActiveId == listViewID) - { - prFileDialogInternal.puFileListViewIsActive = true; - } + ImGuiContext& g = *GImGui; + if (g.LastActiveId - 1 == listViewID || g.LastActiveId == listViewID) + { + prFileDialogInternal.puFileListViewIsActive = true; + } - ImGui::EndTable(); - } + ImGui::EndTable(); + } - ImGui::PopID(); - } + ImGui::PopID(); + } - void IGFD::FileDialog::prDrawThumbnailsGridView(ImVec2 vSize) - { - if (ImGui::BeginChild("##thumbnailsGridsFiles", vSize)) - { - // todo - } + void IGFD::FileDialog::prDrawThumbnailsGridView(ImVec2 vSize) + { + if (ImGui::BeginChild("##thumbnailsGridsFiles", vSize)) + { + // todo + } - ImGui::EndChild(); - } + ImGui::EndChild(); + } #endif - void IGFD::FileDialog::prDrawSidePane(float vHeight) - { - ImGui::SameLine(); + void IGFD::FileDialog::prDrawSidePane(float vHeight) + { + ImGui::SameLine(); - ImGui::BeginChild("##FileTypes", ImVec2(0, vHeight)); + ImGui::BeginChild("##FileTypes", ImVec2(0, vHeight)); - prFileDialogInternal.puDLGoptionsPane( - prFileDialogInternal.puFilterManager.GetSelectedFilter().filter.c_str(), - prFileDialogInternal.puDLGuserDatas, &prFileDialogInternal.puCanWeContinue); + prFileDialogInternal.puDLGoptionsPane( + prFileDialogInternal.puFilterManager.GetSelectedFilter().filter.c_str(), + prFileDialogInternal.puDLGuserDatas, &prFileDialogInternal.puCanWeContinue); - ImGui::EndChild(); - } + ImGui::EndChild(); + } - void IGFD::FileDialog::Close() - { - prFileDialogInternal.puDLGkey.clear(); - prFileDialogInternal.puShowDialog = false; - } + void IGFD::FileDialog::Close() + { + prFileDialogInternal.puDLGkey.clear(); + prFileDialogInternal.puShowDialog = false; + } - bool IGFD::FileDialog::WasOpenedThisFrame(const std::string& vKey) const - { - bool res = prFileDialogInternal.puShowDialog && prFileDialogInternal.puDLGkey == vKey; - if (res) - { - ImGuiContext& g = *GImGui; - res &= prFileDialogInternal.puLastImGuiFrameCount == g.FrameCount; // return true if a dialog was displayed in this frame - } - return res; - } + bool IGFD::FileDialog::WasOpenedThisFrame(const std::string& vKey) const + { + bool res = prFileDialogInternal.puShowDialog && prFileDialogInternal.puDLGkey == vKey; + if (res) + { + ImGuiContext& g = *GImGui; + res &= prFileDialogInternal.puLastImGuiFrameCount == g.FrameCount; // return true if a dialog was displayed in this frame + } + return res; + } - bool IGFD::FileDialog::WasOpenedThisFrame() const - { - bool res = prFileDialogInternal.puShowDialog; - if (res) - { - ImGuiContext& g = *GImGui; - res &= prFileDialogInternal.puLastImGuiFrameCount == g.FrameCount; // return true if a dialog was displayed in this frame - } - return res; - } + bool IGFD::FileDialog::WasOpenedThisFrame() const + { + bool res = prFileDialogInternal.puShowDialog; + if (res) + { + ImGuiContext& g = *GImGui; + res &= prFileDialogInternal.puLastImGuiFrameCount == g.FrameCount; // return true if a dialog was displayed in this frame + } + return res; + } - bool IGFD::FileDialog::IsOpened(const std::string& vKey) const - { - return (prFileDialogInternal.puShowDialog && prFileDialogInternal.puDLGkey == vKey); - } + bool IGFD::FileDialog::IsOpened(const std::string& vKey) const + { + return (prFileDialogInternal.puShowDialog && prFileDialogInternal.puDLGkey == vKey); + } - bool IGFD::FileDialog::IsOpened() const - { - return prFileDialogInternal.puShowDialog; - } + bool IGFD::FileDialog::IsOpened() const + { + return prFileDialogInternal.puShowDialog; + } - std::string IGFD::FileDialog::GetOpenedKey() const - { - if (prFileDialogInternal.puShowDialog) - return prFileDialogInternal.puDLGkey; - return ""; - } + std::string IGFD::FileDialog::GetOpenedKey() const + { + if (prFileDialogInternal.puShowDialog) + return prFileDialogInternal.puDLGkey; + return ""; + } - std::string IGFD::FileDialog::GetFilePathName() - { - return prFileDialogInternal.puFileManager.GetResultingFilePathName(prFileDialogInternal); - } + std::string IGFD::FileDialog::GetFilePathName() + { + return prFileDialogInternal.puFileManager.GetResultingFilePathName(prFileDialogInternal); + } - std::string IGFD::FileDialog::GetCurrentPath() - { - return prFileDialogInternal.puFileManager.GetResultingPath(); - } + std::string IGFD::FileDialog::GetCurrentPath() + { + return prFileDialogInternal.puFileManager.GetResultingPath(); + } - std::string IGFD::FileDialog::GetCurrentFileName() - { - return prFileDialogInternal.puFileManager.GetResultingFileName(prFileDialogInternal); - } + std::string IGFD::FileDialog::GetCurrentFileName() + { + return prFileDialogInternal.puFileManager.GetResultingFileName(prFileDialogInternal); + } - std::string IGFD::FileDialog::GetCurrentFilter() - { - return prFileDialogInternal.puFilterManager.GetSelectedFilter().filter; - } + std::string IGFD::FileDialog::GetCurrentFilter() + { + return prFileDialogInternal.puFilterManager.GetSelectedFilter().filter; + } - std::map IGFD::FileDialog::GetSelection() - { - return prFileDialogInternal.puFileManager.GetResultingSelection(); - } + std::map IGFD::FileDialog::GetSelection() + { + return prFileDialogInternal.puFileManager.GetResultingSelection(); + } - UserDatas IGFD::FileDialog::GetUserDatas() const - { - return prFileDialogInternal.puDLGuserDatas; - } + UserDatas IGFD::FileDialog::GetUserDatas() const + { + return prFileDialogInternal.puDLGuserDatas; + } - bool IGFD::FileDialog::IsOk() const - { - return prFileDialogInternal.puIsOk; - } + bool IGFD::FileDialog::IsOk() const + { + return prFileDialogInternal.puIsOk; + } - void IGFD::FileDialog::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const FileStyle& vInfos) - { - prFileDialogInternal.puFilterManager.SetFileStyle(vFlags, vCriteria, vInfos); - } + void IGFD::FileDialog::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const FileStyle& vInfos) + { + prFileDialogInternal.puFilterManager.SetFileStyle(vFlags, vCriteria, vInfos); + } - void IGFD::FileDialog::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const ImVec4& vColor, const std::string& vIcon, ImFont* vFont) - { - prFileDialogInternal.puFilterManager.SetFileStyle(vFlags, vCriteria, vColor, vIcon, vFont); - } + void IGFD::FileDialog::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const ImVec4& vColor, const std::string& vIcon, ImFont* vFont) + { + prFileDialogInternal.puFilterManager.SetFileStyle(vFlags, vCriteria, vColor, vIcon, vFont); + } - bool IGFD::FileDialog::GetFileStyle(const IGFD_FileStyleFlags& vFlags, const std::string& vCriteria, ImVec4* vOutColor, std::string* vOutIcon, ImFont **vOutFont) - { - return prFileDialogInternal.puFilterManager.GetFileStyle(vFlags, vCriteria, vOutColor, vOutIcon, vOutFont); - } + bool IGFD::FileDialog::GetFileStyle(const IGFD_FileStyleFlags& vFlags, const std::string& vCriteria, ImVec4* vOutColor, std::string* vOutIcon, ImFont **vOutFont) + { + return prFileDialogInternal.puFilterManager.GetFileStyle(vFlags, vCriteria, vOutColor, vOutIcon, vOutFont); + } - void IGFD::FileDialog::ClearFilesStyle() - { - prFileDialogInternal.puFilterManager.ClearFilesStyle(); - } + void IGFD::FileDialog::ClearFilesStyle() + { + prFileDialogInternal.puFilterManager.ClearFilesStyle(); + } - void IGFD::FileDialog::SetLocales(const int& vLocaleCategory, const std::string& vLocaleBegin, const std::string& vLocaleEnd) - { - prFileDialogInternal.puUseCustomLocale = true; - prFileDialogInternal.puLocaleBegin = vLocaleBegin; - prFileDialogInternal.puLocaleEnd = vLocaleEnd; - } + void IGFD::FileDialog::SetLocales(const int& vLocaleCategory, const std::string& vLocaleBegin, const std::string& vLocaleEnd) + { + prFileDialogInternal.puUseCustomLocale = true; + prFileDialogInternal.puLocaleBegin = vLocaleBegin; + prFileDialogInternal.puLocaleEnd = vLocaleEnd; + } - ////////////////////////////////////////////////////////////////////////////// - //// OVERWRITE DIALOG //////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + //// OVERWRITE DIALOG //////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// - bool IGFD::FileDialog::prConfirm_Or_OpenOverWriteFileDialog_IfNeeded(bool vLastAction, ImGuiWindowFlags vFlags) - { - // if confirmation => return true for confirm the overwrite et quit the dialog - // if cancel => return false && set IsOk to false for keep inside the dialog + bool IGFD::FileDialog::prConfirm_Or_OpenOverWriteFileDialog_IfNeeded(bool vLastAction, ImGuiWindowFlags vFlags) + { + // if confirmation => return true for confirm the overwrite et quit the dialog + // if cancel => return false && set IsOk to false for keep inside the dialog - // if IsOk == false => return false for quit the dialog - if (!prFileDialogInternal.puIsOk && vLastAction) - { - QuitFrame(); - return true; - } + // if IsOk == false => return false for quit the dialog + if (!prFileDialogInternal.puIsOk && vLastAction) + { + QuitFrame(); + return true; + } - // if IsOk == true && no check of overwrite => return true for confirm the dialog - if (prFileDialogInternal.puIsOk && vLastAction && !(prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_ConfirmOverwrite)) - { - QuitFrame(); - return true; - } + // if IsOk == true && no check of overwrite => return true for confirm the dialog + if (prFileDialogInternal.puIsOk && vLastAction && !(prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_ConfirmOverwrite)) + { + QuitFrame(); + return true; + } - // if IsOk == true && check of overwrite => return false and show confirm to overwrite dialog - if ((prFileDialogInternal.puOkResultToConfirm || (prFileDialogInternal.puIsOk && vLastAction)) && - (prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_ConfirmOverwrite)) - { - if (prFileDialogInternal.puIsOk) // catched only one time - { - if (!prFileDialogInternal.puFileManager.IsFileExist(GetFilePathName())) // not existing => quit dialog - { - QuitFrame(); - return true; - } - else // existing => confirm dialog to open - { - prFileDialogInternal.puIsOk = false; - prFileDialogInternal.puOkResultToConfirm = true; - } - } + // if IsOk == true && check of overwrite => return false and show confirm to overwrite dialog + if ((prFileDialogInternal.puOkResultToConfirm || (prFileDialogInternal.puIsOk && vLastAction)) && + (prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_ConfirmOverwrite)) + { + if (prFileDialogInternal.puIsOk) // catched only one time + { + if (!prFileDialogInternal.puFileManager.IsFileExist(GetFilePathName())) // not existing => quit dialog + { + QuitFrame(); + return true; + } + else // existing => confirm dialog to open + { + prFileDialogInternal.puIsOk = false; + prFileDialogInternal.puOkResultToConfirm = true; + } + } - std::string name = OverWriteDialogTitleString "##" + prFileDialogInternal.puDLGtitle + prFileDialogInternal.puDLGkey + "OverWriteDialog"; + std::string name = OverWriteDialogTitleString "##" + prFileDialogInternal.puDLGtitle + prFileDialogInternal.puDLGkey + "OverWriteDialog"; - bool res = false; + bool res = false; - ImGui::OpenPopup(name.c_str()); - if (ImGui::BeginPopupModal(name.c_str(), (bool*)0, - vFlags | ImGuiWindowFlags_AlwaysAutoResize | - ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) - { - ImGui::SetWindowPos(prFileDialogInternal.puDialogCenterPos - ImGui::GetWindowSize() * 0.5f); // next frame needed for GetWindowSize to work + ImGui::OpenPopup(name.c_str()); + if (ImGui::BeginPopupModal(name.c_str(), (bool*)0, + vFlags | ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) + { + ImGui::SetWindowPos(prFileDialogInternal.puDialogCenterPos - ImGui::GetWindowSize() * 0.5f); // next frame needed for GetWindowSize to work - ImGui::Text("%s", OverWriteDialogMessageString); + ImGui::Text("%s", OverWriteDialogMessageString); - if (IMGUI_BUTTON(OverWriteDialogConfirmButtonString)) - { - prFileDialogInternal.puOkResultToConfirm = false; - prFileDialogInternal.puIsOk = true; - res = true; - ImGui::CloseCurrentPopup(); - } + if (IMGUI_BUTTON(OverWriteDialogConfirmButtonString)) + { + prFileDialogInternal.puOkResultToConfirm = false; + prFileDialogInternal.puIsOk = true; + res = true; + ImGui::CloseCurrentPopup(); + } - ImGui::SameLine(); + ImGui::SameLine(); - if (IMGUI_BUTTON(OverWriteDialogCancelButtonString)) - { - prFileDialogInternal.puOkResultToConfirm = false; - prFileDialogInternal.puIsOk = false; - res = false; - ImGui::CloseCurrentPopup(); - } + if (IMGUI_BUTTON(OverWriteDialogCancelButtonString)) + { + prFileDialogInternal.puOkResultToConfirm = false; + prFileDialogInternal.puIsOk = false; + res = false; + ImGui::CloseCurrentPopup(); + } - ImGui::EndPopup(); - } + ImGui::EndPopup(); + } - if (res) - { - QuitFrame(); - } - return res; - } + if (res) + { + QuitFrame(); + } + return res; + } - return false; - } + return false; + } } #endif // __cplusplus @@ -4555,574 +4555,574 @@ namespace IGFD // Return an initialized IGFD_Selection_Pair IMGUIFILEDIALOG_API IGFD_Selection_Pair IGFD_Selection_Pair_Get(void) { - IGFD_Selection_Pair res = {}; - res.fileName = nullptr; - res.filePathName = nullptr; - return res; + IGFD_Selection_Pair res = {}; + res.fileName = nullptr; + res.filePathName = nullptr; + return res; } // destroy only the content of vSelection_Pair IMGUIFILEDIALOG_API void IGFD_Selection_Pair_DestroyContent(IGFD_Selection_Pair* vSelection_Pair) { - if (vSelection_Pair) - { - delete[] vSelection_Pair->fileName; - delete[] vSelection_Pair->filePathName; - } + if (vSelection_Pair) + { + delete[] vSelection_Pair->fileName; + delete[] vSelection_Pair->filePathName; + } } // Return an initialized IGFD_Selection IMGUIFILEDIALOG_API IGFD_Selection IGFD_Selection_Get(void) { - return { nullptr, 0U }; + return { nullptr, 0U }; } // destroy only the content of vSelection IMGUIFILEDIALOG_API void IGFD_Selection_DestroyContent(IGFD_Selection* vSelection) { - if (vSelection) - { - if (vSelection->table) - { - for (size_t i = 0U; i < vSelection->count; i++) - { - IGFD_Selection_Pair_DestroyContent(&vSelection->table[i]); - } - delete[] vSelection->table; - } - vSelection->count = 0U; - } + if (vSelection) + { + if (vSelection->table) + { + for (size_t i = 0U; i < vSelection->count; i++) + { + IGFD_Selection_Pair_DestroyContent(&vSelection->table[i]); + } + delete[] vSelection->table; + } + vSelection->count = 0U; + } } // create an instance of ImGuiFileDialog IMGUIFILEDIALOG_API ImGuiFileDialog* IGFD_Create(void) { - return new ImGuiFileDialog(); + return new ImGuiFileDialog(); } // destroy the instance of ImGuiFileDialog IMGUIFILEDIALOG_API void IGFD_Destroy(ImGuiFileDialog* vContext) { - if (vContext) - { - delete vContext; - vContext = nullptr; - } + if (vContext) + { + delete vContext; + vContext = nullptr; + } } // standard dialog IMGUIFILEDIALOG_API void IGFD_OpenDialog( - ImGuiFileDialog* vContext, - const char* vKey, - const char* vTitle, - const char* vFilters, - const char* vPath, - const char* vFileName, - const int vCountSelectionMax, - void* vUserDatas, - ImGuiFileDialogFlags flags) + ImGuiFileDialog* vContext, + const char* vKey, + const char* vTitle, + const char* vFilters, + const char* vPath, + const char* vFileName, + const int vCountSelectionMax, + void* vUserDatas, + ImGuiFileDialogFlags flags) { - if (vContext) - { - vContext->OpenDialog( - vKey, vTitle, vFilters, vPath, vFileName, - vCountSelectionMax, vUserDatas, flags); - } + if (vContext) + { + vContext->OpenDialog( + vKey, vTitle, vFilters, vPath, vFileName, + vCountSelectionMax, vUserDatas, flags); + } } IMGUIFILEDIALOG_API void IGFD_OpenDialog2( - ImGuiFileDialog* vContext, - const char* vKey, - const char* vTitle, - const char* vFilters, - const char* vFilePathName, - const int vCountSelectionMax, - void* vUserDatas, - ImGuiFileDialogFlags flags) + ImGuiFileDialog* vContext, + const char* vKey, + const char* vTitle, + const char* vFilters, + const char* vFilePathName, + const int vCountSelectionMax, + void* vUserDatas, + ImGuiFileDialogFlags flags) { - if (vContext) - { - vContext->OpenDialog( - vKey, vTitle, vFilters, vFilePathName, - vCountSelectionMax, vUserDatas, flags); - } + if (vContext) + { + vContext->OpenDialog( + vKey, vTitle, vFilters, vFilePathName, + vCountSelectionMax, vUserDatas, flags); + } } IMGUIFILEDIALOG_API void IGFD_OpenPaneDialog( - ImGuiFileDialog* vContext, - const char* vKey, - const char* vTitle, - const char* vFilters, - const char* vPath, - const char* vFileName, - IGFD_PaneFun vSidePane, - const float vSidePaneWidth, - const int vCountSelectionMax, - void* vUserDatas, - ImGuiFileDialogFlags flags) + ImGuiFileDialog* vContext, + const char* vKey, + const char* vTitle, + const char* vFilters, + const char* vPath, + const char* vFileName, + IGFD_PaneFun vSidePane, + const float vSidePaneWidth, + const int vCountSelectionMax, + void* vUserDatas, + ImGuiFileDialogFlags flags) { - if (vContext) - { - vContext->OpenDialog( - vKey, vTitle, vFilters, - vPath, vFileName, - vSidePane, vSidePaneWidth, - vCountSelectionMax, vUserDatas, flags); - } + if (vContext) + { + vContext->OpenDialog( + vKey, vTitle, vFilters, + vPath, vFileName, + vSidePane, vSidePaneWidth, + vCountSelectionMax, vUserDatas, flags); + } } IMGUIFILEDIALOG_API void IGFD_OpenPaneDialog2( - ImGuiFileDialog* vContext, - const char* vKey, - const char* vTitle, - const char* vFilters, - const char* vFilePathName, - IGFD_PaneFun vSidePane, - const float vSidePaneWidth, - const int vCountSelectionMax, - void* vUserDatas, - ImGuiFileDialogFlags flags) + ImGuiFileDialog* vContext, + const char* vKey, + const char* vTitle, + const char* vFilters, + const char* vFilePathName, + IGFD_PaneFun vSidePane, + const float vSidePaneWidth, + const int vCountSelectionMax, + void* vUserDatas, + ImGuiFileDialogFlags flags) { - if (vContext) - { - vContext->OpenDialog( - vKey, vTitle, vFilters, - vFilePathName, - vSidePane, vSidePaneWidth, - vCountSelectionMax, vUserDatas, flags); - } + if (vContext) + { + vContext->OpenDialog( + vKey, vTitle, vFilters, + vFilePathName, + vSidePane, vSidePaneWidth, + vCountSelectionMax, vUserDatas, flags); + } } // modal dialog IMGUIFILEDIALOG_API void IGFD_OpenModal( - ImGuiFileDialog* vContext, - const char* vKey, - const char* vTitle, - const char* vFilters, - const char* vPath, - const char* vFileName, - const int vCountSelectionMax, - void* vUserDatas, - ImGuiFileDialogFlags flags) + ImGuiFileDialog* vContext, + const char* vKey, + const char* vTitle, + const char* vFilters, + const char* vPath, + const char* vFileName, + const int vCountSelectionMax, + void* vUserDatas, + ImGuiFileDialogFlags flags) { - if (vContext) - { - vContext->OpenModal( - vKey, vTitle, vFilters, vPath, vFileName, - vCountSelectionMax, vUserDatas, flags); - } + if (vContext) + { + vContext->OpenModal( + vKey, vTitle, vFilters, vPath, vFileName, + vCountSelectionMax, vUserDatas, flags); + } } IMGUIFILEDIALOG_API void IGFD_OpenModal2( - ImGuiFileDialog* vContext, - const char* vKey, - const char* vTitle, - const char* vFilters, - const char* vFilePathName, - const int vCountSelectionMax, - void* vUserDatas, - ImGuiFileDialogFlags flags) + ImGuiFileDialog* vContext, + const char* vKey, + const char* vTitle, + const char* vFilters, + const char* vFilePathName, + const int vCountSelectionMax, + void* vUserDatas, + ImGuiFileDialogFlags flags) { - if (vContext) - { - vContext->OpenModal( - vKey, vTitle, vFilters, vFilePathName, - vCountSelectionMax, vUserDatas, flags); - } + if (vContext) + { + vContext->OpenModal( + vKey, vTitle, vFilters, vFilePathName, + vCountSelectionMax, vUserDatas, flags); + } } IMGUIFILEDIALOG_API void IGFD_OpenPaneModal( - ImGuiFileDialog* vContext, - const char* vKey, - const char* vTitle, - const char* vFilters, - const char* vPath, - const char* vFileName, - IGFD_PaneFun vSidePane, - const float vSidePaneWidth, - const int vCountSelectionMax, - void* vUserDatas, - ImGuiFileDialogFlags flags) + ImGuiFileDialog* vContext, + const char* vKey, + const char* vTitle, + const char* vFilters, + const char* vPath, + const char* vFileName, + IGFD_PaneFun vSidePane, + const float vSidePaneWidth, + const int vCountSelectionMax, + void* vUserDatas, + ImGuiFileDialogFlags flags) { - if (vContext) - { - vContext->OpenModal( - vKey, vTitle, vFilters, - vPath, vFileName, - vSidePane, vSidePaneWidth, - vCountSelectionMax, vUserDatas, flags); - } + if (vContext) + { + vContext->OpenModal( + vKey, vTitle, vFilters, + vPath, vFileName, + vSidePane, vSidePaneWidth, + vCountSelectionMax, vUserDatas, flags); + } } IMGUIFILEDIALOG_API void IGFD_OpenPaneModal2( - ImGuiFileDialog* vContext, - const char* vKey, - const char* vTitle, - const char* vFilters, - const char* vFilePathName, - IGFD_PaneFun vSidePane, - const float vSidePaneWidth, - const int vCountSelectionMax, - void* vUserDatas, - ImGuiFileDialogFlags flags) + ImGuiFileDialog* vContext, + const char* vKey, + const char* vTitle, + const char* vFilters, + const char* vFilePathName, + IGFD_PaneFun vSidePane, + const float vSidePaneWidth, + const int vCountSelectionMax, + void* vUserDatas, + ImGuiFileDialogFlags flags) { - if (vContext) - { - vContext->OpenModal( - vKey, vTitle, vFilters, - vFilePathName, - vSidePane, vSidePaneWidth, - vCountSelectionMax, vUserDatas, flags); - } + if (vContext) + { + vContext->OpenModal( + vKey, vTitle, vFilters, + vFilePathName, + vSidePane, vSidePaneWidth, + vCountSelectionMax, vUserDatas, flags); + } } IMGUIFILEDIALOG_API bool IGFD_DisplayDialog(ImGuiFileDialog* vContext, - const char* vKey, ImGuiWindowFlags vFlags, ImVec2 vMinSize, ImVec2 vMaxSize) + const char* vKey, ImGuiWindowFlags vFlags, ImVec2 vMinSize, ImVec2 vMaxSize) { - if (vContext) - { - return vContext->Display(vKey, vFlags, vMinSize, vMaxSize); - } + if (vContext) + { + return vContext->Display(vKey, vFlags, vMinSize, vMaxSize); + } - return false; + return false; } IMGUIFILEDIALOG_API void IGFD_CloseDialog(ImGuiFileDialog* vContext) { - if (vContext) - { - vContext->Close(); - } + if (vContext) + { + vContext->Close(); + } } IMGUIFILEDIALOG_API bool IGFD_IsOk(ImGuiFileDialog* vContext) { - if (vContext) - { - return vContext->IsOk(); - } + if (vContext) + { + return vContext->IsOk(); + } - return false; + return false; } IMGUIFILEDIALOG_API bool IGFD_WasKeyOpenedThisFrame(ImGuiFileDialog* vContext, - const char* vKey) + const char* vKey) { - if (vContext) - { - vContext->WasOpenedThisFrame(vKey); - } + if (vContext) + { + vContext->WasOpenedThisFrame(vKey); + } - return false; + return false; } IMGUIFILEDIALOG_API bool IGFD_WasOpenedThisFrame(ImGuiFileDialog* vContext) { - if (vContext) - { - vContext->WasOpenedThisFrame(); - } + if (vContext) + { + vContext->WasOpenedThisFrame(); + } - return false; + return false; } IMGUIFILEDIALOG_API bool IGFD_IsKeyOpened(ImGuiFileDialog* vContext, - const char* vCurrentOpenedKey) + const char* vCurrentOpenedKey) { - if (vContext) - { - vContext->IsOpened(vCurrentOpenedKey); - } + if (vContext) + { + vContext->IsOpened(vCurrentOpenedKey); + } - return false; + return false; } IMGUIFILEDIALOG_API bool IGFD_IsOpened(ImGuiFileDialog* vContext) { - if (vContext) - { - vContext->IsOpened(); - } + if (vContext) + { + vContext->IsOpened(); + } - return false; + return false; } IMGUIFILEDIALOG_API IGFD_Selection IGFD_GetSelection(ImGuiFileDialog* vContext) { - IGFD_Selection res = IGFD_Selection_Get(); + IGFD_Selection res = IGFD_Selection_Get(); - if (vContext) - { - auto sel = vContext->GetSelection(); - if (!sel.empty()) - { - res.count = sel.size(); - res.table = new IGFD_Selection_Pair[res.count]; + if (vContext) + { + auto sel = vContext->GetSelection(); + if (!sel.empty()) + { + res.count = sel.size(); + res.table = new IGFD_Selection_Pair[res.count]; - size_t idx = 0U; - for (const auto& s : sel) - { - IGFD_Selection_Pair* pair = res.table + idx++; + size_t idx = 0U; + for (const auto& s : sel) + { + IGFD_Selection_Pair* pair = res.table + idx++; - // fileNameExt - if (!s.first.empty()) - { - size_t siz = s.first.size() + 1U; - pair->fileName = new char[siz]; + // fileNameExt + if (!s.first.empty()) + { + size_t siz = s.first.size() + 1U; + pair->fileName = new char[siz]; #ifndef MSVC - strncpy(pair->fileName, s.first.c_str(), siz); + strncpy(pair->fileName, s.first.c_str(), siz); #else - strncpy_s(pair->fileName, siz, s.first.c_str(), siz); + strncpy_s(pair->fileName, siz, s.first.c_str(), siz); #endif - pair->fileName[siz - 1U] = '\0'; - } + pair->fileName[siz - 1U] = '\0'; + } - // filePathName - if (!s.second.empty()) - { - size_t siz = s.first.size() + 1U; - pair->filePathName = new char[siz]; + // filePathName + if (!s.second.empty()) + { + size_t siz = s.first.size() + 1U; + pair->filePathName = new char[siz]; #ifndef MSVC - strncpy(pair->filePathName, s.first.c_str(), siz); + strncpy(pair->filePathName, s.first.c_str(), siz); #else - strncpy_s(pair->filePathName, siz, s.first.c_str(), siz); + strncpy_s(pair->filePathName, siz, s.first.c_str(), siz); #endif - pair->filePathName[siz - 1U] = '\0'; - } - } + pair->filePathName[siz - 1U] = '\0'; + } + } - return res; - } - } + return res; + } + } - return res; + return res; } IMGUIFILEDIALOG_API char* IGFD_GetFilePathName(ImGuiFileDialog* vContext) { - char* res = nullptr; + char* res = nullptr; - if (vContext) - { - auto s = vContext->GetFilePathName(); - if (!s.empty()) - { - size_t siz = s.size() + 1U; - res = new char[siz]; + if (vContext) + { + auto s = vContext->GetFilePathName(); + if (!s.empty()) + { + size_t siz = s.size() + 1U; + res = new char[siz]; #ifndef MSVC - strncpy(res, s.c_str(), siz); + strncpy(res, s.c_str(), siz); #else - strncpy_s(res, siz, s.c_str(), siz); + strncpy_s(res, siz, s.c_str(), siz); #endif - res[siz - 1U] = '\0'; - } - } + res[siz - 1U] = '\0'; + } + } - return res; + return res; } IMGUIFILEDIALOG_API char* IGFD_GetCurrentFileName(ImGuiFileDialog* vContext) { - char* res = nullptr; + char* res = nullptr; - if (vContext) - { - auto s = vContext->GetCurrentFileName(); - if (!s.empty()) - { - size_t siz = s.size() + 1U; - res = new char[siz]; + if (vContext) + { + auto s = vContext->GetCurrentFileName(); + if (!s.empty()) + { + size_t siz = s.size() + 1U; + res = new char[siz]; #ifndef MSVC - strncpy(res, s.c_str(), siz); + strncpy(res, s.c_str(), siz); #else - strncpy_s(res, siz, s.c_str(), siz); + strncpy_s(res, siz, s.c_str(), siz); #endif - res[siz - 1U] = '\0'; - } - } + res[siz - 1U] = '\0'; + } + } - return res; + return res; } IMGUIFILEDIALOG_API char* IGFD_GetCurrentPath(ImGuiFileDialog* vContext) { - char* res = nullptr; + char* res = nullptr; - if (vContext) - { - auto s = vContext->GetCurrentPath(); - if (!s.empty()) - { - size_t siz = s.size() + 1U; - res = new char[siz]; + if (vContext) + { + auto s = vContext->GetCurrentPath(); + if (!s.empty()) + { + size_t siz = s.size() + 1U; + res = new char[siz]; #ifndef MSVC - strncpy(res, s.c_str(), siz); + strncpy(res, s.c_str(), siz); #else - strncpy_s(res, siz, s.c_str(), siz); + strncpy_s(res, siz, s.c_str(), siz); #endif - res[siz - 1U] = '\0'; - } - } + res[siz - 1U] = '\0'; + } + } - return res; + return res; } IMGUIFILEDIALOG_API char* IGFD_GetCurrentFilter(ImGuiFileDialog* vContext) { - char* res = nullptr; + char* res = nullptr; - if (vContext) - { - auto s = vContext->GetCurrentFilter(); - if (!s.empty()) - { - size_t siz = s.size() + 1U; - res = new char[siz]; + if (vContext) + { + auto s = vContext->GetCurrentFilter(); + if (!s.empty()) + { + size_t siz = s.size() + 1U; + res = new char[siz]; #ifndef MSVC - strncpy(res, s.c_str(), siz); + strncpy(res, s.c_str(), siz); #else - strncpy_s(res, siz, s.c_str(), siz); + strncpy_s(res, siz, s.c_str(), siz); #endif - res[siz - 1U] = '\0'; - } - } + res[siz - 1U] = '\0'; + } + } - return res; + return res; } IMGUIFILEDIALOG_API void* IGFD_GetUserDatas(ImGuiFileDialog* vContext) { - if (vContext) - { - return vContext->GetUserDatas(); - } + if (vContext) + { + return vContext->GetUserDatas(); + } - return nullptr; + return nullptr; } IMGUIFILEDIALOG_API void IGFD_SetFileStyle(ImGuiFileDialog* vContext, - IGFD_FileStyleFlags vFlags, const char* vCriteria, ImVec4 vColor, const char* vIcon, ImFont* vFont) //-V813 + IGFD_FileStyleFlags vFlags, const char* vCriteria, ImVec4 vColor, const char* vIcon, ImFont* vFont) //-V813 { - if (vContext) - { - vContext->SetFileStyle(vFlags, vCriteria, vColor, vIcon, vFont); - } + if (vContext) + { + vContext->SetFileStyle(vFlags, vCriteria, vColor, vIcon, vFont); + } } IMGUIFILEDIALOG_API void IGFD_SetFileStyle2(ImGuiFileDialog* vContext, - IGFD_FileStyleFlags vFlags, const char* vCriteria, float vR, float vG, float vB, float vA, const char* vIcon, ImFont* vFont) + IGFD_FileStyleFlags vFlags, const char* vCriteria, float vR, float vG, float vB, float vA, const char* vIcon, ImFont* vFont) { - if (vContext) - { - vContext->SetFileStyle(vFlags, vCriteria, ImVec4(vR, vG, vB, vA), vIcon, vFont); - } + if (vContext) + { + vContext->SetFileStyle(vFlags, vCriteria, ImVec4(vR, vG, vB, vA), vIcon, vFont); + } } IMGUIFILEDIALOG_API bool IGFD_GetFileStyle(ImGuiFileDialog* vContext, - IGFD_FileStyleFlags vFlags, const char* vCriteria, ImVec4* vOutColor, char** vOutIcon, ImFont** vOutFont) + IGFD_FileStyleFlags vFlags, const char* vCriteria, ImVec4* vOutColor, char** vOutIcon, ImFont** vOutFont) { - if (vContext) - { - std::string icon; - bool res = vContext->GetFileStyle(vFlags, vCriteria, vOutColor, &icon, vOutFont); - if (!icon.empty() && vOutIcon) - { - size_t siz = icon.size() + 1U; - *vOutIcon = new char[siz]; + if (vContext) + { + std::string icon; + bool res = vContext->GetFileStyle(vFlags, vCriteria, vOutColor, &icon, vOutFont); + if (!icon.empty() && vOutIcon) + { + size_t siz = icon.size() + 1U; + *vOutIcon = new char[siz]; #ifndef MSVC - strncpy(*vOutIcon, icon.c_str(), siz); + strncpy(*vOutIcon, icon.c_str(), siz); #else - strncpy_s(*vOutIcon, siz, icon.c_str(), siz); + strncpy_s(*vOutIcon, siz, icon.c_str(), siz); #endif - (*vOutIcon)[siz - 1U] = '\0'; - } - return res; - } + (*vOutIcon)[siz - 1U] = '\0'; + } + return res; + } - return false; + return false; } IMGUIFILEDIALOG_API void IGFD_ClearFilesStyle(ImGuiFileDialog* vContext) { - if (vContext) - { - vContext->ClearFilesStyle(); - } + if (vContext) + { + vContext->ClearFilesStyle(); + } } IMGUIFILEDIALOG_API void SetLocales(ImGuiFileDialog* vContext, const int vCategory, const char* vBeginLocale, const char* vEndLocale) { - if (vContext) - { - vContext->SetLocales(vCategory, (vBeginLocale ? vBeginLocale : ""), (vEndLocale ? vEndLocale : "")); - } + if (vContext) + { + vContext->SetLocales(vCategory, (vBeginLocale ? vBeginLocale : ""), (vEndLocale ? vEndLocale : "")); + } } #ifdef USE_EXPLORATION_BY_KEYS IMGUIFILEDIALOG_API void IGFD_SetFlashingAttenuationInSeconds(ImGuiFileDialog* vContext, float vAttenValue) { - if (vContext) - { - vContext->SetFlashingAttenuationInSeconds(vAttenValue); - } + if (vContext) + { + vContext->SetFlashingAttenuationInSeconds(vAttenValue); + } } #endif #ifdef USE_BOOKMARK IMGUIFILEDIALOG_API char* IGFD_SerializeBookmarks(ImGuiFileDialog* vContext) { - char* res = nullptr; + char* res = nullptr; - if (vContext) - { - auto s = vContext->SerializeBookmarks(); - if (!s.empty()) - { - size_t siz = s.size() + 1U; - res = new char[siz]; + if (vContext) + { + auto s = vContext->SerializeBookmarks(); + if (!s.empty()) + { + size_t siz = s.size() + 1U; + res = new char[siz]; #ifndef MSVC - strncpy(res, s.c_str(), siz); + strncpy(res, s.c_str(), siz); #else - strncpy_s(res, siz, s.c_str(), siz); + strncpy_s(res, siz, s.c_str(), siz); #endif - res[siz - 1U] = '\0'; - } - } + res[siz - 1U] = '\0'; + } + } - return res; + return res; } IMGUIFILEDIALOG_API void IGFD_DeserializeBookmarks(ImGuiFileDialog* vContext, const char* vBookmarks) { - if (vContext) - { - vContext->DeserializeBookmarks(vBookmarks); - } + if (vContext) + { + vContext->DeserializeBookmarks(vBookmarks); + } } #endif #ifdef USE_THUMBNAILS IMGUIFILEDIALOG_API void SetCreateThumbnailCallback(ImGuiFileDialog* vContext, const IGFD_CreateThumbnailFun vCreateThumbnailFun) { - if (vContext) - { - vContext->SetCreateThumbnailCallback(vCreateThumbnailFun); - } + if (vContext) + { + vContext->SetCreateThumbnailCallback(vCreateThumbnailFun); + } } IMGUIFILEDIALOG_API void SetDestroyThumbnailCallback(ImGuiFileDialog* vContext, const IGFD_DestroyThumbnailFun vDestroyThumbnailFun) { - if (vContext) - { - vContext->SetDestroyThumbnailCallback(vDestroyThumbnailFun); - } + if (vContext) + { + vContext->SetDestroyThumbnailCallback(vDestroyThumbnailFun); + } } IMGUIFILEDIALOG_API void ManageGPUThumbnails(ImGuiFileDialog* vContext) { - if (vContext) - { - vContext->ManageGPUThumbnails(); - } + if (vContext) + { + vContext->ManageGPUThumbnails(); + } } #endif // USE_THUMBNAILS diff --git a/extern/igfd/ImGuiFileDialog.h b/extern/igfd/ImGuiFileDialog.h index 9c2d246f8..5ab7f6c84 100644 --- a/extern/igfd/ImGuiFileDialog.h +++ b/extern/igfd/ImGuiFileDialog.h @@ -85,20 +85,20 @@ void drawGui() { // open Dialog Simple if (ImGui::Button("Open File Dialog")) - ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Choose File", ".cpp,.h,.hpp", "."); + ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Choose File", ".cpp,.h,.hpp", "."); // display if (ImGuiFileDialog::Instance()->FileDialog("ChooseFileDlgKey")) { - // action if OK - if (ImGuiFileDialog::Instance()->IsOk == true) - { - std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName(); - std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath(); - // action - } - // close - ImGuiFileDialog::Instance()->CloseDialog("ChooseFileDlgKey"); + // action if OK + if (ImGuiFileDialog::Instance()->IsOk == true) + { + std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName(); + std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath(); + // action + } + // close + ImGuiFileDialog::Instance()->CloseDialog("ChooseFileDlgKey"); } } @@ -121,40 +121,40 @@ Example code : static bool canValidateDialog = false; inline void InfosPane(std::string& vFilter, IGFD::UserDatas vUserDatas, bool *vCantContinue) // if vCantContinue is false, the user cant validate the dialog { - ImGui::TextColored(ImVec4(0, 1, 1, 1), "Infos Pane"); - ImGui::Text("Selected Filter : %s", vFilter.c_str()); - if (vUserDatas) - ImGui::Text("UserDatas : %s", vUserDatas); - ImGui::Checkbox("if not checked you cant validate the dialog", &canValidateDialog); - if (vCantContinue) - *vCantContinue = canValidateDialog; + ImGui::TextColored(ImVec4(0, 1, 1, 1), "Infos Pane"); + ImGui::Text("Selected Filter : %s", vFilter.c_str()); + if (vUserDatas) + ImGui::Text("UserDatas : %s", vUserDatas); + ImGui::Checkbox("if not checked you cant validate the dialog", &canValidateDialog); + if (vCantContinue) + *vCantContinue = canValidateDialog; } void drawGui() { // open Dialog with Pane if (ImGui::Button("Open File Dialog with a custom pane")) - ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Choose File", ".cpp,.h,.hpp", - ".", "", std::bind(&InfosPane, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), 350, 1, IGFD::UserDatas("InfosPane")); + ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Choose File", ".cpp,.h,.hpp", + ".", "", std::bind(&InfosPane, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), 350, 1, IGFD::UserDatas("InfosPane")); // display and action if ok if (ImGuiFileDialog::Instance()->FileDialog("ChooseFileDlgKey")) { - if (ImGuiFileDialog::Instance()->IsOk == true) - { - std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName(); - std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath(); - std::string filter = ImGuiFileDialog::Instance()->GetCurrentFilter(); - // here convert from string because a string was passed as a userDatas, but it can be what you want - std::string userDatas; - if (ImGuiFileDialog::Instance()->GetUserDatas()) - userDatas = std::string((const char*)ImGuiFileDialog::Instance()->GetUserDatas()); - auto selection = ImGuiFileDialog::Instance()->GetSelection(); // multiselection + if (ImGuiFileDialog::Instance()->IsOk == true) + { + std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName(); + std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath(); + std::string filter = ImGuiFileDialog::Instance()->GetCurrentFilter(); + // here convert from string because a string was passed as a userDatas, but it can be what you want + std::string userDatas; + if (ImGuiFileDialog::Instance()->GetUserDatas()) + userDatas = std::string((const char*)ImGuiFileDialog::Instance()->GetUserDatas()); + auto selection = ImGuiFileDialog::Instance()->GetSelection(); // multiselection - // action - } - // close - ImGuiFileDialog::Instance()->CloseDialog("ChooseFileDlgKey"); + // action + } + // close + ImGuiFileDialog::Instance()->CloseDialog("ChooseFileDlgKey"); } } @@ -170,13 +170,13 @@ the general form is : ImGuiFileDialog::Instance()->SetFileStyle(styleType, criteria, color, icon, font); styleType can be thoses : -IGFD_FileStyle_None // define none style -IGFD_FileStyleByTypeFile // define style for all files -IGFD_FileStyleByTypeDir // define style for all dir -IGFD_FileStyleByTypeLink // define style for all link -IGFD_FileStyleByExtention // define style by extention, for files or links -IGFD_FileStyleByFullName // define style for particular file/dir/link full name (filename + extention) -IGFD_FileStyleByContainedInFullName // define style for file/dir/link when criteria is contained in full name +IGFD_FileStyle_None // define none style +IGFD_FileStyleByTypeFile // define style for all files +IGFD_FileStyleByTypeDir // define style for all dir +IGFD_FileStyleByTypeLink // define style for all link +IGFD_FileStyleByExtention // define style by extention, for files or links +IGFD_FileStyleByFullName // define style for particular file/dir/link full name (filename + extention) +IGFD_FileStyleByContainedInFullName // define style for file/dir/link when criteria is contained in full name samples : @@ -341,14 +341,14 @@ define if a dialog will be for Open or Save behavior. (and its wanted :) ) Example code For Standard Dialog : Example code : ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", - ICON_IGFD_SAVE " Choose a File", filters, - ".", "", 1, nullptr, ImGuiFileDialogFlags_ConfirmOverwrite); + ICON_IGFD_SAVE " Choose a File", filters, + ".", "", 1, nullptr, ImGuiFileDialogFlags_ConfirmOverwrite); Example code For Modal Dialog : Example code : ImGuiFileDialog::Instance()->OpenModal("ChooseFileDlgKey", - ICON_IGFD_SAVE " Choose a File", filters, - ".", "", 1, nullptr, ImGuiFileDialogFlags_ConfirmOverwrite); + ICON_IGFD_SAVE " Choose a File", filters, + ".", "", 1, nullptr, ImGuiFileDialogFlags_ConfirmOverwrite); This dialog will only verify the file in the file field. So Not to be used with GetSelection() @@ -372,8 +372,8 @@ Example code : ----------------------------------------------------------------------------------------------------------------- flag must be specified in OpenDialog or OpenModal -* ImGuiFileDialogFlags_ConfirmOverwrite => show confirm to overwrite dialog -* ImGuiFileDialogFlags_DontShowHiddenFiles => dont show hidden file (file starting with a .) +* ImGuiFileDialogFlags_ConfirmOverwrite => show confirm to overwrite dialog +* ImGuiFileDialogFlags_DontShowHiddenFiles => dont show hidden file (file starting with a .) ----------------------------------------------------------------------------------------------------------------- ## Open / Save dialog Behavior : @@ -419,43 +419,43 @@ Example code : // Create thumbnails texture ImGuiFileDialog::Instance()->SetCreateThumbnailCallback([](IGFD_Thumbnail_Info *vThumbnail_Info) -> void { - if (vThumbnail_Info && - vThumbnail_Info->isReadyToUpload && - vThumbnail_Info->textureFileDatas) - { - GLuint textureId = 0; - glGenTextures(1, &textureId); - vThumbnail_Info->textureID = (void*)textureId; + if (vThumbnail_Info && + vThumbnail_Info->isReadyToUpload && + vThumbnail_Info->textureFileDatas) + { + GLuint textureId = 0; + glGenTextures(1, &textureId); + vThumbnail_Info->textureID = (void*)textureId; - glBindTexture(GL_TEXTURE_2D, textureId); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, - (GLsizei)vThumbnail_Info->textureWidth, (GLsizei)vThumbnail_Info->textureHeight, - 0, GL_RGBA, GL_UNSIGNED_BYTE, vThumbnail_Info->textureFileDatas); - glFinish(); - glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(GL_TEXTURE_2D, textureId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + (GLsizei)vThumbnail_Info->textureWidth, (GLsizei)vThumbnail_Info->textureHeight, + 0, GL_RGBA, GL_UNSIGNED_BYTE, vThumbnail_Info->textureFileDatas); + glFinish(); + glBindTexture(GL_TEXTURE_2D, 0); - delete[] vThumbnail_Info->textureFileDatas; - vThumbnail_Info->textureFileDatas = nullptr; + delete[] vThumbnail_Info->textureFileDatas; + vThumbnail_Info->textureFileDatas = nullptr; - vThumbnail_Info->isReadyToUpload = false; - vThumbnail_Info->isReadyToDisplay = true; - } + vThumbnail_Info->isReadyToUpload = false; + vThumbnail_Info->isReadyToDisplay = true; + } }); Example code : // Destroy thumbnails texture ImGuiFileDialog::Instance()->SetDestroyThumbnailCallback([](IGFD_Thumbnail_Info* vThumbnail_Info) { - if (vThumbnail_Info) - { - GLuint texID = (GLuint)vThumbnail_Info->textureID; - glDeleteTextures(1, &texID); - glFinish(); - } + if (vThumbnail_Info) + { + GLuint texID = (GLuint)vThumbnail_Info->textureID; + glDeleteTextures(1, &texID); + glFinish(); + } }); Example code : @@ -477,17 +477,17 @@ ImGuiFileDialog *cfileDialog = IGFD_Create(); // open dialog if (igButton("Open File", buttonSize)) { - IGFD_OpenDialog(cfiledialog, - "filedlg", // dialog key (make it possible to have different treatment reagrding the dialog key - "Open a File", // dialog title - "c files(*.c *.h){.c,.h}", // dialog filter syntax : simple => .h,.c,.pp, etc and collections : text1{filter0,filter1,filter2}, text2{filter0,filter1,filter2}, etc.. - ".", // base directory for files scan - "", // base filename - 0, // a fucntion for display a right pane if you want - 0.0f, // base width of the pane - 0, // count selection : 0 infinite, 1 one file (default), n (n files) - "User data !", // some user datas - ImGuiFileDialogFlags_ConfirmOverwrite); // ImGuiFileDialogFlags + IGFD_OpenDialog(cfiledialog, + "filedlg", // dialog key (make it possible to have different treatment reagrding the dialog key + "Open a File", // dialog title + "c files(*.c *.h){.c,.h}", // dialog filter syntax : simple => .h,.c,.pp, etc and collections : text1{filter0,filter1,filter2}, text2{filter0,filter1,filter2}, etc.. + ".", // base directory for files scan + "", // base filename + 0, // a fucntion for display a right pane if you want + 0.0f, // base width of the pane + 0, // count selection : 0 infinite, 1 one file (default), n (n files) + "User data !", // some user datas + ImGuiFileDialogFlags_ConfirmOverwrite); // ImGuiFileDialogFlags } ImGuiIO* ioptr = igGetIO(); @@ -501,34 +501,34 @@ minSize.y = maxSize.y * 0.25f; // display dialog if (IGFD_DisplayDialog(cfiledialog, "filedlg", ImGuiWindowFlags_NoCollapse, minSize, maxSize)) { - if (IGFD_IsOk(cfiledialog)) // result ok - { - char* cfilePathName = IGFD_GetFilePathName(cfiledialog); - printf("GetFilePathName : %s\n", cfilePathName); - char* cfilePath = IGFD_GetCurrentPath(cfiledialog); - printf("GetCurrentPath : %s\n", cfilePath); - char* cfilter = IGFD_GetCurrentFilter(cfiledialog); - printf("GetCurrentFilter : %s\n", cfilter); - // here convert from string because a string was passed as a userDatas, but it can be what you want - void* cdatas = IGFD_GetUserDatas(cfiledialog); - if (cdatas) - printf("GetUserDatas : %s\n", (const char*)cdatas); - struct IGFD_Selection csel = IGFD_GetSelection(cfiledialog); // multi selection - printf("Selection :\n"); - for (int i = 0; i < (int)csel.count; i++) - { - printf("(%i) FileName %s => path %s\n", i, csel.table[i].fileName, csel.table[i].filePathName); - } - // action + if (IGFD_IsOk(cfiledialog)) // result ok + { + char* cfilePathName = IGFD_GetFilePathName(cfiledialog); + printf("GetFilePathName : %s\n", cfilePathName); + char* cfilePath = IGFD_GetCurrentPath(cfiledialog); + printf("GetCurrentPath : %s\n", cfilePath); + char* cfilter = IGFD_GetCurrentFilter(cfiledialog); + printf("GetCurrentFilter : %s\n", cfilter); + // here convert from string because a string was passed as a userDatas, but it can be what you want + void* cdatas = IGFD_GetUserDatas(cfiledialog); + if (cdatas) + printf("GetUserDatas : %s\n", (const char*)cdatas); + struct IGFD_Selection csel = IGFD_GetSelection(cfiledialog); // multi selection + printf("Selection :\n"); + for (int i = 0; i < (int)csel.count; i++) + { + printf("(%i) FileName %s => path %s\n", i, csel.table[i].fileName, csel.table[i].filePathName); + } + // action - // destroy - if (cfilePathName) free(cfilePathName); - if (cfilePath) free(cfilePath); - if (cfilter) free(cfilter); + // destroy + if (cfilePathName) free(cfilePathName); + if (cfilePath) free(cfilePath); + if (cfilter) free(cfilter); - IGFD_Selection_DestroyContent(&csel); - } - IGFD_CloseDialog(cfiledialog); + IGFD_Selection_DestroyContent(&csel); + } + IGFD_CloseDialog(cfiledialog); } // destroy ImGuiFileDialog @@ -572,9 +572,9 @@ ImGuiFontStudio is using also ImGuiFileDialog. #define IMGUIFILEDIALOG_H #if defined(__WIN32__) || defined(_WIN32) - #ifndef WIN32 - #define WIN32 - #endif // WIN32 + #ifndef WIN32 + #define WIN32 + #endif // WIN32 #endif // defined(__WIN32__) || defined(_WIN32) #define IMGUIFILEDIALOG_VERSION "v0.6.4" @@ -589,43 +589,43 @@ ImGuiFontStudio is using also ImGuiFileDialog. typedef int IGFD_FileStyleFlags; // -> enum IGFD_FileStyleFlags_ enum IGFD_FileStyleFlags_ // by evaluation / priority order { - IGFD_FileStyle_None = 0, // define none style - IGFD_FileStyleByTypeFile = (1 << 0), // define style for all files - IGFD_FileStyleByTypeDir = (1 << 1), // define style for all dir - IGFD_FileStyleByTypeLink = (1 << 2), // define style for all link - IGFD_FileStyleByExtention = (1 << 3), // define style by extention, for files or links - IGFD_FileStyleByFullName = (1 << 4), // define style for particular file/dir/link full name (filename + extention) - IGFD_FileStyleByContainedInFullName = (1 << 5), // define style for file/dir/link when criteria is contained in full name + IGFD_FileStyle_None = 0, // define none style + IGFD_FileStyleByTypeFile = (1 << 0), // define style for all files + IGFD_FileStyleByTypeDir = (1 << 1), // define style for all dir + IGFD_FileStyleByTypeLink = (1 << 2), // define style for all link + IGFD_FileStyleByExtention = (1 << 3), // define style by extention, for files or links + IGFD_FileStyleByFullName = (1 << 4), // define style for particular file/dir/link full name (filename + extention) + IGFD_FileStyleByContainedInFullName = (1 << 5), // define style for file/dir/link when criteria is contained in full name }; typedef int ImGuiFileDialogFlags; // -> enum ImGuiFileDialogFlags_ enum ImGuiFileDialogFlags_ { - ImGuiFileDialogFlags_None = 0, - ImGuiFileDialogFlags_ConfirmOverwrite = (1 << 0), // show confirm to overwrite dialog - ImGuiFileDialogFlags_DontShowHiddenFiles = (1 << 1), // dont show hidden file (file starting with a .) - ImGuiFileDialogFlags_DisableCreateDirectoryButton = (1 << 2), // disable the create directory button - ImGuiFileDialogFlags_HideColumnType = (1 << 3), // hide column file type - ImGuiFileDialogFlags_HideColumnSize = (1 << 4), // hide column file size - ImGuiFileDialogFlags_HideColumnDate = (1 << 5), // hide column file date + ImGuiFileDialogFlags_None = 0, + ImGuiFileDialogFlags_ConfirmOverwrite = (1 << 0), // show confirm to overwrite dialog + ImGuiFileDialogFlags_DontShowHiddenFiles = (1 << 1), // dont show hidden file (file starting with a .) + ImGuiFileDialogFlags_DisableCreateDirectoryButton = (1 << 2), // disable the create directory button + ImGuiFileDialogFlags_HideColumnType = (1 << 3), // hide column file type + ImGuiFileDialogFlags_HideColumnSize = (1 << 4), // hide column file size + ImGuiFileDialogFlags_HideColumnDate = (1 << 5), // hide column file date #ifdef USE_THUMBNAILS - ImGuiFileDialogFlags_DisableThumbnailMode = (1 << 6), // disable the thumbnail mode + ImGuiFileDialogFlags_DisableThumbnailMode = (1 << 6), // disable the thumbnail mode #endif - ImGuiFileDialogFlags_Default = ImGuiFileDialogFlags_ConfirmOverwrite + ImGuiFileDialogFlags_Default = ImGuiFileDialogFlags_ConfirmOverwrite }; #ifdef USE_THUMBNAILS struct IGFD_Thumbnail_Info { - int isReadyToDisplay = 0; // ready to be rendered, so texture created - int isReadyToUpload = 0; // ready to upload to gpu - int isLoadingOrLoaded = 0; // was sent to laoding or loaded - void* textureID = 0; // 2d texture id (void* is like ImtextureID type) (GL, DX, VK, Etc..) - unsigned char* textureFileDatas = 0; // file texture datas, will be rested to null after gpu upload - int textureWidth = 0; // width of the texture to upload - int textureHeight = 0; // height of the texture to upload - int textureChannels = 0; // count channels of the texture to upload - void* userDatas = 0; // user datas + int isReadyToDisplay = 0; // ready to be rendered, so texture created + int isReadyToUpload = 0; // ready to upload to gpu + int isLoadingOrLoaded = 0; // was sent to laoding or loaded + void* textureID = 0; // 2d texture id (void* is like ImtextureID type) (GL, DX, VK, Etc..) + unsigned char* textureFileDatas = 0; // file texture datas, will be rested to null after gpu upload + int textureWidth = 0; // width of the texture to upload + int textureHeight = 0; // height of the texture to upload + int textureChannels = 0; // count channels of the texture to upload + void* userDatas = 0; // user datas }; #endif // USE_THUMBNAILS @@ -659,680 +659,680 @@ namespace IGFD #define MAX_PATH_BUFFER_SIZE 1024 #endif // MAX_PATH_BUFFER_SIZE - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - class FileDialogInternal; + class FileDialogInternal; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - class SearchManager - { - public: - std::string puSearchTag; - char puSearchBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = ""; - bool puSearchInputIsActive = false; + class SearchManager + { + public: + std::string puSearchTag; + char puSearchBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = ""; + bool puSearchInputIsActive = false; - public: - void Clear(); // clear datas - void DrawSearchBar(FileDialogInternal& vFileDialogInternal); // draw the search bar - }; + public: + void Clear(); // clear datas + void DrawSearchBar(FileDialogInternal& vFileDialogInternal); // draw the search bar + }; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - class Utils - { - public: - struct PathStruct - { - std::string path; - std::string name; - std::string ext; - bool isOk = false; - }; + class Utils + { + public: + struct PathStruct + { + std::string path; + std::string name; + std::string ext; + bool isOk = false; + }; - public: - static bool Splitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size = -1.0f); - static bool ReplaceString(std::string& str, const std::string& oldStr, const std::string& newStr); - static bool IsDirectoryExist(const std::string& name); - static bool CreateDirectoryIfNotExist(const std::string& name); - static PathStruct ParsePathFileName(const std::string& vPathFileName); - static void AppendToBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr); - static void ResetBuffer(char* vBuffer); - static void SetBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr); + public: + static bool Splitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size = -1.0f); + static bool ReplaceString(std::string& str, const std::string& oldStr, const std::string& newStr); + static bool IsDirectoryExist(const std::string& name); + static bool CreateDirectoryIfNotExist(const std::string& name); + static PathStruct ParsePathFileName(const std::string& vPathFileName); + static void AppendToBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr); + static void ResetBuffer(char* vBuffer); + static void SetBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr); #ifdef WIN32 - static bool WReplaceString(std::wstring& str, const std::wstring& oldStr, const std::wstring& newStr); - static std::vector WSplitStringToVector(const std::wstring& text, char delimiter, bool pushEmpty); - static std::string wstring_to_string(const std::wstring& wstr); - static std::wstring string_to_wstring(const std::string& mbstr); + static bool WReplaceString(std::wstring& str, const std::wstring& oldStr, const std::wstring& newStr); + static std::vector WSplitStringToVector(const std::wstring& text, char delimiter, bool pushEmpty); + static std::string wstring_to_string(const std::wstring& wstr); + static std::wstring string_to_wstring(const std::string& mbstr); #endif - static std::vector SplitStringToVector(const std::string& text, char delimiter, bool pushEmpty); - static std::vector GetDrivesList(); - }; + static std::vector SplitStringToVector(const std::string& text, char delimiter, bool pushEmpty); + static std::vector GetDrivesList(); + }; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - class FileStyle - { - public: - ImVec4 color = ImVec4(0, 0, 0, 0); - std::string icon; - ImFont* font = nullptr; - IGFD_FileStyleFlags flags = 0; + class FileStyle + { + public: + ImVec4 color = ImVec4(0, 0, 0, 0); + std::string icon; + ImFont* font = nullptr; + IGFD_FileStyleFlags flags = 0; - public: - FileStyle(); - FileStyle(const FileStyle& vStyle); - FileStyle(const ImVec4& vColor, const std::string& vIcon = "", ImFont* vFont = nullptr); - }; + public: + FileStyle(); + FileStyle(const FileStyle& vStyle); + FileStyle(const ImVec4& vColor, const std::string& vIcon = "", ImFont* vFont = nullptr); + }; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - class FileInfos; - class FilterManager - { - public: - class FilterInfos - { - public: - std::string filter; - std::set collectionfilters; + class FileInfos; + class FilterManager + { + public: + class FilterInfos + { + public: + std::string filter; + std::set collectionfilters; - public: - void clear(); // clear the datas - bool empty() const; // is filter empty - bool exist(const std::string& vFilter) const; // is filter exist - }; + public: + void clear(); // clear the datas + bool empty() const; // is filter empty + bool exist(const std::string& vFilter) const; // is filter exist + }; - private: - std::vector prParsedFilters; - std::unordered_map>> prFilesStyle; // file infos for file extention only - FilterInfos prSelectedFilter; + private: + std::vector prParsedFilters; + std::unordered_map>> prFilesStyle; // file infos for file extention only + FilterInfos prSelectedFilter; - public: - std::string puDLGFilters; - std::string puDLGdefaultExt; + public: + std::string puDLGFilters; + std::string puDLGdefaultExt; - public: - void ParseFilters(const char* vFilters); // Parse filter syntax, detect and parse filter collection - void SetSelectedFilterWithExt(const std::string& vFilter); // Select filter - - bool prFillFileStyle(std::shared_ptr vFileInfos) const; // fill with the good style - - void SetFileStyle( - const IGFD_FileStyleFlags& vFlags, - const char* vCriteria, - const FileStyle& vInfos); // Set FileStyle - void SetFileStyle( - const IGFD_FileStyleFlags& vFlags, - const char* vCriteria, - const ImVec4& vColor, - const std::string& vIcon, - ImFont* vFont); // link file style to Color and Icon and Font - bool GetFileStyle( - const IGFD_FileStyleFlags& vFlags, - const std::string& vCriteria, - ImVec4* vOutColor, - std::string* vOutIcon, - ImFont** vOutFont); // Get Color and Icon for Filter - void ClearFilesStyle(); // clear prFileStyle + public: + void ParseFilters(const char* vFilters); // Parse filter syntax, detect and parse filter collection + void SetSelectedFilterWithExt(const std::string& vFilter); // Select filter + + bool prFillFileStyle(std::shared_ptr vFileInfos) const; // fill with the good style + + void SetFileStyle( + const IGFD_FileStyleFlags& vFlags, + const char* vCriteria, + const FileStyle& vInfos); // Set FileStyle + void SetFileStyle( + const IGFD_FileStyleFlags& vFlags, + const char* vCriteria, + const ImVec4& vColor, + const std::string& vIcon, + ImFont* vFont); // link file style to Color and Icon and Font + bool GetFileStyle( + const IGFD_FileStyleFlags& vFlags, + const std::string& vCriteria, + ImVec4* vOutColor, + std::string* vOutIcon, + ImFont** vOutFont); // Get Color and Icon for Filter + void ClearFilesStyle(); // clear prFileStyle - bool IsCoveredByFilters(const std::string& vTag) const; // check if current file extention (vTag) is covered by current filter - bool DrawFilterComboBox(FileDialogInternal& vFileDialogInternal); // draw the filter combobox - FilterInfos GetSelectedFilter(); // get the current selected filter - std::string ReplaceExtentionWithCurrentFilter(const std::string& vFile) const; // replace the extention of the current file by the selected filter - void SetDefaultFilterIfNotDefined(); // define the first filter if no filter is selected - }; + bool IsCoveredByFilters(const std::string& vTag) const; // check if current file extention (vTag) is covered by current filter + bool DrawFilterComboBox(FileDialogInternal& vFileDialogInternal); // draw the filter combobox + FilterInfos GetSelectedFilter(); // get the current selected filter + std::string ReplaceExtentionWithCurrentFilter(const std::string& vFile) const; // replace the extention of the current file by the selected filter + void SetDefaultFilterIfNotDefined(); // define the first filter if no filter is selected + }; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - class FileInfos - { - public: - char fileType = ' '; // dirent fileType (f:file, d:directory, l:link) - std::string filePath; // path of the file - std::string fileNameExt; // filename of the file (file name + extention) (but no path) - std::string fileNameExt_optimized; // optimized for search => insensitivecase - std::string fileExt; // extention of the file - size_t fileSize = 0; // for sorting operations - std::string formatedFileSize; // file size formated (10 o, 10 ko, 10 mo, 10 go) - std::string fileModifDate; // file user defined format of the date (data + time by default) - std::shared_ptr fileStyle = nullptr; // style of the file + class FileInfos + { + public: + char fileType = ' '; // dirent fileType (f:file, d:directory, l:link) + std::string filePath; // path of the file + std::string fileNameExt; // filename of the file (file name + extention) (but no path) + std::string fileNameExt_optimized; // optimized for search => insensitivecase + std::string fileExt; // extention of the file + size_t fileSize = 0; // for sorting operations + std::string formatedFileSize; // file size formated (10 o, 10 ko, 10 mo, 10 go) + std::string fileModifDate; // file user defined format of the date (data + time by default) + std::shared_ptr fileStyle = nullptr; // style of the file #ifdef USE_THUMBNAILS - IGFD_Thumbnail_Info thumbnailInfo; // structre for the display for image file tetxure + IGFD_Thumbnail_Info thumbnailInfo; // structre for the display for image file tetxure #endif // USE_THUMBNAILS - public: - bool IsTagFound(const std::string& vTag) const; - }; + public: + bool IsTagFound(const std::string& vTag) const; + }; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - class FileManager - { - public: // types - enum class SortingFieldEnum // sorting for filetering of the file lsit - { - FIELD_NONE = 0, // no sorting preference, result indetermined haha.. - FIELD_FILENAME, // sorted by filename - FIELD_TYPE, // sorted by filetype - FIELD_SIZE, // sorted by filesize (formated file size) - FIELD_DATE, // sorted by filedate + class FileManager + { + public: // types + enum class SortingFieldEnum // sorting for filetering of the file lsit + { + FIELD_NONE = 0, // no sorting preference, result indetermined haha.. + FIELD_FILENAME, // sorted by filename + FIELD_TYPE, // sorted by filetype + FIELD_SIZE, // sorted by filesize (formated file size) + FIELD_DATE, // sorted by filedate #ifdef USE_THUMBNAILS - FIELD_THUMBNAILS, // sorted by thumbnails (comparaison by width then by height) + FIELD_THUMBNAILS, // sorted by thumbnails (comparaison by width then by height) #endif // USE_THUMBNAILS - }; + }; - private: - std::string prCurrentPath; // current path (to be decomposed in prCurrentPathDecomposition - std::vector prCurrentPathDecomposition; // part words - std::vector> prFileList; // base container - std::vector> prFilteredFileList; // filtered container (search, sorting, etc..) - std::string prLastSelectedFileName; // for shift multi selection - std::set prSelectedFileNames; // the user selection of FilePathNames - bool prCreateDirectoryMode = false; // for create directory widget + private: + std::string prCurrentPath; // current path (to be decomposed in prCurrentPathDecomposition + std::vector prCurrentPathDecomposition; // part words + std::vector> prFileList; // base container + std::vector> prFilteredFileList; // filtered container (search, sorting, etc..) + std::string prLastSelectedFileName; // for shift multi selection + std::set prSelectedFileNames; // the user selection of FilePathNames + bool prCreateDirectoryMode = false; // for create directory widget - public: - char puVariadicBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = ""; // called by prSelectableItem - bool puInputPathActivated = false; // show input for path edition - bool puDrivesClicked = false; // event when a drive button is clicked - bool puPathClicked = false; // event when a path button was clicked - char puInputPathBuffer[MAX_PATH_BUFFER_SIZE] = ""; // input path buffer for imgui widget input text (displayed in palce of composer) - char puFileNameBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = ""; // file name buffer in footer for imgui widget input text - char puDirectoryNameBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = ""; // directory name buffer in footer for imgui widget input text (when is directory mode) - std::string puHeaderFileName; // detail view name of column file - std::string puHeaderFileType; // detail view name of column type - std::string puHeaderFileSize; // detail view name of column size - std::string puHeaderFileDate; // detail view name of column date + time + public: + char puVariadicBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = ""; // called by prSelectableItem + bool puInputPathActivated = false; // show input for path edition + bool puDrivesClicked = false; // event when a drive button is clicked + bool puPathClicked = false; // event when a path button was clicked + char puInputPathBuffer[MAX_PATH_BUFFER_SIZE] = ""; // input path buffer for imgui widget input text (displayed in palce of composer) + char puFileNameBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = ""; // file name buffer in footer for imgui widget input text + char puDirectoryNameBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = ""; // directory name buffer in footer for imgui widget input text (when is directory mode) + std::string puHeaderFileName; // detail view name of column file + std::string puHeaderFileType; // detail view name of column type + std::string puHeaderFileSize; // detail view name of column size + std::string puHeaderFileDate; // detail view name of column date + time #ifdef USE_THUMBNAILS - std::string puHeaderFileThumbnails; // detail view name of column thumbnails - bool puSortingDirection[5]; // detail view // true => Descending, false => Ascending + std::string puHeaderFileThumbnails; // detail view name of column thumbnails + bool puSortingDirection[5]; // detail view // true => Descending, false => Ascending #else - bool puSortingDirection[4]; // detail view // true => Descending, false => Ascending + bool puSortingDirection[4]; // detail view // true => Descending, false => Ascending #endif - SortingFieldEnum puSortingField = SortingFieldEnum::FIELD_FILENAME; // detail view sorting column - bool puShowDrives = false; // drives are shown (only on os windows) + SortingFieldEnum puSortingField = SortingFieldEnum::FIELD_FILENAME; // detail view sorting column + bool puShowDrives = false; // drives are shown (only on os windows) bool fileListActuallyEmpty = false; - std::string puDLGpath; // base path set by user when OpenDialog/OpenModal was called + std::string puDLGpath; // base path set by user when OpenDialog/OpenModal was called std::string puError; // last error - std::string puDLGDefaultFileName; // base default file path name set by user when OpenDialog/OpenModal was called - size_t puDLGcountSelectionMax = 1U; // 0 for infinite // base max selection count set by user when OpenDialog/OpenModal was called - bool puDLGDirectoryMode = false; // is directory mode (defiend like : puDLGDirectoryMode = (filters.empty())) + std::string puDLGDefaultFileName; // base default file path name set by user when OpenDialog/OpenModal was called + size_t puDLGcountSelectionMax = 1U; // 0 for infinite // base max selection count set by user when OpenDialog/OpenModal was called + bool puDLGDirectoryMode = false; // is directory mode (defiend like : puDLGDirectoryMode = (filters.empty())) - std::string puFsRoot; + std::string puFsRoot; - private: - static std::string prRoundNumber(double vvalue, int n); // custom rounding number - static std::string prFormatFileSize(size_t vByteSize); // format file size field - static std::string prOptimizeFilenameForSearchOperations(const std::string& vFileNameExt); // turn all text in lower case for search facilitie - static void prCompleteFileInfos(const std::shared_ptr& FileInfos); // set time and date infos of a file (detail view mode) - void prRemoveFileNameInSelection(const std::string& vFileName); // selection : remove a file name - void prAddFileNameInSelection(const std::string& vFileName, bool vSetLastSelectionFileName); // selection : add a file name - void AddFile(const FileDialogInternal& vFileDialogInternal, - const std::string& vPath, const std::string& vFileName, const char& vFileType); // add file called by scandir + private: + static std::string prRoundNumber(double vvalue, int n); // custom rounding number + static std::string prFormatFileSize(size_t vByteSize); // format file size field + static std::string prOptimizeFilenameForSearchOperations(const std::string& vFileNameExt); // turn all text in lower case for search facilitie + static void prCompleteFileInfos(const std::shared_ptr& FileInfos); // set time and date infos of a file (detail view mode) + void prRemoveFileNameInSelection(const std::string& vFileName); // selection : remove a file name + void prAddFileNameInSelection(const std::string& vFileName, bool vSetLastSelectionFileName); // selection : add a file name + void AddFile(const FileDialogInternal& vFileDialogInternal, + const std::string& vPath, const std::string& vFileName, const char& vFileType); // add file called by scandir - public: - FileManager(); - bool IsComposerEmpty(); - size_t GetComposerSize(); - bool IsFileListEmpty(); - bool IsFilteredListEmpty(); - size_t GetFullFileListSize(); - std::shared_ptr GetFullFileAt(size_t vIdx); - size_t GetFilteredListSize(); - std::shared_ptr GetFilteredFileAt(size_t vIdx); - bool IsFileNameSelected(const std::string& vFileName); - std::string GetBack(); - void ClearComposer(); - void ClearFileLists(); // clear file list, will destroy thumbnail textures - void ClearAll(); - void ApplyFilteringOnFileList(const FileDialogInternal& vFileDialogInternal); - void OpenCurrentPath(const FileDialogInternal& vFileDialogInternal); // set the path of the dialog, will launch the directory scan for populate the file listview - void SortFields(const FileDialogInternal& vFileDialogInternal, - const SortingFieldEnum& vSortingField, const bool vCanChangeOrder); // will sort a column - bool GetDrives(); // list drives on windows platform - bool CreateDir(const std::string& vPath); // create a directory on the file system - void ComposeNewPath(std::vector::iterator vIter); // compose a path from the compose path widget - bool SetPathOnParentDirectoryIfAny(); // compose paht on parent directory - std::string GetCurrentPath(); // get the current path - void SetCurrentPath(const std::string& vCurrentPath); // set the current path - static bool IsFileExist(const std::string& vFile); - void SetDefaultFileName(const std::string& vFileName); - bool SelectDirectory(const std::shared_ptr& vInfos); // enter directory - void SelectFileName(const FileDialogInternal& vFileDialogInternal, - const std::shared_ptr& vInfos); // select filename - - //depend of dirent.h - void SetCurrentDir(const std::string& vPath); // define current directory for scan - void ScanDir(const FileDialogInternal& vFileDialogInternal, const std::string& vPath); // scan the directory for retrieve the file list + public: + FileManager(); + bool IsComposerEmpty(); + size_t GetComposerSize(); + bool IsFileListEmpty(); + bool IsFilteredListEmpty(); + size_t GetFullFileListSize(); + std::shared_ptr GetFullFileAt(size_t vIdx); + size_t GetFilteredListSize(); + std::shared_ptr GetFilteredFileAt(size_t vIdx); + bool IsFileNameSelected(const std::string& vFileName); + std::string GetBack(); + void ClearComposer(); + void ClearFileLists(); // clear file list, will destroy thumbnail textures + void ClearAll(); + void ApplyFilteringOnFileList(const FileDialogInternal& vFileDialogInternal); + void OpenCurrentPath(const FileDialogInternal& vFileDialogInternal); // set the path of the dialog, will launch the directory scan for populate the file listview + void SortFields(const FileDialogInternal& vFileDialogInternal, + const SortingFieldEnum& vSortingField, const bool vCanChangeOrder); // will sort a column + bool GetDrives(); // list drives on windows platform + bool CreateDir(const std::string& vPath); // create a directory on the file system + void ComposeNewPath(std::vector::iterator vIter); // compose a path from the compose path widget + bool SetPathOnParentDirectoryIfAny(); // compose paht on parent directory + std::string GetCurrentPath(); // get the current path + void SetCurrentPath(const std::string& vCurrentPath); // set the current path + static bool IsFileExist(const std::string& vFile); + void SetDefaultFileName(const std::string& vFileName); + bool SelectDirectory(const std::shared_ptr& vInfos); // enter directory + void SelectFileName(const FileDialogInternal& vFileDialogInternal, + const std::shared_ptr& vInfos); // select filename + + //depend of dirent.h + void SetCurrentDir(const std::string& vPath); // define current directory for scan + void ScanDir(const FileDialogInternal& vFileDialogInternal, const std::string& vPath); // scan the directory for retrieve the file list - public: - std::string GetResultingPath(); - std::string GetResultingFileName(FileDialogInternal& vFileDialogInternal); - std::string GetResultingFilePathName(FileDialogInternal& vFileDialogInternal); - std::map GetResultingSelection(); + public: + std::string GetResultingPath(); + std::string GetResultingFileName(FileDialogInternal& vFileDialogInternal); + std::string GetResultingFilePathName(FileDialogInternal& vFileDialogInternal); + std::map GetResultingSelection(); - public: - void DrawDirectoryCreation(const FileDialogInternal& vFileDialogInternal); // draw directory creation widget - void DrawPathComposer(const FileDialogInternal& vFileDialogInternal); // draw path composer widget - }; + public: + void DrawDirectoryCreation(const FileDialogInternal& vFileDialogInternal); // draw directory creation widget + void DrawPathComposer(const FileDialogInternal& vFileDialogInternal); // draw path composer widget + }; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #ifdef USE_THUMBNAILS - typedef std::function CreateThumbnailFun; // texture 2d creation function binding - typedef std::function DestroyThumbnailFun; // texture 2d destroy function binding + typedef std::function CreateThumbnailFun; // texture 2d creation function binding + typedef std::function DestroyThumbnailFun; // texture 2d destroy function binding #endif - class ThumbnailFeature - { - protected: - ThumbnailFeature(); - ~ThumbnailFeature(); + class ThumbnailFeature + { + protected: + ThumbnailFeature(); + ~ThumbnailFeature(); - void NewThumbnailFrame(FileDialogInternal& vFileDialogInternal); - void EndThumbnailFrame(FileDialogInternal& vFileDialogInternal); - void QuitThumbnailFrame(FileDialogInternal& vFileDialogInternal); + void NewThumbnailFrame(FileDialogInternal& vFileDialogInternal); + void EndThumbnailFrame(FileDialogInternal& vFileDialogInternal); + void QuitThumbnailFrame(FileDialogInternal& vFileDialogInternal); #ifdef USE_THUMBNAILS - protected: - enum class DisplayModeEnum - { - FILE_LIST = 0, - THUMBNAILS_LIST, - THUMBNAILS_GRID - }; + protected: + enum class DisplayModeEnum + { + FILE_LIST = 0, + THUMBNAILS_LIST, + THUMBNAILS_GRID + }; - private: - uint32_t prCountFiles = 0U; - bool prIsWorking = false; - std::shared_ptr prThumbnailGenerationThread = nullptr; - std::list> prThumbnailFileDatasToGet; // base container - std::mutex prThumbnailFileDatasToGetMutex; - std::list> prThumbnailToCreate; // base container - std::mutex prThumbnailToCreateMutex; - std::list prThumbnailToDestroy; // base container - std::mutex prThumbnailToDestroyMutex; + private: + uint32_t prCountFiles = 0U; + bool prIsWorking = false; + std::shared_ptr prThumbnailGenerationThread = nullptr; + std::list> prThumbnailFileDatasToGet; // base container + std::mutex prThumbnailFileDatasToGetMutex; + std::list> prThumbnailToCreate; // base container + std::mutex prThumbnailToCreateMutex; + std::list prThumbnailToDestroy; // base container + std::mutex prThumbnailToDestroyMutex; - CreateThumbnailFun prCreateThumbnailFun = nullptr; - DestroyThumbnailFun prDestroyThumbnailFun = nullptr; + CreateThumbnailFun prCreateThumbnailFun = nullptr; + DestroyThumbnailFun prDestroyThumbnailFun = nullptr; - protected: - DisplayModeEnum prDisplayMode = DisplayModeEnum::FILE_LIST; + protected: + DisplayModeEnum prDisplayMode = DisplayModeEnum::FILE_LIST; - protected: - // will be call in cpu zone (imgui computations, will call a texture file retrieval thread) - void prStartThumbnailFileDatasExtraction(); // start the thread who will get byte buffer from image files - bool prStopThumbnailFileDatasExtraction(); // stop the thread who will get byte buffer from image files - void prThreadThumbnailFileDatasExtractionFunc(); // the thread who will get byte buffer from image files - void prDrawThumbnailGenerationProgress(); // a little progressbar who will display the texture gen status - void prAddThumbnailToLoad(const std::shared_ptr& vFileInfos); // add texture to load in the thread - void prAddThumbnailToCreate(const std::shared_ptr& vFileInfos); - void prAddThumbnailToDestroy(const IGFD_Thumbnail_Info& vIGFD_Thumbnail_Info); - void prDrawDisplayModeToolBar(); // draw display mode toolbar (file list, thumbnails list, small thumbnails grid, big thumbnails grid) - void prClearThumbnails(FileDialogInternal& vFileDialogInternal); + protected: + // will be call in cpu zone (imgui computations, will call a texture file retrieval thread) + void prStartThumbnailFileDatasExtraction(); // start the thread who will get byte buffer from image files + bool prStopThumbnailFileDatasExtraction(); // stop the thread who will get byte buffer from image files + void prThreadThumbnailFileDatasExtractionFunc(); // the thread who will get byte buffer from image files + void prDrawThumbnailGenerationProgress(); // a little progressbar who will display the texture gen status + void prAddThumbnailToLoad(const std::shared_ptr& vFileInfos); // add texture to load in the thread + void prAddThumbnailToCreate(const std::shared_ptr& vFileInfos); + void prAddThumbnailToDestroy(const IGFD_Thumbnail_Info& vIGFD_Thumbnail_Info); + void prDrawDisplayModeToolBar(); // draw display mode toolbar (file list, thumbnails list, small thumbnails grid, big thumbnails grid) + void prClearThumbnails(FileDialogInternal& vFileDialogInternal); - public: - void SetCreateThumbnailCallback(const CreateThumbnailFun& vCreateThumbnailFun); - void SetDestroyThumbnailCallback(const DestroyThumbnailFun& vCreateThumbnailFun); - - // must be call in gpu zone (rendering, possibly one rendering thread) - void ManageGPUThumbnails(); // in gpu rendering zone, whill create or destroy texture + public: + void SetCreateThumbnailCallback(const CreateThumbnailFun& vCreateThumbnailFun); + void SetDestroyThumbnailCallback(const DestroyThumbnailFun& vCreateThumbnailFun); + + // must be call in gpu zone (rendering, possibly one rendering thread) + void ManageGPUThumbnails(); // in gpu rendering zone, whill create or destroy texture #endif - }; + }; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - class BookMarkFeature - { - protected: - BookMarkFeature(); + class BookMarkFeature + { + protected: + BookMarkFeature(); #ifdef USE_BOOKMARK - private: - struct BookmarkStruct - { - std::string name; // name of the bookmark - - // todo: the path could be relative, better if the app is movedn but bookmarked path can be outside of the app - std::string path; // absolute path of the bookmarked directory - }; + private: + struct BookmarkStruct + { + std::string name; // name of the bookmark + + // todo: the path could be relative, better if the app is movedn but bookmarked path can be outside of the app + std::string path; // absolute path of the bookmarked directory + }; - private: - ImGuiListClipper prBookmarkClipper; - std::vector prBookmarks; - char prBookmarkEditBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = ""; + private: + ImGuiListClipper prBookmarkClipper; + std::vector prBookmarks; + char prBookmarkEditBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = ""; - protected: - float prBookmarkWidth = 200.0f; - bool prBookmarkPaneShown = false; - - protected: - void prDrawBookmarkButton(); // draw bookmark button - bool prDrawBookmarkPane(FileDialogInternal& vFileDialogInternal, const ImVec2& vSize); // draw bookmark Pane + protected: + float prBookmarkWidth = 200.0f; + bool prBookmarkPaneShown = false; + + protected: + void prDrawBookmarkButton(); // draw bookmark button + bool prDrawBookmarkPane(FileDialogInternal& vFileDialogInternal, const ImVec2& vSize); // draw bookmark Pane - public: - std::string SerializeBookmarks(); // serialize bookmarks : return bookmark buffer to save in a file - void DeserializeBookmarks( // deserialize bookmarks : load bookmark buffer to load in the dialog (saved from previous use with SerializeBookmarks()) - const std::string& vBookmarks); // bookmark buffer to load + public: + std::string SerializeBookmarks(); // serialize bookmarks : return bookmark buffer to save in a file + void DeserializeBookmarks( // deserialize bookmarks : load bookmark buffer to load in the dialog (saved from previous use with SerializeBookmarks()) + const std::string& vBookmarks); // bookmark buffer to load #endif // USE_BOOKMARK - }; + }; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // file localization by input chat // widget flashing - class KeyExplorerFeature - { - protected: - KeyExplorerFeature(); + // file localization by input chat // widget flashing + class KeyExplorerFeature + { + protected: + KeyExplorerFeature(); #ifdef USE_EXPLORATION_BY_KEYS - private: - size_t prFlashedItem = 0; // flash when select by char - float prFlashAlpha = 0.0f; // flash when select by char - float prFlashAlphaAttenInSecs = 1.0f; // fps display dependant - size_t prLocateFileByInputChar_lastFileIdx = 0; - ImWchar prLocateFileByInputChar_lastChar = 0; - int prLocateFileByInputChar_InputQueueCharactersSize = 0; - bool prLocateFileByInputChar_lastFound = false; + private: + size_t prFlashedItem = 0; // flash when select by char + float prFlashAlpha = 0.0f; // flash when select by char + float prFlashAlphaAttenInSecs = 1.0f; // fps display dependant + size_t prLocateFileByInputChar_lastFileIdx = 0; + ImWchar prLocateFileByInputChar_lastChar = 0; + int prLocateFileByInputChar_InputQueueCharactersSize = 0; + bool prLocateFileByInputChar_lastFound = false; - protected: - void prLocateByInputKey(FileDialogInternal& vFileDialogInternal); // select a file line in listview according to char key - bool prLocateItem_Loop(FileDialogInternal& vFileDialogInternal, ImWchar vC); // restrat for start of list view if not found a corresponding file - void prExploreWithkeys(FileDialogInternal& vFileDialogInternal, ImGuiID vListViewID); // select file/directory line in listview accroding to up/down enter/backspace keys - static bool prFlashableSelectable( // custom flashing selectable widgets, for flash the selected line in a short time - const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, - bool vFlashing = false, const ImVec2& size = ImVec2(0, 0)); - void prStartFlashItem(size_t vIdx); // define than an item must be flashed - bool prBeginFlashItem(size_t vIdx); // start the flashing of a line in lsit view - static void prEndFlashItem(); // end the fleshing accrdoin to var prFlashAlphaAttenInSecs + protected: + void prLocateByInputKey(FileDialogInternal& vFileDialogInternal); // select a file line in listview according to char key + bool prLocateItem_Loop(FileDialogInternal& vFileDialogInternal, ImWchar vC); // restrat for start of list view if not found a corresponding file + void prExploreWithkeys(FileDialogInternal& vFileDialogInternal, ImGuiID vListViewID); // select file/directory line in listview accroding to up/down enter/backspace keys + static bool prFlashableSelectable( // custom flashing selectable widgets, for flash the selected line in a short time + const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, + bool vFlashing = false, const ImVec2& size = ImVec2(0, 0)); + void prStartFlashItem(size_t vIdx); // define than an item must be flashed + bool prBeginFlashItem(size_t vIdx); // start the flashing of a line in lsit view + static void prEndFlashItem(); // end the fleshing accrdoin to var prFlashAlphaAttenInSecs - public: - void SetFlashingAttenuationInSeconds( // set the flashing time of the line in file list when use exploration keys - float vAttenValue); // set the attenuation (from flashed to not flashed) in seconds + public: + void SetFlashingAttenuationInSeconds( // set the flashing time of the line in file list when use exploration keys + float vAttenValue); // set the attenuation (from flashed to not flashed) in seconds #endif // USE_EXPLORATION_BY_KEYS - }; + }; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - typedef void* UserDatas; - typedef std::function PaneFun; // side pane function binding + typedef void* UserDatas; + typedef std::function PaneFun; // side pane function binding typedef std::function SelectFun; // click on file function binding - class FileDialogInternal - { - public: - FileManager puFileManager; - FilterManager puFilterManager; - SearchManager puSearchManager; + class FileDialogInternal + { + public: + FileManager puFileManager; + FilterManager puFilterManager; + SearchManager puSearchManager; - public: - std::string puName; - bool puShowDialog = false; - ImVec2 puDialogCenterPos = ImVec2(0, 0); // center pos for display the confirm overwrite dialog - int puLastImGuiFrameCount = 0; // to be sure than only one dialog displayed per frame - float puFooterHeight = 0.0f; - bool puCanWeContinue = true; // events - bool puOkResultToConfirm = false; // to confim if ok for OverWrite - bool puIsOk = false; - bool puFileInputIsActive = false; // when input text for file or directory is active - bool puFileListViewIsActive = false; // when list view is active - std::string puDLGkey; - std::string puDLGtitle; - ImGuiFileDialogFlags puDLGflags = ImGuiFileDialogFlags_None; - UserDatas puDLGuserDatas = nullptr; - PaneFun puDLGoptionsPane = nullptr; + public: + std::string puName; + bool puShowDialog = false; + ImVec2 puDialogCenterPos = ImVec2(0, 0); // center pos for display the confirm overwrite dialog + int puLastImGuiFrameCount = 0; // to be sure than only one dialog displayed per frame + float puFooterHeight = 0.0f; + bool puCanWeContinue = true; // events + bool puOkResultToConfirm = false; // to confim if ok for OverWrite + bool puIsOk = false; + bool puFileInputIsActive = false; // when input text for file or directory is active + bool puFileListViewIsActive = false; // when list view is active + std::string puDLGkey; + std::string puDLGtitle; + ImGuiFileDialogFlags puDLGflags = ImGuiFileDialogFlags_None; + UserDatas puDLGuserDatas = nullptr; + PaneFun puDLGoptionsPane = nullptr; SelectFun puDLGselFun = nullptr; - float puDLGoptionsPaneWidth = 0.0f; - bool puDLGmodal = false; - bool puNeedToExitDialog = false; + float puDLGoptionsPaneWidth = 0.0f; + bool puDLGmodal = false; + bool puNeedToExitDialog = false; - bool puUseCustomLocale = false; - int puLocaleCategory = LC_ALL; // locale category to use - std::string puLocaleBegin; // the locale who will be applied at start of the display dialog - std::string puLocaleEnd; // the locale who will be applaied at end of the display dialog + bool puUseCustomLocale = false; + int puLocaleCategory = LC_ALL; // locale category to use + std::string puLocaleBegin; // the locale who will be applied at start of the display dialog + std::string puLocaleEnd; // the locale who will be applaied at end of the display dialog - public: - void NewFrame(); // new frame, so maybe neded to do somethings, like reset events - void EndFrame(); // end frame, so maybe neded to do somethings fater all - void ResetForNewDialog(); // reset what is needed to reset for the openging of a new dialog - }; + public: + void NewFrame(); // new frame, so maybe neded to do somethings, like reset events + void EndFrame(); // end frame, so maybe neded to do somethings fater all + void ResetForNewDialog(); // reset what is needed to reset for the openging of a new dialog + }; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - class FileDialog : - public BookMarkFeature, - public KeyExplorerFeature, - public ThumbnailFeature - { - private: - FileDialogInternal prFileDialogInternal; - ImGuiListClipper prFileListClipper; + class FileDialog : + public BookMarkFeature, + public KeyExplorerFeature, + public ThumbnailFeature + { + private: + FileDialogInternal prFileDialogInternal; + ImGuiListClipper prFileListClipper; - public: - bool puAnyWindowsHovered = false; // not remember why haha :) todo : to check if we can remove + public: + bool puAnyWindowsHovered = false; // not remember why haha :) todo : to check if we can remove double DpiScale; bool singleClickSel; bool mobileMode; std::string homePath; - public: - static FileDialog* Instance() // Singleton for easier accces form anywhere but only one dialog at a time - { - static FileDialog _instance; - return &_instance; - } + public: + static FileDialog* Instance() // Singleton for easier accces form anywhere but only one dialog at a time + { + static FileDialog _instance; + return &_instance; + } - public: - FileDialog(); // ImGuiFileDialog Constructor. can be used for have many dialog at same tiem (not possible with singleton) - virtual ~FileDialog(); // ImGuiFileDialog Destructor + public: + FileDialog(); // ImGuiFileDialog Constructor. can be used for have many dialog at same tiem (not possible with singleton) + virtual ~FileDialog(); // ImGuiFileDialog Destructor - // standard dialog - void OpenDialog( // open simple dialog (path and fileName can be specified) - const std::string& vKey, // key dialog - const std::string& vTitle, // title - const char* vFilters, // filters - const std::string& vPath, // path - const std::string& vFileName, // defaut file name - const int& vCountSelectionMax = 1, // count selection max - UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags - SelectFun vSelectFun = nullptr); // function to be called on file click + // standard dialog + void OpenDialog( // open simple dialog (path and fileName can be specified) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vPath, // path + const std::string& vFileName, // defaut file name + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click - void OpenDialog( // open simple dialog (path and filename are obtained from filePathName) - const std::string& vKey, // key dialog - const std::string& vTitle, // title - const char* vFilters, // filters - const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) - const int& vCountSelectionMax = 1, // count selection max - UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags - SelectFun vSelectFun = nullptr); // function to be called on file click + void OpenDialog( // open simple dialog (path and filename are obtained from filePathName) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click - // with pane - void OpenDialog( // open dialog with custom right pane (path and fileName can be specified) - const std::string& vKey, // key dialog - const std::string& vTitle, // title - const char* vFilters, // filters - const std::string& vPath, // path - const std::string& vFileName, // defaut file name - const PaneFun& vSidePane, // side pane - const float& vSidePaneWidth = 250.0f, // side pane width - const int& vCountSelectionMax = 1, // count selection max - UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags - SelectFun vSelectFun = nullptr); // function to be called on file click + // with pane + void OpenDialog( // open dialog with custom right pane (path and fileName can be specified) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vPath, // path + const std::string& vFileName, // defaut file name + const PaneFun& vSidePane, // side pane + const float& vSidePaneWidth = 250.0f, // side pane width + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click - void OpenDialog( // open dialog with custom right pane (path and filename are obtained from filePathName) - const std::string& vKey, // key dialog - const std::string& vTitle, // title - const char* vFilters, // filters - const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) - const PaneFun& vSidePane, // side pane - const float& vSidePaneWidth = 250.0f, // side pane width - const int& vCountSelectionMax = 1, // count selection max - UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags - SelectFun vSelectFun = nullptr); // function to be called on file click + void OpenDialog( // open dialog with custom right pane (path and filename are obtained from filePathName) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) + const PaneFun& vSidePane, // side pane + const float& vSidePaneWidth = 250.0f, // side pane width + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click - // modal dialog - void OpenModal( // open simple modal (path and fileName can be specified) - const std::string& vKey, // key dialog - const std::string& vTitle, // title - const char* vFilters, // filters - const std::string& vPath, // path - const std::string& vFileName, // defaut file name - const int& vCountSelectionMax = 1, // count selection max - UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags - SelectFun vSelectFun = nullptr); // function to be called on file click + // modal dialog + void OpenModal( // open simple modal (path and fileName can be specified) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vPath, // path + const std::string& vFileName, // defaut file name + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click - void OpenModal( // open simple modal (path and fielname are obtained from filePathName) - const std::string& vKey, // key dialog - const std::string& vTitle, // title - const char* vFilters, // filters - const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) - const int& vCountSelectionMax = 1, // count selection max - UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags - SelectFun vSelectFun = nullptr); // function to be called on file click + void OpenModal( // open simple modal (path and fielname are obtained from filePathName) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click - // with pane - void OpenModal( // open modal with custom right pane (path and filename are obtained from filePathName) - const std::string& vKey, // key dialog - const std::string& vTitle, // title - const char* vFilters, // filters - const std::string& vPath, // path - const std::string& vFileName, // defaut file name - const PaneFun& vSidePane, // side pane - const float& vSidePaneWidth = 250.0f, // side pane width - const int& vCountSelectionMax = 1, // count selection max - UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags - SelectFun vSelectFun = nullptr); // function to be called on file click + // with pane + void OpenModal( // open modal with custom right pane (path and filename are obtained from filePathName) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vPath, // path + const std::string& vFileName, // defaut file name + const PaneFun& vSidePane, // side pane + const float& vSidePaneWidth = 250.0f, // side pane width + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click - void OpenModal( // open modal with custom right pane (path and fielname are obtained from filePathName) - const std::string& vKey, // key dialog - const std::string& vTitle, // title - const char* vFilters, // filters - const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) - const PaneFun& vSidePane, // side pane - const float& vSidePaneWidth = 250.0f, // side pane width - const int& vCountSelectionMax = 1, // count selection max - UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags - SelectFun vSelectFun = nullptr); // function to be called on file click + void OpenModal( // open modal with custom right pane (path and fielname are obtained from filePathName) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) + const PaneFun& vSidePane, // side pane + const float& vSidePaneWidth = 250.0f, // side pane width + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click - // Display / Close dialog form - bool Display( // Display the dialog. return true if a result was obtained (Ok or not) - const std::string& vKey, // key dialog to display (if not the same key as defined by OpenDialog/Modal => no opening) - ImGuiWindowFlags vFlags = ImGuiWindowFlags_NoCollapse, // ImGuiWindowFlags - ImVec2 vMinSize = ImVec2(0, 0), // mininmal size contraint for the ImGuiWindow - ImVec2 vMaxSize = ImVec2(FLT_MAX, FLT_MAX)); // maximal size contraint for the ImGuiWindow - void Close(); // close dialog + // Display / Close dialog form + bool Display( // Display the dialog. return true if a result was obtained (Ok or not) + const std::string& vKey, // key dialog to display (if not the same key as defined by OpenDialog/Modal => no opening) + ImGuiWindowFlags vFlags = ImGuiWindowFlags_NoCollapse, // ImGuiWindowFlags + ImVec2 vMinSize = ImVec2(0, 0), // mininmal size contraint for the ImGuiWindow + ImVec2 vMaxSize = ImVec2(FLT_MAX, FLT_MAX)); // maximal size contraint for the ImGuiWindow + void Close(); // close dialog - // queries - bool WasOpenedThisFrame(const std::string& vKey) const; // say if the dialog key was already opened this frame - bool WasOpenedThisFrame() const; // say if the dialog was already opened this frame - bool IsOpened(const std::string& vKey) const; // say if the key is opened - bool IsOpened() const; // say if the dialog is opened somewhere - std::string GetOpenedKey() const; // return the dialog key who is opened, return nothing if not opened + // queries + bool WasOpenedThisFrame(const std::string& vKey) const; // say if the dialog key was already opened this frame + bool WasOpenedThisFrame() const; // say if the dialog was already opened this frame + bool IsOpened(const std::string& vKey) const; // say if the key is opened + bool IsOpened() const; // say if the dialog is opened somewhere + std::string GetOpenedKey() const; // return the dialog key who is opened, return nothing if not opened - // get result - bool IsOk() const; // true => Dialog Closed with Ok result / false : Dialog closed with cancel result - std::map GetSelection(); // Open File behavior : will return selection via a map - std::string GetFilePathName(); // Save File behavior : will always return the content of the field with current filter extention and current path - std::string GetCurrentFileName(); // Save File behavior : will always return the content of the field with current filter extention - std::string GetCurrentPath(); // will return current path - std::string GetCurrentFilter(); // will return selected filter - UserDatas GetUserDatas() const; // will return user datas send with Open Dialog/Modal + // get result + bool IsOk() const; // true => Dialog Closed with Ok result / false : Dialog closed with cancel result + std::map GetSelection(); // Open File behavior : will return selection via a map + std::string GetFilePathName(); // Save File behavior : will always return the content of the field with current filter extention and current path + std::string GetCurrentFileName(); // Save File behavior : will always return the content of the field with current filter extention + std::string GetCurrentPath(); // will return current path + std::string GetCurrentFilter(); // will return selected filter + UserDatas GetUserDatas() const; // will return user datas send with Open Dialog/Modal - // file style by extentions - void SetFileStyle( // SetExtention datas for have custom display of particular file type - const IGFD_FileStyleFlags& vFlags, // file style - const char* vCriteria, // extention filter to tune - const FileStyle& vInfos); // Filter Extention Struct who contain Color and Icon/Text for the display of the file with extention filter - void SetFileStyle( // SetExtention datas for have custom display of particular file type - const IGFD_FileStyleFlags& vFlags, // file style - const char* vCriteria, // extention filter to tune - const ImVec4& vColor, // wanted color for the display of the file with extention filter - const std::string& vIcon = "", // wanted text or icon of the file with extention filter - ImFont *vFont = nullptr); // wantes font - bool GetFileStyle( // GetExtention datas. return true is extention exist - const IGFD_FileStyleFlags& vFlags, // file style - const std::string& vCriteria, // extention filter (same as used in SetExtentionInfos) - ImVec4* vOutColor, // color to retrieve - std::string* vOutIcon = nullptr, // icon or text to retrieve + // file style by extentions + void SetFileStyle( // SetExtention datas for have custom display of particular file type + const IGFD_FileStyleFlags& vFlags, // file style + const char* vCriteria, // extention filter to tune + const FileStyle& vInfos); // Filter Extention Struct who contain Color and Icon/Text for the display of the file with extention filter + void SetFileStyle( // SetExtention datas for have custom display of particular file type + const IGFD_FileStyleFlags& vFlags, // file style + const char* vCriteria, // extention filter to tune + const ImVec4& vColor, // wanted color for the display of the file with extention filter + const std::string& vIcon = "", // wanted text or icon of the file with extention filter + ImFont *vFont = nullptr); // wantes font + bool GetFileStyle( // GetExtention datas. return true is extention exist + const IGFD_FileStyleFlags& vFlags, // file style + const std::string& vCriteria, // extention filter (same as used in SetExtentionInfos) + ImVec4* vOutColor, // color to retrieve + std::string* vOutIcon = nullptr, // icon or text to retrieve ImFont** vOutFont = nullptr); // font to retreive - void ClearFilesStyle(); // clear extentions setttings + void ClearFilesStyle(); // clear extentions setttings - void SetLocales( // set locales to use before and after the dialog display - const int& vLocaleCategory, // set local category - const std::string& vLocaleBegin, // locale to use at begining of the dialog display - const std::string& vLocaleEnd); // locale to use at the end of the dialog display + void SetLocales( // set locales to use before and after the dialog display + const int& vLocaleCategory, // set local category + const std::string& vLocaleBegin, // locale to use at begining of the dialog display + const std::string& vLocaleEnd); // locale to use at the end of the dialog display - protected: - void NewFrame(); // new frame just at begining of display - void EndFrame(); // end frame just at end of display - void QuitFrame(); // quit frame when qui quit the dialog + protected: + void NewFrame(); // new frame just at begining of display + void EndFrame(); // end frame just at end of display + void QuitFrame(); // quit frame when qui quit the dialog - // others - bool prConfirm_Or_OpenOverWriteFileDialog_IfNeeded( - bool vLastAction, ImGuiWindowFlags vFlags); // treatment of the result, start the confirm to overwrite dialog if needed (if defined with flag) - - public: - // dialog parts - virtual void prDrawHeader(); // draw header part of the dialog (bookmark btn, dir creation, path composer, search bar) - virtual bool prDrawContent(); // draw content part of the dialog (bookmark pane, file list, side pane) - virtual bool prDrawFooter(); // draw footer part of the dialog (file field, fitler combobox, ok/cancel btn's) + // others + bool prConfirm_Or_OpenOverWriteFileDialog_IfNeeded( + bool vLastAction, ImGuiWindowFlags vFlags); // treatment of the result, start the confirm to overwrite dialog if needed (if defined with flag) + + public: + // dialog parts + virtual void prDrawHeader(); // draw header part of the dialog (bookmark btn, dir creation, path composer, search bar) + virtual bool prDrawContent(); // draw content part of the dialog (bookmark pane, file list, side pane) + virtual bool prDrawFooter(); // draw footer part of the dialog (file field, fitler combobox, ok/cancel btn's) - // widgets components - virtual void prDrawSidePane(float vHeight); // draw side pane - virtual int prSelectableItem(int vidx, - std::shared_ptr vInfos, - bool vSelected, const char* vFmt, ...); // draw a custom selectable behavior item - virtual bool prDrawFileListView(ImVec2 vSize); // draw file list view (default mode) + // widgets components + virtual void prDrawSidePane(float vHeight); // draw side pane + virtual int prSelectableItem(int vidx, + std::shared_ptr vInfos, + bool vSelected, const char* vFmt, ...); // draw a custom selectable behavior item + virtual bool prDrawFileListView(ImVec2 vSize); // draw file list view (default mode) #ifdef USE_THUMBNAILS - virtual void prDrawThumbnailsListView(ImVec2 vSize); // draw file list view with small thumbnails on the same line - virtual void prDrawThumbnailsGridView(ImVec2 vSize); // draw a grid of small thumbnails + virtual void prDrawThumbnailsListView(ImVec2 vSize); // draw file list view with small thumbnails on the same line + virtual void prDrawThumbnailsGridView(ImVec2 vSize); // draw a grid of small thumbnails #endif - // to be called only by these function and theirs overrides - // - prDrawFileListView - // - prDrawThumbnailsListView - // - prDrawThumbnailsGridView - void prBeginFileColorIconStyle( - std::shared_ptr vFileInfos, - bool& vOutShowColor, - std::string& vOutStr, - ImFont** vOutFont); // begin style apply of filter with color an icon if any - void prEndFileColorIconStyle( - const bool& vShowColor, - ImFont* vFont); // end style apply of filter - }; + // to be called only by these function and theirs overrides + // - prDrawFileListView + // - prDrawThumbnailsListView + // - prDrawThumbnailsGridView + void prBeginFileColorIconStyle( + std::shared_ptr vFileInfos, + bool& vOutShowColor, + std::string& vOutStr, + ImFont** vOutFont); // begin style apply of filter with color an icon if any + void prEndFileColorIconStyle( + const bool& vShowColor, + ImFont* vFont); // end style apply of filter + }; } typedef IGFD::UserDatas IGFDUserDatas; @@ -1374,229 +1374,229 @@ typedef struct IGFD_Selection IGFD_Selection; struct IGFD_Selection_Pair { - char* fileName; - char* filePathName; + char* fileName; + char* filePathName; }; -IMGUIFILEDIALOG_API IGFD_Selection_Pair IGFD_Selection_Pair_Get(); // return an initialized IGFD_Selection_Pair -IMGUIFILEDIALOG_API void IGFD_Selection_Pair_DestroyContent(IGFD_Selection_Pair* vSelection_Pair); // destroy the content of a IGFD_Selection_Pair +IMGUIFILEDIALOG_API IGFD_Selection_Pair IGFD_Selection_Pair_Get(); // return an initialized IGFD_Selection_Pair +IMGUIFILEDIALOG_API void IGFD_Selection_Pair_DestroyContent(IGFD_Selection_Pair* vSelection_Pair); // destroy the content of a IGFD_Selection_Pair struct IGFD_Selection { - IGFD_Selection_Pair* table; // 0 - size_t count; // 0U + IGFD_Selection_Pair* table; // 0 + size_t count; // 0U }; -IMGUIFILEDIALOG_API IGFD_Selection IGFD_Selection_Get(); // return an initialized IGFD_Selection -IMGUIFILEDIALOG_API void IGFD_Selection_DestroyContent(IGFD_Selection* vSelection); // destroy the content of a IGFD_Selection +IMGUIFILEDIALOG_API IGFD_Selection IGFD_Selection_Get(); // return an initialized IGFD_Selection +IMGUIFILEDIALOG_API void IGFD_Selection_DestroyContent(IGFD_Selection* vSelection); // destroy the content of a IGFD_Selection // constructor / destructor -IMGUIFILEDIALOG_API ImGuiFileDialog* IGFD_Create(void); // create the filedialog context -IMGUIFILEDIALOG_API void IGFD_Destroy(ImGuiFileDialog* vContext); // destroy the filedialog context +IMGUIFILEDIALOG_API ImGuiFileDialog* IGFD_Create(void); // create the filedialog context +IMGUIFILEDIALOG_API void IGFD_Destroy(ImGuiFileDialog* vContext); // destroy the filedialog context -typedef void (*IGFD_PaneFun)(const char*, void*, bool*); // callback fucntion for display the pane +typedef void (*IGFD_PaneFun)(const char*, void*, bool*); // callback fucntion for display the pane #ifdef USE_THUMBNAILS -typedef void (*IGFD_CreateThumbnailFun)(IGFD_Thumbnail_Info*); // callback function for create thumbnail texture -typedef void (*IGFD_DestroyThumbnailFun)(IGFD_Thumbnail_Info*); // callback fucntion for destroy thumbnail texture +typedef void (*IGFD_CreateThumbnailFun)(IGFD_Thumbnail_Info*); // callback function for create thumbnail texture +typedef void (*IGFD_DestroyThumbnailFun)(IGFD_Thumbnail_Info*); // callback fucntion for destroy thumbnail texture #endif // USE_THUMBNAILS -IMGUIFILEDIALOG_API void IGFD_OpenDialog( // open a standard dialog - ImGuiFileDialog* vContext, // ImGuiFileDialog context - const char* vKey, // key dialog - const char* vTitle, // title - const char* vFilters, // filters/filter collections. set it to null for directory mode - const char* vPath, // path - const char* vFileName, // defaut file name - const int vCountSelectionMax, // count selection max - void* vUserDatas, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags +IMGUIFILEDIALOG_API void IGFD_OpenDialog( // open a standard dialog + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vPath, // path + const char* vFileName, // defaut file name + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags -IMGUIFILEDIALOG_API void IGFD_OpenDialog2( // open a standard dialog - ImGuiFileDialog* vContext, // ImGuiFileDialog context - const char* vKey, // key dialog - const char* vTitle, // title - const char* vFilters, // filters/filter collections. set it to null for directory mode - const char* vFilePathName, // defaut file path name (path and filename witl be extracted from it) - const int vCountSelectionMax, // count selection max - void* vUserDatas, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags +IMGUIFILEDIALOG_API void IGFD_OpenDialog2( // open a standard dialog + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vFilePathName, // defaut file path name (path and filename witl be extracted from it) + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags -IMGUIFILEDIALOG_API void IGFD_OpenPaneDialog( // open a standard dialog with pane - ImGuiFileDialog* vContext, // ImGuiFileDialog context - const char* vKey, // key dialog - const char* vTitle, // title - const char* vFilters, // filters/filter collections. set it to null for directory mode - const char* vPath, // path - const char* vFileName, // defaut file name - const IGFD_PaneFun vSidePane, // side pane - const float vSidePaneWidth, // side pane base width - const int vCountSelectionMax, // count selection max - void* vUserDatas, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags +IMGUIFILEDIALOG_API void IGFD_OpenPaneDialog( // open a standard dialog with pane + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vPath, // path + const char* vFileName, // defaut file name + const IGFD_PaneFun vSidePane, // side pane + const float vSidePaneWidth, // side pane base width + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags -IMGUIFILEDIALOG_API void IGFD_OpenPaneDialog2( // open a standard dialog with pane - ImGuiFileDialog* vContext, // ImGuiFileDialog context - const char* vKey, // key dialog - const char* vTitle, // title - const char* vFilters, // filters/filter collections. set it to null for directory mode - const char* vFilePathName, // defaut file name (path and filename witl be extracted from it) - const IGFD_PaneFun vSidePane, // side pane - const float vSidePaneWidth, // side pane base width - const int vCountSelectionMax, // count selection max - void* vUserDatas, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags +IMGUIFILEDIALOG_API void IGFD_OpenPaneDialog2( // open a standard dialog with pane + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vFilePathName, // defaut file name (path and filename witl be extracted from it) + const IGFD_PaneFun vSidePane, // side pane + const float vSidePaneWidth, // side pane base width + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags -IMGUIFILEDIALOG_API void IGFD_OpenModal( // open a modal dialog - ImGuiFileDialog* vContext, // ImGuiFileDialog context - const char* vKey, // key dialog - const char* vTitle, // title - const char* vFilters, // filters/filter collections. set it to null for directory mode - const char* vPath, // path - const char* vFileName, // defaut file name - const int vCountSelectionMax, // count selection max - void* vUserDatas, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags +IMGUIFILEDIALOG_API void IGFD_OpenModal( // open a modal dialog + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vPath, // path + const char* vFileName, // defaut file name + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags -IMGUIFILEDIALOG_API void IGFD_OpenModal2( // open a modal dialog - ImGuiFileDialog* vContext, // ImGuiFileDialog context - const char* vKey, // key dialog - const char* vTitle, // title - const char* vFilters, // filters/filter collections. set it to null for directory mode - const char* vFilePathName, // defaut file name (path and filename witl be extracted from it) - const int vCountSelectionMax, // count selection max - void* vUserDatas, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags +IMGUIFILEDIALOG_API void IGFD_OpenModal2( // open a modal dialog + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vFilePathName, // defaut file name (path and filename witl be extracted from it) + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags -IMGUIFILEDIALOG_API void IGFD_OpenPaneModal( // open a modal dialog with pane - ImGuiFileDialog* vContext, // ImGuiFileDialog context - const char* vKey, // key dialog - const char* vTitle, // title - const char* vFilters, // filters/filter collections. set it to null for directory mode - const char* vPath, // path - const char* vFileName, // defaut file name - const IGFD_PaneFun vSidePane, // side pane - const float vSidePaneWidth, // side pane base width - const int vCountSelectionMax, // count selection max - void* vUserDatas, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags +IMGUIFILEDIALOG_API void IGFD_OpenPaneModal( // open a modal dialog with pane + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vPath, // path + const char* vFileName, // defaut file name + const IGFD_PaneFun vSidePane, // side pane + const float vSidePaneWidth, // side pane base width + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags -IMGUIFILEDIALOG_API void IGFD_OpenPaneModal2( // open a modal dialog with pane - ImGuiFileDialog* vContext, // ImGuiFileDialog context - const char* vKey, // key dialog - const char* vTitle, // title - const char* vFilters, // filters/filter collections. set it to null for directory mode - const char* vFilePathName, // defaut file name (path and filename witl be extracted from it) - const IGFD_PaneFun vSidePane, // side pane - const float vSidePaneWidth, // side pane base width - const int vCountSelectionMax, // count selection max - void* vUserDatas, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags +IMGUIFILEDIALOG_API void IGFD_OpenPaneModal2( // open a modal dialog with pane + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vFilePathName, // defaut file name (path and filename witl be extracted from it) + const IGFD_PaneFun vSidePane, // side pane + const float vSidePaneWidth, // side pane base width + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags -IMGUIFILEDIALOG_API bool IGFD_DisplayDialog( // Display the dialog - ImGuiFileDialog* vContext, // ImGuiFileDialog context - const char* vKey, // key dialog to display (if not the same key as defined by OpenDialog/Modal => no opening) - ImGuiWindowFlags vFlags, // ImGuiWindowFlags - ImVec2 vMinSize, // mininmal size contraint for the ImGuiWindow - ImVec2 vMaxSize); // maximal size contraint for the ImGuiWindow +IMGUIFILEDIALOG_API bool IGFD_DisplayDialog( // Display the dialog + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog to display (if not the same key as defined by OpenDialog/Modal => no opening) + ImGuiWindowFlags vFlags, // ImGuiWindowFlags + ImVec2 vMinSize, // mininmal size contraint for the ImGuiWindow + ImVec2 vMaxSize); // maximal size contraint for the ImGuiWindow -IMGUIFILEDIALOG_API void IGFD_CloseDialog( // Close the dialog - ImGuiFileDialog* vContext); // ImGuiFileDialog context +IMGUIFILEDIALOG_API void IGFD_CloseDialog( // Close the dialog + ImGuiFileDialog* vContext); // ImGuiFileDialog context -IMGUIFILEDIALOG_API bool IGFD_IsOk( // true => Dialog Closed with Ok result / false : Dialog closed with cancel result - ImGuiFileDialog* vContext); // ImGuiFileDialog context +IMGUIFILEDIALOG_API bool IGFD_IsOk( // true => Dialog Closed with Ok result / false : Dialog closed with cancel result + ImGuiFileDialog* vContext); // ImGuiFileDialog context -IMGUIFILEDIALOG_API bool IGFD_WasKeyOpenedThisFrame( // say if the dialog key was already opened this frame - ImGuiFileDialog* vContext, // ImGuiFileDialog context - const char* vKey); +IMGUIFILEDIALOG_API bool IGFD_WasKeyOpenedThisFrame( // say if the dialog key was already opened this frame + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey); -IMGUIFILEDIALOG_API bool IGFD_WasOpenedThisFrame( // say if the dialog was already opened this frame - ImGuiFileDialog* vContext); // ImGuiFileDialog context +IMGUIFILEDIALOG_API bool IGFD_WasOpenedThisFrame( // say if the dialog was already opened this frame + ImGuiFileDialog* vContext); // ImGuiFileDialog context -IMGUIFILEDIALOG_API bool IGFD_IsKeyOpened( // say if the dialog key is opened - ImGuiFileDialog* vContext, // ImGuiFileDialog context - const char* vCurrentOpenedKey); // the dialog key +IMGUIFILEDIALOG_API bool IGFD_IsKeyOpened( // say if the dialog key is opened + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vCurrentOpenedKey); // the dialog key -IMGUIFILEDIALOG_API bool IGFD_IsOpened( // say if the dialog is opened somewhere - ImGuiFileDialog* vContext); // ImGuiFileDialog context +IMGUIFILEDIALOG_API bool IGFD_IsOpened( // say if the dialog is opened somewhere + ImGuiFileDialog* vContext); // ImGuiFileDialog context -IMGUIFILEDIALOG_API IGFD_Selection IGFD_GetSelection( // Open File behavior : will return selection via a map - ImGuiFileDialog* vContext); // ImGuiFileDialog context +IMGUIFILEDIALOG_API IGFD_Selection IGFD_GetSelection( // Open File behavior : will return selection via a map + ImGuiFileDialog* vContext); // ImGuiFileDialog context -IMGUIFILEDIALOG_API char* IGFD_GetFilePathName( // Save File behavior : will always return the content of the field with current filter extention and current path - ImGuiFileDialog* vContext); // ImGuiFileDialog context +IMGUIFILEDIALOG_API char* IGFD_GetFilePathName( // Save File behavior : will always return the content of the field with current filter extention and current path + ImGuiFileDialog* vContext); // ImGuiFileDialog context -IMGUIFILEDIALOG_API char* IGFD_GetCurrentFileName( // Save File behavior : will always return the content of the field with current filter extention - ImGuiFileDialog* vContext); // ImGuiFileDialog context +IMGUIFILEDIALOG_API char* IGFD_GetCurrentFileName( // Save File behavior : will always return the content of the field with current filter extention + ImGuiFileDialog* vContext); // ImGuiFileDialog context -IMGUIFILEDIALOG_API char* IGFD_GetCurrentPath( // will return current path - ImGuiFileDialog* vContext); // ImGuiFileDialog context +IMGUIFILEDIALOG_API char* IGFD_GetCurrentPath( // will return current path + ImGuiFileDialog* vContext); // ImGuiFileDialog context -IMGUIFILEDIALOG_API char* IGFD_GetCurrentFilter( // will return selected filter - ImGuiFileDialog* vContext); // ImGuiFileDialog context +IMGUIFILEDIALOG_API char* IGFD_GetCurrentFilter( // will return selected filter + ImGuiFileDialog* vContext); // ImGuiFileDialog context -IMGUIFILEDIALOG_API void* IGFD_GetUserDatas( // will return user datas send with Open Dialog/Modal - ImGuiFileDialog* vContext); // ImGuiFileDialog context +IMGUIFILEDIALOG_API void* IGFD_GetUserDatas( // will return user datas send with Open Dialog/Modal + ImGuiFileDialog* vContext); // ImGuiFileDialog context -IMGUIFILEDIALOG_API void IGFD_SetFileStyle( // SetExtention datas for have custom display of particular file type - ImGuiFileDialog* vContext, // ImGuiFileDialog context - IGFD_FileStyleFlags vFileStyleFlags, // file style type - const char* vFilter, // extention filter to tune - ImVec4 vColor, // wanted color for the display of the file with extention filter - const char* vIconText, // wanted text or icon of the file with extention filter (can be sued with font icon) - ImFont* vFont); // wanted font pointer +IMGUIFILEDIALOG_API void IGFD_SetFileStyle( // SetExtention datas for have custom display of particular file type + ImGuiFileDialog* vContext, // ImGuiFileDialog context + IGFD_FileStyleFlags vFileStyleFlags, // file style type + const char* vFilter, // extention filter to tune + ImVec4 vColor, // wanted color for the display of the file with extention filter + const char* vIconText, // wanted text or icon of the file with extention filter (can be sued with font icon) + ImFont* vFont); // wanted font pointer -IMGUIFILEDIALOG_API void IGFD_SetFileStyle2( // SetExtention datas for have custom display of particular file type - ImGuiFileDialog* vContext, // ImGuiFileDialog context - IGFD_FileStyleFlags vFileStyleFlags, // file style type - const char* vFilter, // extention filter to tune - float vR, float vG, float vB, float vA, // wanted color channels RGBA for the display of the file with extention filter - const char* vIconText, // wanted text or icon of the file with extention filter (can be sued with font icon) - ImFont* vFont); // wanted font pointer +IMGUIFILEDIALOG_API void IGFD_SetFileStyle2( // SetExtention datas for have custom display of particular file type + ImGuiFileDialog* vContext, // ImGuiFileDialog context + IGFD_FileStyleFlags vFileStyleFlags, // file style type + const char* vFilter, // extention filter to tune + float vR, float vG, float vB, float vA, // wanted color channels RGBA for the display of the file with extention filter + const char* vIconText, // wanted text or icon of the file with extention filter (can be sued with font icon) + ImFont* vFont); // wanted font pointer IMGUIFILEDIALOG_API bool IGFD_GetFileStyle( - ImGuiFileDialog* vContext, // ImGuiFileDialog context - IGFD_FileStyleFlags vFileStyleFlags, // file style type - const char* vFilter, // extention filter (same as used in SetExtentionInfos) - ImVec4* vOutColor, // color to retrieve - char** vOutIconText, // icon or text to retrieve - ImFont** vOutFont); // font pointer to retrived + ImGuiFileDialog* vContext, // ImGuiFileDialog context + IGFD_FileStyleFlags vFileStyleFlags, // file style type + const char* vFilter, // extention filter (same as used in SetExtentionInfos) + ImVec4* vOutColor, // color to retrieve + char** vOutIconText, // icon or text to retrieve + ImFont** vOutFont); // font pointer to retrived -IMGUIFILEDIALOG_API void IGFD_ClearFilesStyle( // clear extentions setttings - ImGuiFileDialog* vContext); // ImGuiFileDialog context +IMGUIFILEDIALOG_API void IGFD_ClearFilesStyle( // clear extentions setttings + ImGuiFileDialog* vContext); // ImGuiFileDialog context -IMGUIFILEDIALOG_API void SetLocales( // set locales to use before and after display - ImGuiFileDialog* vContext, // ImGuiFileDialog context - const int vCategory, // set local category - const char* vBeginLocale, // locale to use at begining of the dialog display - const char* vEndLocale); // locale to set at end of the dialog display +IMGUIFILEDIALOG_API void SetLocales( // set locales to use before and after display + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const int vCategory, // set local category + const char* vBeginLocale, // locale to use at begining of the dialog display + const char* vEndLocale); // locale to set at end of the dialog display #ifdef USE_EXPLORATION_BY_KEYS -IMGUIFILEDIALOG_API void IGFD_SetFlashingAttenuationInSeconds( // set the flashing time of the line in file list when use exploration keys - ImGuiFileDialog* vContext, // ImGuiFileDialog context - float vAttenValue); // set the attenuation (from flashed to not flashed) in seconds +IMGUIFILEDIALOG_API void IGFD_SetFlashingAttenuationInSeconds( // set the flashing time of the line in file list when use exploration keys + ImGuiFileDialog* vContext, // ImGuiFileDialog context + float vAttenValue); // set the attenuation (from flashed to not flashed) in seconds #endif #ifdef USE_BOOKMARK -IMGUIFILEDIALOG_API char* IGFD_SerializeBookmarks( // serialize bookmarks : return bookmark buffer to save in a file - ImGuiFileDialog* vContext); // ImGuiFileDialog context +IMGUIFILEDIALOG_API char* IGFD_SerializeBookmarks( // serialize bookmarks : return bookmark buffer to save in a file + ImGuiFileDialog* vContext); // ImGuiFileDialog context -IMGUIFILEDIALOG_API void IGFD_DeserializeBookmarks( // deserialize bookmarks : load bookmar buffer to load in the dialog (saved from previous use with SerializeBookmarks()) - ImGuiFileDialog* vContext, // ImGuiFileDialog context - const char* vBookmarks); // bookmark buffer to load +IMGUIFILEDIALOG_API void IGFD_DeserializeBookmarks( // deserialize bookmarks : load bookmar buffer to load in the dialog (saved from previous use with SerializeBookmarks()) + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vBookmarks); // bookmark buffer to load #endif #ifdef USE_THUMBNAILS -IMGUIFILEDIALOG_API void SetCreateThumbnailCallback( // define the callback for create the thumbnails texture - ImGuiFileDialog* vContext, // ImGuiFileDialog context - IGFD_CreateThumbnailFun vCreateThumbnailFun); // the callback for create the thumbnails texture +IMGUIFILEDIALOG_API void SetCreateThumbnailCallback( // define the callback for create the thumbnails texture + ImGuiFileDialog* vContext, // ImGuiFileDialog context + IGFD_CreateThumbnailFun vCreateThumbnailFun); // the callback for create the thumbnails texture -IMGUIFILEDIALOG_API void SetDestroyThumbnailCallback( // define the callback for destroy the thumbnails texture - ImGuiFileDialog* vContext, // ImGuiFileDialog context - IGFD_DestroyThumbnailFun vDestroyThumbnailFun); // the callback for destroy the thumbnails texture +IMGUIFILEDIALOG_API void SetDestroyThumbnailCallback( // define the callback for destroy the thumbnails texture + ImGuiFileDialog* vContext, // ImGuiFileDialog context + IGFD_DestroyThumbnailFun vDestroyThumbnailFun); // the callback for destroy the thumbnails texture -IMGUIFILEDIALOG_API void ManageGPUThumbnails( // must be call in gpu zone, possibly a thread, will call the callback for create / destroy the textures - ImGuiFileDialog* vContext); // ImGuiFileDialog context +IMGUIFILEDIALOG_API void ManageGPUThumbnails( // must be call in gpu zone, possibly a thread, will call the callback for create / destroy the textures + ImGuiFileDialog* vContext); // ImGuiFileDialog context #endif // USE_THUMBNAILS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// From 29dfeccfe725e31bde3d9a9a1ecb143dd4064a93 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 16 Jun 2023 17:30:11 -0500 Subject: [PATCH 12/13] change chan osc range - PLEASE READ as of now the range is ~32768, either from -16384 to 16383, or 0 to 32767. it previously was -32768 to 32767 (~65536). this change was made to better suit chips that only output a positive value. if you are working on a new chip, update your code and shift right by one if necessary. --- extern/SAASound/src/SAADevice.cpp | 14 +++++++------- src/engine/platform/amiga.cpp | 2 +- src/engine/platform/arcade.cpp | 4 ++-- src/engine/platform/ay.cpp | 12 ++++++------ src/engine/platform/ay8930.cpp | 6 +++--- src/engine/platform/bubsyswsg.cpp | 2 +- src/engine/platform/c64.cpp | 12 ++++++------ src/engine/platform/dummy.cpp | 2 +- src/engine/platform/es5506.cpp | 2 +- src/engine/platform/fds.cpp | 4 ++-- src/engine/platform/ga20.cpp | 2 +- src/engine/platform/gb.cpp | 2 +- src/engine/platform/genesis.cpp | 18 +++++++++--------- src/engine/platform/k007232.cpp | 4 ++-- src/engine/platform/mmc5.cpp | 6 +++--- src/engine/platform/msm5232.cpp | 2 +- src/engine/platform/msm6258.cpp | 2 +- src/engine/platform/msm6295.cpp | 3 +-- src/engine/platform/n163.cpp | 2 +- src/engine/platform/namcowsg.cpp | 2 +- src/engine/platform/nes.cpp | 20 ++++++++++---------- src/engine/platform/opl.cpp | 12 +++++------- src/engine/platform/opll.cpp | 4 ++-- src/engine/platform/pce.cpp | 2 +- src/engine/platform/pcmdac.cpp | 2 +- src/engine/platform/pokey.cpp | 8 ++++---- src/engine/platform/qsound.cpp | 2 +- src/engine/platform/rf5c68.cpp | 2 +- src/engine/platform/scc.cpp | 2 +- src/engine/platform/segapcm.cpp | 2 +- src/engine/platform/sm8521.cpp | 4 ++-- src/engine/platform/snes.cpp | 2 +- src/engine/platform/sound/lynx/Mikey.cpp | 2 +- src/engine/platform/sound/pokey/AltASAP.cpp | 2 +- src/engine/platform/swan.cpp | 2 +- src/engine/platform/tia.cpp | 4 ++-- src/engine/platform/tx81z.cpp | 2 +- src/engine/platform/vb.cpp | 2 +- src/engine/platform/vera.cpp | 4 ++-- src/engine/platform/vic20.cpp | 2 +- src/engine/platform/vrc6.cpp | 4 ++-- src/engine/platform/x1_010.cpp | 2 +- src/engine/platform/ym2203.cpp | 8 ++++---- src/engine/platform/ym2608.cpp | 16 ++++++++-------- src/engine/platform/ym2610.cpp | 16 ++++++++-------- src/engine/platform/ym2610b.cpp | 16 ++++++++-------- src/engine/platform/ymz280b.cpp | 2 +- src/gui/chanOsc.cpp | 6 +++--- 48 files changed, 126 insertions(+), 129 deletions(-) diff --git a/extern/SAASound/src/SAADevice.cpp b/extern/SAASound/src/SAADevice.cpp index 9064dcab9..78b6e4a43 100644 --- a/extern/SAASound/src/SAADevice.cpp +++ b/extern/SAASound/src/SAADevice.cpp @@ -316,27 +316,27 @@ void CSAADevice::_TickAndOutputStereo(unsigned int& left_mixed, unsigned int& ri m_Noise0.Tick(); m_Noise1.Tick(); m_Amp0.TickAndOutputStereo(temp_left, temp_right); - oscBuf[0]->data[oscBuf[0]->needle++]=(temp_left+temp_right)<<4; + oscBuf[0]->data[oscBuf[0]->needle++]=(temp_left+temp_right)<<3; accum_left += temp_left; accum_right += temp_right; m_Amp1.TickAndOutputStereo(temp_left, temp_right); - oscBuf[1]->data[oscBuf[1]->needle++]=(temp_left+temp_right)<<4; + oscBuf[1]->data[oscBuf[1]->needle++]=(temp_left+temp_right)<<3; accum_left += temp_left; accum_right += temp_right; m_Amp2.TickAndOutputStereo(temp_left, temp_right); - oscBuf[2]->data[oscBuf[2]->needle++]=(temp_left+temp_right)<<4; + oscBuf[2]->data[oscBuf[2]->needle++]=(temp_left+temp_right)<<3; accum_left += temp_left; accum_right += temp_right; m_Amp3.TickAndOutputStereo(temp_left, temp_right); - oscBuf[3]->data[oscBuf[3]->needle++]=(temp_left+temp_right)<<4; + oscBuf[3]->data[oscBuf[3]->needle++]=(temp_left+temp_right)<<3; accum_left += temp_left; accum_right += temp_right; m_Amp4.TickAndOutputStereo(temp_left, temp_right); - oscBuf[4]->data[oscBuf[4]->needle++]=(temp_left+temp_right)<<4; + oscBuf[4]->data[oscBuf[4]->needle++]=(temp_left+temp_right)<<3; accum_left += temp_left; accum_right += temp_right; m_Amp5.TickAndOutputStereo(temp_left, temp_right); - oscBuf[5]->data[oscBuf[5]->needle++]=(temp_left+temp_right)<<4; + oscBuf[5]->data[oscBuf[5]->needle++]=(temp_left+temp_right)<<3; accum_left += temp_left; accum_right += temp_right; } @@ -394,4 +394,4 @@ void CSAADevice::_TickAndOutputSeparate(unsigned int& left_mixed, unsigned int& } left_mixed = accum_left; right_mixed = accum_right; -} \ No newline at end of file +} diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 7c0b8c243..48128bc66 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -168,7 +168,7 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) { outL+=(output*sep2)>>7; outR+=(output*sep1)>>7; } - oscBuf[i]->data[oscBuf[i]->needle++]=(amiga.nextOut[i]*MIN(64,amiga.audVol[i]))<<2; + oscBuf[i]->data[oscBuf[i]->needle++]=(amiga.nextOut[i]*MIN(64,amiga.audVol[i]))<<1; } else { oscBuf[i]->data[oscBuf[i]->needle++]=0; } diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index e617fc0d0..8de0fa3bf 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -76,7 +76,7 @@ void DivPlatformArcade::acquire_nuked(short** buf, size_t len) { } for (int i=0; i<8; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]; + oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]>>1; } if (o[0]<-32768) o[0]=-32768; @@ -111,7 +111,7 @@ void DivPlatformArcade::acquire_ymfm(short** buf, size_t len) { fm_ymfm->generate(&out_ymfm); for (int i=0; i<8; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1)); + oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))>>1; } os[0]=out_ymfm.data[0]; diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 8eaea3fea..d31f0683e 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -187,9 +187,9 @@ void DivPlatformAY8910::acquire(short** buf, size_t len) { buf[0][i]=ayBuf[0][0]; buf[1][i]=buf[0][i]; - oscBuf[0]->data[oscBuf[0]->needle++]=sunsoftVolTable[31-(ay->lastIndx&31)]<<3; - oscBuf[1]->data[oscBuf[1]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>5)&31)]<<3; - oscBuf[2]->data[oscBuf[2]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>10)&31)]<<3; + oscBuf[0]->data[oscBuf[0]->needle++]=sunsoftVolTable[31-(ay->lastIndx&31)]<<2; + oscBuf[1]->data[oscBuf[1]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>5)&31)]<<2; + oscBuf[2]->data[oscBuf[2]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>10)&31)]<<2; } } else { for (size_t i=0; idata[oscBuf[0]->needle++]=ayBuf[0][0]<<2; - oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<2; - oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<2; + oscBuf[0]->data[oscBuf[0]->needle++]=ayBuf[0][0]<<1; + oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<1; + oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<1; } } } diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 8561548d0..515609754 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -186,9 +186,9 @@ void DivPlatformAY8930::acquire(short** buf, size_t len) { buf[1][i]=buf[0][i]; } - oscBuf[0]->data[oscBuf[0]->needle++]=ayBuf[0][0]<<2; - oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<2; - oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<2; + oscBuf[0]->data[oscBuf[0]->needle++]=ayBuf[0][0]<<1; + oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<1; + oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<1; } } diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index 94202de19..bfb1e365d 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -55,7 +55,7 @@ void DivPlatformBubSysWSG::acquire(short** buf, size_t len) { chanOut=chan[i].waveROM[k005289.addr(i)]*(regPool[2+i]&0xf); out+=chanOut; if (writeOscBuf==0) { - oscBuf[i]->data[oscBuf[i]->needle++]=chanOut<<7; + oscBuf[i]->data[oscBuf[i]->needle++]=chanOut<<6; } } } diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 977e39511..43cfbdf0a 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -80,18 +80,18 @@ void DivPlatformC64::acquire(short** buf, size_t len) { sid_fp.clock(4,&buf[0][i]); if (++writeOscBuf>=4) { writeOscBuf=0; - oscBuf[0]->data[oscBuf[0]->needle++]=(sid_fp.lastChanOut[0]-dcOff)>>5; - oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp.lastChanOut[1]-dcOff)>>5; - oscBuf[2]->data[oscBuf[2]->needle++]=(sid_fp.lastChanOut[2]-dcOff)>>5; + oscBuf[0]->data[oscBuf[0]->needle++]=(sid_fp.lastChanOut[0]-dcOff)>>6; + oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp.lastChanOut[1]-dcOff)>>6; + oscBuf[2]->data[oscBuf[2]->needle++]=(sid_fp.lastChanOut[2]-dcOff)>>6; } } else { sid.clock(); buf[0][i]=sid.output(); if (++writeOscBuf>=16) { writeOscBuf=0; - oscBuf[0]->data[oscBuf[0]->needle++]=(sid.last_chan_out[0]-dcOff)>>5; - oscBuf[1]->data[oscBuf[1]->needle++]=(sid.last_chan_out[1]-dcOff)>>5; - oscBuf[2]->data[oscBuf[2]->needle++]=(sid.last_chan_out[2]-dcOff)>>5; + oscBuf[0]->data[oscBuf[0]->needle++]=(sid.last_chan_out[0]-dcOff)>>6; + oscBuf[1]->data[oscBuf[1]->needle++]=(sid.last_chan_out[1]-dcOff)>>6; + oscBuf[2]->data[oscBuf[2]->needle++]=(sid.last_chan_out[2]-dcOff)>>6; } } } diff --git a/src/engine/platform/dummy.cpp b/src/engine/platform/dummy.cpp index 541630de7..04763898d 100644 --- a/src/engine/platform/dummy.cpp +++ b/src/engine/platform/dummy.cpp @@ -32,7 +32,7 @@ void DivPlatformDummy::acquire(short** buf, size_t len) { if (chan[j].active) { if (!isMuted[j]) { chanOut=(((signed short)chan[j].pos)*chan[j].amp*chan[j].vol)>>12; - oscBuf[j]->data[oscBuf[j]->needle++]=chanOut; + oscBuf[j]->data[oscBuf[j]->needle++]=chanOut>>1; out+=chanOut; } else { oscBuf[j]->data[oscBuf[j]->needle++]=0; diff --git a/src/engine/platform/es5506.cpp b/src/engine/platform/es5506.cpp index 5f6e28292..f388e860c 100644 --- a/src/engine/platform/es5506.cpp +++ b/src/engine/platform/es5506.cpp @@ -168,7 +168,7 @@ void DivPlatformES5506::acquire(short** buf, size_t len) { buf[(o<<1)|1][h]=es5506.rout(o); } for (int i=chanMax; i>=0; i--) { - oscBuf[i]->data[oscBuf[i]->needle++]=(es5506.voice_lout(i)+es5506.voice_rout(i))>>5; + oscBuf[i]->data[oscBuf[i]->needle++]=(es5506.voice_lout(i)+es5506.voice_rout(i))>>6; } } } diff --git a/src/engine/platform/fds.cpp b/src/engine/platform/fds.cpp index 420f3e3a1..2674e9549 100644 --- a/src/engine/platform/fds.cpp +++ b/src/engine/platform/fds.cpp @@ -64,7 +64,7 @@ void DivPlatformFDS::acquire_puNES(short* buf, size_t len) { buf[i]=sample; if (++writeOscBuf>=32) { writeOscBuf=0; - oscBuf->data[oscBuf->needle++]=sample<<1; + oscBuf->data[oscBuf->needle++]=sample; } } } @@ -80,7 +80,7 @@ void DivPlatformFDS::acquire_NSFPlay(short* buf, size_t len) { buf[i]=sample; if (++writeOscBuf>=32) { writeOscBuf=0; - oscBuf->data[oscBuf->needle++]=sample<<1; + oscBuf->data[oscBuf->needle++]=sample; } } } diff --git a/src/engine/platform/ga20.cpp b/src/engine/platform/ga20.cpp index 7b9d86a19..607645857 100644 --- a/src/engine/platform/ga20.cpp +++ b/src/engine/platform/ga20.cpp @@ -75,7 +75,7 @@ void DivPlatformGA20::acquire(short** buf, size_t len) { ga20.sound_stream_update(buffer, 1); buf[0][h]=(signed int)(ga20Buf[0][h]+ga20Buf[1][h]+ga20Buf[2][h]+ga20Buf[3][h])>>2; for (int i=0; i<4; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=ga20Buf[i][h]; + oscBuf[i]->data[oscBuf[i]->needle++]=ga20Buf[i][h]>>1; } } } diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 9a5d6d7d4..6a3b92d08 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -74,7 +74,7 @@ void DivPlatformGB::acquire(short** buf, size_t len) { buf[1][i]=gb->apu_output.final_sample.right; for (int i=0; i<4; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=(gb->apu_output.current_sample[i].left+gb->apu_output.current_sample[i].right)<<6; + oscBuf[i]->data[oscBuf[i]->needle++]=(gb->apu_output.current_sample[i].left+gb->apu_output.current_sample[i].right)<<5; } } } diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 8e92c40f3..072e8bc0c 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -184,18 +184,18 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) { if (i==5) { if (fm.dacen) { if (softPCM) { - oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<7; - oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<7; + oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<6; + oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<6; } else { - oscBuf[i]->data[oscBuf[i]->needle++]=fm.dacdata<<7; + oscBuf[i]->data[oscBuf[i]->needle++]=fm.dacdata<<6; oscBuf[6]->data[oscBuf[6]->needle++]=0; } } else { - oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fm.ch_out[i]<<(chipType==2?2:7),-32768,32767); + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fm.ch_out[i]<<(chipType==2?1:6),-32768,32767); oscBuf[6]->data[oscBuf[6]->needle++]=0; } } else { - oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fm.ch_out[i]<<(chipType==2?2:7),-32768,32767); + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fm.ch_out[i]<<(chipType==2?1:6),-32768,32767); } } @@ -243,16 +243,16 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) { //OPN2_Write(&fm,0,0); for (int i=0; i<6; i++) { - int chOut=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6; + int chOut=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<5; if (chOut<-32768) chOut=-32768; if (chOut>32767) chOut=32767; if (i==5) { if (fm_ymfm->debug_dac_enable()) { if (softPCM) { - oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<7; - oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<7; + oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<6; + oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<6; } else { - oscBuf[i]->data[oscBuf[i]->needle++]=fm_ymfm->debug_dac_data()<<7; + oscBuf[i]->data[oscBuf[i]->needle++]=fm_ymfm->debug_dac_data()<<6; oscBuf[6]->data[oscBuf[6]->needle++]=0; } } else { diff --git a/src/engine/platform/k007232.cpp b/src/engine/platform/k007232.cpp index 0b11d45f6..bb0bf7a98 100644 --- a/src/engine/platform/k007232.cpp +++ b/src/engine/platform/k007232.cpp @@ -79,14 +79,14 @@ void DivPlatformK007232::acquire(short** buf, size_t len) { buf[0][h]=(lout[0]+lout[1])<<4; buf[1][h]=(rout[0]+rout[1])<<4; for (int i=0; i<2; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=(lout[i]+rout[i])<<4; + oscBuf[i]->data[oscBuf[i]->needle++]=(lout[i]+rout[i])<<3; } } else { const unsigned char vol=regPool[0xc]; const signed int out[2]={(k007232.output(0)*(vol&0xf)),(k007232.output(1)*((vol>>4)&0xf))}; buf[0][h]=(out[0]+out[1])<<4; for (int i=0; i<2; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=out[i]<<5; + oscBuf[i]->data[oscBuf[i]->needle++]=out[i]<<4; } } } diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 11b04e815..0edc83b2d 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -85,9 +85,9 @@ void DivPlatformMMC5::acquire(short** buf, size_t len) { if (++writeOscBuf>=32) { writeOscBuf=0; - oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:((mmc5->S3.output*10)<<7); - oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:((mmc5->S4.output*10)<<7); - oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:((mmc5->pcm.output*2)<<6); + oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:((mmc5->S3.output*10)<<6); + oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:((mmc5->S4.output*10)<<6); + oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:((mmc5->pcm.output*2)<<5); } } } diff --git a/src/engine/platform/msm5232.cpp b/src/engine/platform/msm5232.cpp index eb8d5cbf5..55df61281 100644 --- a/src/engine/platform/msm5232.cpp +++ b/src/engine/platform/msm5232.cpp @@ -60,7 +60,7 @@ void DivPlatformMSM5232::acquire(short** buf, size_t len) { ((regPool[12+(i>>4)]&2)?((msm->vo8[i]*partVolume[2+(i&4)])>>8):0)+ ((regPool[12+(i>>4)]&4)?((msm->vo4[i]*partVolume[1+(i&4)])>>8):0)+ ((regPool[12+(i>>4)]&8)?((msm->vo2[i]*partVolume[i&4])>>8):0) - )<<3; + )<<2; oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(o,-32768,32767); } diff --git a/src/engine/platform/msm6258.cpp b/src/engine/platform/msm6258.cpp index 6591eda5e..31002a9c4 100644 --- a/src/engine/platform/msm6258.cpp +++ b/src/engine/platform/msm6258.cpp @@ -84,7 +84,7 @@ void DivPlatformMSM6258::acquire(short** buf, size_t len) { } else { buf[0][h]=(msmPan&2)?msmOut:0; buf[1][h]=(msmPan&1)?msmOut:0; - oscBuf[0]->data[oscBuf[0]->needle++]=msmPan?msmOut:0; + oscBuf[0]->data[oscBuf[0]->needle++]=msmPan?(msmOut>>1):0; } } } diff --git a/src/engine/platform/msm6295.cpp b/src/engine/platform/msm6295.cpp index de7fedd5b..2aff0006c 100644 --- a/src/engine/platform/msm6295.cpp +++ b/src/engine/platform/msm6295.cpp @@ -79,9 +79,8 @@ void DivPlatformMSM6295::acquire(short** buf, size_t len) { if (++updateOsc>=22) { updateOsc=0; - // TODO: per-channel osc for (int i=0; i<4; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=msm.voice_out(i)<<6; + oscBuf[i]->data[oscBuf[i]->needle++]=msm.voice_out(i)<<5; } } } diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index ead44cc1e..eee73b990 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -118,7 +118,7 @@ void DivPlatformN163::acquire(short** buf, size_t len) { buf[0][i]=out; if (n163.voice_cycle()==0x78) for (int i=0; i<8; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=n163.voice_out(i)<<7; + oscBuf[i]->data[oscBuf[i]->needle++]=n163.voice_out(i)<<6; } // command queue diff --git a/src/engine/platform/namcowsg.cpp b/src/engine/platform/namcowsg.cpp index 87721eb82..088f1e639 100644 --- a/src/engine/platform/namcowsg.cpp +++ b/src/engine/platform/namcowsg.cpp @@ -177,7 +177,7 @@ void DivPlatformNamcoWSG::acquire(short** buf, size_t len) { }; namco->sound_stream_update(bufC,1); for (int i=0; idata[oscBuf[i]->needle++]=namco->m_channel_list[i].last_out*chans; + oscBuf[i]->data[oscBuf[i]->needle++]=(namco->m_channel_list[i].last_out*chans)>>1; } } } diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index a25568d0c..07f9c5ae5 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -115,11 +115,11 @@ void DivPlatformNES::acquire_puNES(short** buf, size_t len) { buf[0][i]=sample; if (++writeOscBuf>=32) { writeOscBuf=0; - oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:(nes->S1.output<<11); - oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:(nes->S2.output<<11); - oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:(nes->TR.output<<11); - oscBuf[3]->data[oscBuf[3]->needle++]=isMuted[3]?0:(nes->NS.output<<11); - oscBuf[4]->data[oscBuf[4]->needle++]=isMuted[4]?0:(nes->DMC.output<<8); + oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:(nes->S1.output<<10); + oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:(nes->S2.output<<10); + oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:(nes->TR.output<<10); + oscBuf[3]->data[oscBuf[3]->needle++]=isMuted[3]?0:(nes->NS.output<<10); + oscBuf[4]->data[oscBuf[4]->needle++]=isMuted[4]?0:(nes->DMC.output<<7); } } } @@ -142,11 +142,11 @@ void DivPlatformNES::acquire_NSFPlay(short** buf, size_t len) { buf[0][i]=sample; if (++writeOscBuf>=32) { writeOscBuf=0; - oscBuf[0]->data[oscBuf[0]->needle++]=nes1_NP->out[0]<<11; - oscBuf[1]->data[oscBuf[1]->needle++]=nes1_NP->out[1]<<11; - oscBuf[2]->data[oscBuf[2]->needle++]=nes2_NP->out[0]<<11; - oscBuf[3]->data[oscBuf[3]->needle++]=nes2_NP->out[1]<<11; - oscBuf[4]->data[oscBuf[4]->needle++]=nes2_NP->out[2]<<8; + oscBuf[0]->data[oscBuf[0]->needle++]=nes1_NP->out[0]<<10; + oscBuf[1]->data[oscBuf[1]->needle++]=nes1_NP->out[1]<<10; + oscBuf[2]->data[oscBuf[2]->needle++]=nes2_NP->out[0]<<10; + oscBuf[3]->data[oscBuf[3]->needle++]=nes2_NP->out[1]<<10; + oscBuf[4]->data[oscBuf[4]->needle++]=nes2_NP->out[2]<<7; } } } diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index a54667a7a..b39fb8f66 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -211,7 +211,7 @@ void DivPlatformOPL::acquire_nuked(short** buf, size_t len) { if (!isMuted[adpcmChan]) { os[0]-=aOut.data[0]>>3; os[1]-=aOut.data[0]>>3; - oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]=aOut.data[0]; + oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]=aOut.data[0]>>1; } else { oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]=0; } @@ -234,14 +234,13 @@ void DivPlatformOPL::acquire_nuked(short** buf, size_t len) { if (fm.channel[i].out[3]!=NULL) { oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[3]; } - oscBuf[i]->data[oscBuf[i]->needle]<<=1; oscBuf[i]->needle++; } // special - oscBuf[melodicChans+1]->data[oscBuf[melodicChans+1]->needle++]=fm.slot[16].out*6; - oscBuf[melodicChans+2]->data[oscBuf[melodicChans+2]->needle++]=fm.slot[14].out*6; - oscBuf[melodicChans+3]->data[oscBuf[melodicChans+3]->needle++]=fm.slot[17].out*6; - oscBuf[melodicChans+4]->data[oscBuf[melodicChans+4]->needle++]=fm.slot[13].out*6; + oscBuf[melodicChans+1]->data[oscBuf[melodicChans+1]->needle++]=fm.slot[16].out*3; + oscBuf[melodicChans+2]->data[oscBuf[melodicChans+2]->needle++]=fm.slot[14].out*3; + oscBuf[melodicChans+3]->data[oscBuf[melodicChans+3]->needle++]=fm.slot[17].out*3; + oscBuf[melodicChans+4]->data[oscBuf[melodicChans+4]->needle++]=fm.slot[13].out*3; } else { for (int i=0; idata[oscBuf[i]->needle]+=*fm.channel[ch].out[3]; } - oscBuf[i]->data[oscBuf[i]->needle]<<=1; oscBuf[i]->needle++; } } diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index d0effd152..e7486b949 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -68,7 +68,7 @@ void DivPlatformOPLL::acquire_nuked(short** buf, size_t len) { unsigned char nextOut=cycleMapOPLL[fm.cycles]; if ((nextOut>=6 && properDrums) || !isMuted[nextOut]) { os+=(o[0]+o[1]); - if (vrc7 || (fm.rm_enable&0x20)) oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=(o[0]+o[1])<<6; + if (vrc7 || (fm.rm_enable&0x20)) oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=(o[0]+o[1])<<5; } else { if (vrc7 || (fm.rm_enable&0x20)) oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=0; } @@ -76,7 +76,7 @@ void DivPlatformOPLL::acquire_nuked(short** buf, size_t len) { if (!(vrc7 || (fm.rm_enable&0x20))) for (int i=0; i<9; i++) { unsigned char ch=visMapOPLL[i]; if ((i>=6 && properDrums) || !isMuted[ch]) { - oscBuf[ch]->data[oscBuf[ch]->needle++]=(fm.output_ch[i])<<6; + oscBuf[ch]->data[oscBuf[ch]->needle++]=(fm.output_ch[i])<<5; } else { oscBuf[ch]->data[oscBuf[ch]->needle++]=0; } diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index a920a4d06..47e5bbcdd 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -101,7 +101,7 @@ void DivPlatformPCE::acquire(short** buf, size_t len) { pce->ResetTS(0); for (int i=0; i<6; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP((pce->channel[i].blip_prev_samp[0]+pce->channel[i].blip_prev_samp[1])<<1,-32768,32767); + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(pce->channel[i].blip_prev_samp[0]+pce->channel[i].blip_prev_samp[1],-32768,32767); } tempL[0]=(tempL[0]>>1)+(tempL[0]>>2); diff --git a/src/engine/platform/pcmdac.cpp b/src/engine/platform/pcmdac.cpp index 283b6e24e..3a00e431a 100644 --- a/src/engine/platform/pcmdac.cpp +++ b/src/engine/platform/pcmdac.cpp @@ -229,7 +229,7 @@ void DivPlatformPCMDAC::acquire(short** buf, size_t len) { } else { output=output*chan[0].vol*chan[0].envVol/16384; } - oscBuf->data[oscBuf->needle++]=output; + oscBuf->data[oscBuf->needle++]=output>>1; if (outStereo) { buf[0][h]=((output*chan[0].panL)>>(depthScale+8))<>(depthScale+8))<=14) { oscBufDelay=0; - oscBuf[0]->data[oscBuf[0]->needle++]=pokey.outvol_0<<11; - oscBuf[1]->data[oscBuf[1]->needle++]=pokey.outvol_1<<11; - oscBuf[2]->data[oscBuf[2]->needle++]=pokey.outvol_2<<11; - oscBuf[3]->data[oscBuf[3]->needle++]=pokey.outvol_3<<11; + oscBuf[0]->data[oscBuf[0]->needle++]=pokey.outvol_0<<10; + oscBuf[1]->data[oscBuf[1]->needle++]=pokey.outvol_1<<10; + oscBuf[2]->data[oscBuf[2]->needle++]=pokey.outvol_2<<10; + oscBuf[3]->data[oscBuf[3]->needle++]=pokey.outvol_3<<10; } } } diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index b1aeab3e7..d7f908f5b 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -272,7 +272,7 @@ void DivPlatformQSound::acquire(short** buf, size_t len) { buf[1][h]=chip.out[1]; for (int i=0; i<19; i++) { - int data=chip.voice_output[i]<<2; + int data=chip.voice_output[i]<<1; if (data<-32768) data=-32768; if (data>32767) data=32767; oscBuf[i]->data[oscBuf[i]->needle++]=data; diff --git a/src/engine/platform/rf5c68.cpp b/src/engine/platform/rf5c68.cpp index 7be438010..84522c74f 100644 --- a/src/engine/platform/rf5c68.cpp +++ b/src/engine/platform/rf5c68.cpp @@ -74,7 +74,7 @@ void DivPlatformRF5C68::acquire(short** buf, size_t len) { rf5c68.sound_stream_update(bufPtrs,chBufPtrs,blockLen); for (int i=0; i<8; i++) { for (size_t j=0; jdata[oscBuf[i]->needle++]=bufC[i*2][j]+bufC[i*2+1][j]; + oscBuf[i]->data[oscBuf[i]->needle++]=(bufC[i*2][j]+bufC[i*2+1][j])>>1; } } pos+=blockLen; diff --git a/src/engine/platform/scc.cpp b/src/engine/platform/scc.cpp index cd97ff309..e7ad162f0 100644 --- a/src/engine/platform/scc.cpp +++ b/src/engine/platform/scc.cpp @@ -87,7 +87,7 @@ void DivPlatformSCC::acquire(short** buf, size_t len) { buf[0][h]=out; for (int i=0; i<5; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=scc->voice_out(i)<<7; + oscBuf[i]->data[oscBuf[i]->needle++]=scc->voice_out(i)<<6; } } } diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index ce24e2fb0..47411496a 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -49,7 +49,7 @@ void DivPlatformSegaPCM::acquire(short** buf, size_t len) { buf[1][h]=os[1]; for (int i=0; i<16; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=pcm.lastOut[i][0]+pcm.lastOut[i][1]; + oscBuf[i]->data[oscBuf[i]->needle++]=(pcm.lastOut[i][0]+pcm.lastOut[i][1])>>1; } } } diff --git a/src/engine/platform/sm8521.cpp b/src/engine/platform/sm8521.cpp index e1f359c04..1c669a64d 100644 --- a/src/engine/platform/sm8521.cpp +++ b/src/engine/platform/sm8521.cpp @@ -58,9 +58,9 @@ void DivPlatformSM8521::acquire(short** buf, size_t len) { sm8521_sound_tick(&sm8521,8); buf[0][h]=sm8521.out<<6; for (int i=0; i<2; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=sm8521.sg[i].base.out<<6; + oscBuf[i]->data[oscBuf[i]->needle++]=sm8521.sg[i].base.out<<5; } - oscBuf[2]->data[oscBuf[2]->needle++]=sm8521.noise.base.out<<6; + oscBuf[2]->data[oscBuf[2]->needle++]=sm8521.noise.base.out<<5; } } diff --git a/src/engine/platform/snes.cpp b/src/engine/platform/snes.cpp index 0c9734c29..43401ebcb 100644 --- a/src/engine/platform/snes.cpp +++ b/src/engine/platform/snes.cpp @@ -91,7 +91,7 @@ void DivPlatformSNES::acquire(short** buf, size_t len) { next=(next*254)/MAX(1,globalVolL+globalVolR); if (next<-32768) next=-32768; if (next>32767) next=32767; - oscBuf[i]->data[oscBuf[i]->needle++]=next; + oscBuf[i]->data[oscBuf[i]->needle++]=next>>1; } } } diff --git a/src/engine/platform/sound/lynx/Mikey.cpp b/src/engine/platform/sound/lynx/Mikey.cpp index 791336c1b..baf376f54 100644 --- a/src/engine/platform/sound/lynx/Mikey.cpp +++ b/src/engine/platform/sound/lynx/Mikey.cpp @@ -509,7 +509,7 @@ public: } if (oscb!=NULL) { - oscb[i]->data[oscb[i]->needle++]=oscbWrite; + oscb[i]->data[oscb[i]->needle++]=oscbWrite>>1; } } diff --git a/src/engine/platform/sound/pokey/AltASAP.cpp b/src/engine/platform/sound/pokey/AltASAP.cpp index 21bc31a21..9ed2f2077 100644 --- a/src/engine/platform/sound/pokey/AltASAP.cpp +++ b/src/engine/platform/sound/pokey/AltASAP.cpp @@ -39,7 +39,7 @@ static constexpr int MuteInit = 2; static constexpr int MuteSerialInput = 8; //just some magick value to match the audio level of mzpokeysnd static constexpr int16_t MAGICK_VOLUME_BOOSTER = 160; -static constexpr int16_t MAGICK_OSC_VOLUME_BOOSTER = 4; +static constexpr int16_t MAGICK_OSC_VOLUME_BOOSTER = 2; struct PokeyBase { diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 209e2fc2c..46edfd7fb 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -87,7 +87,7 @@ void DivPlatformSwan::acquire(short** buf, size_t len) { buf[0][h]=samp[0]; buf[1][h]=samp[1]; for (int i=0; i<4; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=(ws->sample_cache[i][0]+ws->sample_cache[i][1])<<6; + oscBuf[i]->data[oscBuf[i]->needle++]=(ws->sample_cache[i][0]+ws->sample_cache[i][1])<<5; } } } diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index 1836bee10..cdd116218 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -51,8 +51,8 @@ void DivPlatformTIA::acquire(short** buf, size_t len) { } if (++chanOscCounter>=114) { chanOscCounter=0; - oscBuf[0]->data[oscBuf[0]->needle++]=tia.myChannelOut[0]; - oscBuf[1]->data[oscBuf[1]->needle++]=tia.myChannelOut[1]; + oscBuf[0]->data[oscBuf[0]->needle++]=tia.myChannelOut[0]>>1; + oscBuf[1]->data[oscBuf[1]->needle++]=tia.myChannelOut[1]>>1; } } } diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 935390bcc..c4d921fd1 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -78,7 +78,7 @@ void DivPlatformTX81Z::acquire(short** buf, size_t len) { fm_ymfm->generate(&out_ymfm); for (int i=0; i<8; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1)); + oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))>>1; } os[0]=out_ymfm.data[0]; diff --git a/src/engine/platform/vb.cpp b/src/engine/platform/vb.cpp index 3701b5aa4..6732bdede 100644 --- a/src/engine/platform/vb.cpp +++ b/src/engine/platform/vb.cpp @@ -107,7 +107,7 @@ void DivPlatformVB::acquire(short** buf, size_t len) { tempL=0; tempR=0; for (int i=0; i<6; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=(vb->last_output[i][0]+vb->last_output[i][1])*8; + oscBuf[i]->data[oscBuf[i]->needle++]=(vb->last_output[i][0]+vb->last_output[i][1])*4; tempL+=vb->last_output[i][0]; tempR+=vb->last_output[i][1]; } diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 57e6f29f3..8357f7569 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -107,9 +107,9 @@ void DivPlatformVERA::acquire(short** buf, size_t len) { pos++; for (int i=0; i<16; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=psg->channels[i].lastOut<<4; + oscBuf[i]->data[oscBuf[i]->needle++]=psg->channels[i].lastOut<<3; } - int pcmOut=whyCallItBuf[2][i]+whyCallItBuf[3][i]; + int pcmOut=(whyCallItBuf[2][i]+whyCallItBuf[3][i])>>1; if (pcmOut<-32768) pcmOut=-32768; if (pcmOut>32767) pcmOut=32767; oscBuf[16]->data[oscBuf[16]->needle++]=pcmOut; diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp index bd25b5284..cb78e9b4c 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -69,7 +69,7 @@ void DivPlatformVIC20::acquire(short** buf, size_t len) { vic_sound_machine_calculate_samples(vic,&samp,1,1,0,SAMP_DIVIDER); buf[0][h]=samp; for (int i=0; i<4; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=vic->ch[i].out?(vic->volume<<11):0; + oscBuf[i]->data[oscBuf[i]->needle++]=vic->ch[i].out?(vic->volume<<10):0; } } } diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 2aeb3897d..7834ecbe5 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -87,9 +87,9 @@ void DivPlatformVRC6::acquire(short** buf, size_t len) { if (++writeOscBuf>=32) { writeOscBuf=0; for (int i=0; i<2; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=vrc6.pulse_out(i)<<10; + oscBuf[i]->data[oscBuf[i]->needle++]=vrc6.pulse_out(i)<<9; } - oscBuf[2]->data[oscBuf[2]->needle++]=vrc6.sawtooth_out()<<10; + oscBuf[2]->data[oscBuf[2]->needle++]=vrc6.sawtooth_out()<<9; } // Command part diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 44f94d991..29601ae92 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -222,7 +222,7 @@ void DivPlatformX1_010::acquire(short** buf, size_t len) { if (stereo) buf[1][h]=tempR; for (int i=0; i<16; i++) { - int vo=(x1_010.voice_out(i,0)+x1_010.voice_out(i,1))<<3; + int vo=(x1_010.voice_out(i,0)+x1_010.voice_out(i,1))<<2; oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(vo,-32768,32767); } } diff --git a/src/engine/platform/ym2203.cpp b/src/engine/platform/ym2203.cpp index cc2d1f3f7..69d1ca8a5 100644 --- a/src/engine/platform/ym2203.cpp +++ b/src/engine/platform/ym2203.cpp @@ -231,11 +231,11 @@ void DivPlatformYM2203::acquire_combo(short** buf, size_t len) { buf[0][h]=os; for (int i=0; i<3; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]; + oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]>>1; } for (int i=3; i<6; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]; + oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]>>1; } } } @@ -282,11 +282,11 @@ void DivPlatformYM2203::acquire_ymfm(short** buf, size_t len) { for (int i=0; i<3; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1)); + oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1; } for (int i=3; i<6; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]; + oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]>>1; } } } diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index 7c10e76c2..73280bcbb 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -402,19 +402,19 @@ void DivPlatformYM2608::acquire_combo(short** buf, size_t len) { for (int i=0; idata[oscBuf[i]->needle++]=fm_nuked.ch_out[i]; + oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]>>1; } ssge->get_last_out(ssgOut); for (int i=psgChanOffs; idata[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]; + oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1; } for (int i=adpcmAChanOffs; idata[oscBuf[i]->needle++]=adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1); + oscBuf[i]->data[oscBuf[i]->needle++]=(adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1))>>1; } - oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=abe->get_last_out(0)+abe->get_last_out(1); + oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=(abe->get_last_out(0)+abe->get_last_out(1))>>1; } } @@ -471,19 +471,19 @@ void DivPlatformYM2608::acquire_ymfm(short** buf, size_t len) { buf[1][h]=os[1]; for (int i=0; i<6; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1)); + oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1; } ssge->get_last_out(ssgOut); for (int i=6; i<9; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-6]; + oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-6]>>1; } for (int i=9; i<15; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=adpcmAChan[i-9]->get_last_out(0)+adpcmAChan[i-9]->get_last_out(1); + oscBuf[i]->data[oscBuf[i]->needle++]=(adpcmAChan[i-9]->get_last_out(0)+adpcmAChan[i-9]->get_last_out(1))>>1; } - oscBuf[15]->data[oscBuf[15]->needle++]=abe->get_last_out(0)+abe->get_last_out(1); + oscBuf[15]->data[oscBuf[15]->needle++]=(abe->get_last_out(0)+abe->get_last_out(1))>>1; } } diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index e9cb021d0..303604d61 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -333,19 +333,19 @@ void DivPlatformYM2610::acquire_combo(short** buf, size_t len) { for (int i=0; idata[oscBuf[i]->needle++]=fm_nuked.ch_out[bchOffs[i]]; + oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[bchOffs[i]]>>1; } ssge->get_last_out(ssgOut); for (int i=psgChanOffs; idata[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]; + oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1; } for (int i=adpcmAChanOffs; idata[oscBuf[i]->needle++]=adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1); + oscBuf[i]->data[oscBuf[i]->needle++]=(adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1))>>1; } - oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=abe->get_last_out(0)+abe->get_last_out(1); + oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=(abe->get_last_out(0)+abe->get_last_out(1))>>1; } } @@ -404,19 +404,19 @@ void DivPlatformYM2610::acquire_ymfm(short** buf, size_t len) { buf[1][h]=os[1]; for (int i=0; idata[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1)); + oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1; } ssge->get_last_out(ssgOut); for (int i=psgChanOffs; idata[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]; + oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1; } for (int i=adpcmAChanOffs; idata[oscBuf[i]->needle++]=adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1); + oscBuf[i]->data[oscBuf[i]->needle++]=(adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1))>>1; } - oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=abe->get_last_out(0)+abe->get_last_out(1); + oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=(abe->get_last_out(0)+abe->get_last_out(1))>>1; } } diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 7d28a8016..b4292a14c 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -401,19 +401,19 @@ void DivPlatformYM2610B::acquire_combo(short** buf, size_t len) { for (int i=0; idata[oscBuf[i]->needle++]=fm_nuked.ch_out[i]; + oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]>>1; } ssge->get_last_out(ssgOut); for (int i=psgChanOffs; idata[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]; + oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1; } for (int i=adpcmAChanOffs; idata[oscBuf[i]->needle++]=adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1); + oscBuf[i]->data[oscBuf[i]->needle++]=(adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1))>>1; } - oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=abe->get_last_out(0)+abe->get_last_out(1); + oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=(abe->get_last_out(0)+abe->get_last_out(1))>>1; } } @@ -471,19 +471,19 @@ void DivPlatformYM2610B::acquire_ymfm(short** buf, size_t len) { for (int i=0; idata[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1)); + oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1; } ssge->get_last_out(ssgOut); for (int i=psgChanOffs; idata[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]; + oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1; } for (int i=adpcmAChanOffs; idata[oscBuf[i]->needle++]=adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1); + oscBuf[i]->data[oscBuf[i]->needle++]=(adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1))>>1; } - oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=abe->get_last_out(0)+abe->get_last_out(1); + oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=(abe->get_last_out(0)+abe->get_last_out(1))>>1; } } diff --git a/src/engine/platform/ymz280b.cpp b/src/engine/platform/ymz280b.cpp index a8838ae9a..496f35686 100644 --- a/src/engine/platform/ymz280b.cpp +++ b/src/engine/platform/ymz280b.cpp @@ -76,7 +76,7 @@ void DivPlatformYMZ280B::acquire(short** buf, size_t len) { for (int j=0; j<8; j++) { dataL+=why[j*2][i]; dataR+=why[j*2+1][i]; - oscBuf[j]->data[oscBuf[j]->needle++]=(short)(((int)why[j*2][i]+why[j*2+1][i])/2); + oscBuf[j]->data[oscBuf[j]->needle++]=(short)(((int)why[j*2][i]+why[j*2+1][i])/4); } buf[0][pos]=(short)(dataL/8); buf[1][pos]=(short)(dataR/8); diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index 3e580524a..c278463e6 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -93,7 +93,7 @@ void FurnaceGUI::calcChanOsc() { unsigned short needlePos=buf->needle; needlePos-=displaySize; for (unsigned short i=0; i<512; i++) { - float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f; + float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/32768.0f; if (minLevel>y) minLevel=y; if (maxLeveldata[(unsigned short)(needlePos+(i*displaySize/precision))]/65536.0f; + float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/precision))]/32768.0f; if (minLevel>y) minLevel=y; if (maxLeveldata[(unsigned short)(needlePos+(i*displaySize/precision))]/65536.0f; + float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/precision))]/32768.0f; y-=dcOff; if (y<-0.5f) y=-0.5f; if (y>0.5f) y=0.5f; From d35fa6f1bc02971c95eedcfcb9af2d7c7b3b5fe6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 16 Jun 2023 18:43:33 -0500 Subject: [PATCH 13/13] new demo song by Xan --- demos/snes/changeyourheart.fur | Bin 0 -> 216906 bytes src/gui/about.cpp | 1 + 2 files changed, 1 insertion(+) create mode 100644 demos/snes/changeyourheart.fur diff --git a/demos/snes/changeyourheart.fur b/demos/snes/changeyourheart.fur new file mode 100644 index 0000000000000000000000000000000000000000..7b671c3a26e21f15b0e94c7e225c574c5c2679ae GIT binary patch literal 216906 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

P{(H5euaP;|$t%H=Ai3G!e_Zri zzQu9IIfD65qoO>!!K{DFwJ(bg@(D)Bt%so{ius%txw)uk_WM$V6> zR_FC_t;zea$J2&J(bZEBpB?x0}B_ zevIgbYmDJX;~>4i-&++OgRekt>Vo(W5x2xG1y*LJhI z%B7cZ!z3{`tUERP8g7+MbBj6sEp*rmZX(8uHJ}Y1WMZ~tP(7S<=Ne1~EVsAW2Cyon zfNU0*qV+G^!h0|Cg?86G`}+$_WQ9sJp&Z_K<=2$%_~zeBa(6TDh8d%(RehYFtzq{Z6{{#4c7*xPTN`XppNV{I$r_^%N z*kf1|n8(=~0ydA*#sU|MW-_acn-`gP~`V&M3FiPe=3^;YnadB~2BN$%OZpCp*1V zULB_K^DXQdYTNgCZq6BC3Hw3yh>BkzgQCG6>e2LqOUBm|n%4dKdw@8%_UoO$riMfB z=iLry^q~yM>-OmQ&j@crRKEX{;^p5y+E_wgcY$WJbtvsA_ohEJa1dNfjuJicTdCUY zKTvL$eOF%yIo085NPpp0xxs&5kZ)i{;H%)6(DDF6`d&Uc09Ne|<|G`8i3{H(PvaMe zRgzUg4W&h~zo%c~M^~)vb9`>E8PtC*`@I{$pox3!ge?KU_AU4tx#&dZ0YCNi7pGrYFOI!c`Ir5D!sH_3AsyyPOkMFL&m zx4ZWyd>mLQs}WRw$nSD*+wt1pdiNGq_-W<1o=md==3w8K6*`(qxU@ZCQ)_J z;6+H-$pWd=9TwyS>s=MSG(yFBbK zm2(Sln%L2)pm(zp8*Ga#K3M8%9g}7M)ZAzTC+S~SoMGJ!li4oj)iut+s|m5A7=@*$ zrLputNp#HL;1S|6euw=leWU@BQ2!Wd{GLvaJHZ_^>VW9QDaWI`ggQHLdNlVfiVIVM zF++MBix#Nf`M(GZ2>KSB7mCG1hnf7eDx+$-Z!Lc>91wV=+lIKs*mwJY4kyP1PW>@2 zv+Ge?rV9<;B5BhmA$J)C^5y&`Iie<=bX0f?V@}C2eG|)R-BTQrSykqvpH!Oj_1DKf|K(2*>=L_tCj=GvoKY+dSQvaXv^>-mH6bn{c3PY~p)i)-v82z4W~i)o~T83|HIK#pBBN zHNLHUUbbD^)e>9ps#sBTxK>kBQ@ye3Ud7mQQ)$0)ZH1u9Q#wBXR8eTv@S4kYruy*e zi)Fr5+MKOFCs!?Od0X)$|5VK;^SRm~S@Y_&rn9*}zc3p5TP{>q|Gl5vTK=s0jAON!cce*La!tn#0dKzzK`$nz(ik%Ozr@l(gIv5Pu1#V}&)6aGnviph<>8#g0yzPr9a4)IM#+@Q$7M%~jrm!5=gRk1rk8Fje_FV|uyfgn%DrWO zO8+#TGmq0JR;HB>H#|0fs60@+(Q?Mw-+9K2IZ&v8*5BKW45y>C{}?M+^LTTFCdmQi zBG0%&4Og^vDrW$6{r1-=epN2S==o*2I`2fY7sH z4H3+!?I9NfMPb*&i-K+jhlSbw0+mlyyZpKS!+cW|-4y9Qav9EM@d+QJ0%w!Vud+pf z1|Jn)#}Tu7G1~<9XzT6MyjcK=?r^NOOmwZ1kM`|NUF^tcx#%2Y4XF{j);s;G$7@g7 zmY1!{s;fJw?^1o|ud#S`-OKV_x$*ho<>O1lxxfDY&Pyp?Rc)^AQX(weSGlL=SoPLY z|Lp5Uo!dS&tSNp|j@NH!v{qK-MCRxxXZOB>cz-Pd`M8#a&jX}z^MTEE+}$7wL# zvxMQ3skyXP=sxQz#~``n^Gh;acuKimJ>H+`>k~98;%8WT@bw^E9T0jiQV_8&qAWT! zh8Ei+`g6pv$Su+DIxwPdhLwdp3!M_NCvr#FB>%jCd7(i;Eg?rjo+=Q9ATYu|T{$7} zc=#sY4+5n;)aR%ypSzEJTv=|D$C_t5C=YsZz4 zIo#qCb#v>dRf-GU`FpB1Xtq@#`CCh}G`gy{`BzGgYaF!`i?RzZR&A;IS?;bhY3YvH zh{W{CxZV?nY3(-0dDP-*L34D$>2*P)V29N1;|ZXt_@W1#7M5IeSMf-6Ug#&=>(g603YI|+81INbh}wA) zU(47f8RvOa7C$qjI4$6 z61WJj=U(9+k^u5*b}h3)c1ihJ=*MwWxRl`>w_pa#jDc7mvM-)VtR*3@6XCl+WQwz? zor#NZ8-m$`kT=?AE!SE*w2o|&=_~CP{gLLBmb}KS#t}{P8fH}nmS$A%tP&OuC^%Kw zx!zbG)|gQLZ^he+?Aqj-o28{y+}gjTffdxcOVu+ngzgEy8=24{A(P@^>(#ZrBdMBF!l-YN6B{HE=C8K6pfM8bJDpLBDtUti1oaq zl#0!qD31#4&)4!i(05=sm`-dUpHY8Ot&YXIGp^UbCO6yaXP;_%*p_JA)26ApR>jj? zssE-yHI$k_&6noX#t(IR4Y!GC+^CV(_ODH8OlbL4^}XzBmT$D5Gm+ZE4Nx>uoiDnk{WxtX8|wGEEB_A9#Y$&Bz<%fg{BB$;2`ba}Fj)fqh&j z&D+s9_BYN1;;3UGki&`LG8jF0%cQqN-GwRAFy%|}27a#c-;heb>mtNID{4z{?_f*V ztdN>OQE*wr>@85PzTD{8r6U2l6~LfX!? z4ru(Q>8XjR99|ROoZC85ms%TJGQ0Lp>w>1(n#}3}O_#JawV7qL8;ovq-8Kl#h?_uv={Gi>c33eUGS~3Pp{9iZE8L&(3veC%3_U}1R{Vuk&Wn7n)U#-kya|3o=x3$C^kFQ2@ zn#m@1VjO&ePDRpAu}>&ccwEq*P1Th}*dY8F>! zl`^V_)@$lNRt>7K)-2b2(xla%svX(($q?PPv(2tuuD@*kYhPo(WdCIA<={>TpsHy+UwSmL~{cZQyp`-G*&CHWoW`Fi z50}%F{e51`AN#UZa&aPeq_nq;CDiekb5)!tJRb>HcpSb1WiWn0v*-xLg&!iS={o99 zZ@y<3xPvysljP)rC&}L4K3Fn101Af&5FZ`OEPb_WTJ?rX%NXl0=V|m6l4_Eh-1;hQ zWs|LSpy8Evb5m%;oR*%g^Xp$#zo~7l)6}t=uC-ift<-v(?adTRsx?*jO!vwZW|*rx zXGF}cZ6S>#bPrkwG(@*i%>L$O`o}uHwoe$N!SCKrpa} z@(kqDCNgU1zgh3$!xRiE;hyCNvcrVaWPXyhLO?P=$xvpAPIGHGd-?Cg9c0U-cNI^3 zkNK_$4puiSf>f%oE>YHiWy(JRUBZt?91EJL?ipMix5&CCfd{UgAiboyc@iuUGKaF z#DBT z)!u9@Ym092X-;h?kmcG_#%soYW*R!#J>A4{yaHBXbZd@IgPWLzLz?ME{FK^W~!W( z$4OS1HP~0F@W>uW>0-XHkmq47U_9a0^X|ii%-h0)qS4&@ zEH!rk`x|X2HJs7Fx(|#jQF=S0uqU&UV4-Yg(#h>k7?1trEk1^F!B7cZ%K5G6=b4-)Vpi^~R;zq?VUv ztK*C{$7;4knV;*%>Sk-Ht;<^0wI0_o^}Tf_ZDs4k)_FRCp`Rh%sJESW^|pCU3fCxC zciT_P9cz+jBcSw7v}{C{1I17U?IzI)%ck{WMbLuLFmC|qB<_G)85HJjb{F0=(H6;f z;Rs#}_lavlfEA|o*Me&Hk^Pbk`Tr2$XHf4B#EjstUm4X(k+T4#VK(5<%qGIwysct1I_ zY`)e>NUiIuW23p3xw~Vm>o(#sW$6nHe@suTc~*ycy7`m^vE|#3n}-`G+ckEMX}j)U zZJTbTzM%DF>khq5&oYFXyV<$6kNP2|(bgJ6z2UI+rg^b3$g_06|^AOu- z`)TWD^Ge4M&qmh;$7f5Jwa_D{ZlwLDBr#4i>!7iep|l&U?kocO!Po@X((XgiY#Vnv zcN{B=_g8#~4={Zg2U&ic&Wz)XKK$o`7*;i%$G$5G7H{Ls=ZJ-!;f4E2(Z!a1+F#ru7>l5Fxw~VEMuy%)cV{; zn1))mBYf*IU8L!@d#87|E!Qy6OmpmTSKIBDM7I(>>g?*OMT@+boim(5SD@>d^PEj- zx#gVhk=WDBeQk^EtF*nE9_yQJQrmtVwFS~mu*J2zv==l1|z~MiKZKe@uA`ZG*Zoq_m6Rb$AtTyy&R-7P}a(<`(eo z^MCT6^TonBg70iCrX23Cg4@fJa0#z7bvQ5#d@?>;zoDy%c;YCiX0HFY}7LBR%I(mv^_<&z)fN zw~RDb+oQ3uoWa?Z2Ieb;HQcQL;-FSB)ZM7S$mr;(4= zG$haUy*;w&-i_Xyo-LRZ2eB~R2Y>GU>N)8Foa7wIKKK@CxbF@ZI} zk#?4CBtwWv)K~Pcz#&3O4WVrUHUf4Eg*qR4#Tmy2=nDE>WJw@b^*H4*c@3zbjitn6Rxd!b01RL$cGsQh zNOIPAws`)!*bbR(tQE1(a(a+$_AQn-#@@&w*K!+T?qmIIU*+8G5<8Y5b@mF2#8j&< zu?%&dwBd$dhMDHWwrkcmmSeWb)^Vm0#!1#_C*8?I9@|6gDG1^Gg@|l@9dlhh99m?R z^StxEI~pD8xr&^$b+aw7tw)YIzS>i)^Bf1f)4Zkb-LB=H3q(5El?)=M6ZND6U=cmZ zX^vcS(oA1r|QP8mgUpp_msT8Y0RQn70&&D+(z z#}R_ObRzC~?pdy6&u>hP6?>0(*L%y+-yV^>$(8J0>0OA%c|*M`-NCL`&NjzG=YIE5 z*G9)5=W(amIlz<%e)}TFKIeb-AC`-@qc)utw)Qb~ zGJUngBd^`zo(AV-*FaZ)_g$>iGYQ%3I)&DI=ey=PR=SU)d+<-*bk~3ASPB6SM-fjQ zahh@fJWKeI*Qj%#n{*Fo1d6FUspXV|)M&~Q%35e9^Ba7FaS@)+p2ZG?4UFE*5awxk z7iYep59bDr3Wifo(_TSupdFBgIhCvB%@QVwa(M??6i$OMSf-UW3MA|Y?DhPO+--y?y=N7L4DYJI5Pjn7v)rBGn(LhBY_kVCTHF!t6P|hKes3rD z3AY_L;GaE^rxG2FFY($PiH;h~P6T*qct=nO`cU#gU*KQzFl88|q<^6ff$qX?SjQ@7 zIH>EHKlxkvGuwCJ9BVS~uP9iY&cDUmDEKH+i*JkW^Y5_d^AzH<0w2~mmWKCGKoJ0Z z3vUab&ELwK$m_-L!P7DC!u1?X(1Y8Jv6(S|H4(0-6)-PwqM)UeN0f`yWM~+iOW#F{ zqFto+rD`Z|z`rQN6N>dGkK;VoV~4?0LHH3N&TQnO>x6TqW1nj$`q3pqdN{wkdV5EB zP-Fmd*SX)j0=tiHaZj=r*`B*Q;V02$uG`KP=r(-0=V$v4wzzJ4_)ZIA@Z=Kri5uu< zbR5xm4R0RuUhG1l$)Nf?q{Hpl5Lr*$vDBU*U(b@!(ThJ!KPd3m8VV z0Y2bvpbh9uw?G@HrG%3#rQM?cL(8D`YLC&=&=$A{^BELOKLgp|N$lm!(TpHQHUnc8 zG9#HFE0pDAaali@ft-yT6a1Ya;*8_oU943u zpfu3*c37AE*M|I0Kd9z1!*FDBh7A3L1#{SSz`e5(EB6 zHj?9kTp}EwkM}0_6EE8R(n35KP4k{6mVp((0`LIk4tWS4Mph8xiGRpt{CTY z+db<$XNr5S?Y-qX66%h2e@D98wf3=y$}PqVJxTT@?b=b~$wZs6m0rNBz&WIvtS1>@ zF8C2#0zRV@QyH{Fl7d%KT3K}74pn=z@6nNx7XNT+wV9AxPLffZB@1`cdmDo zJIWRAA>2idEPI5r(UsxwB6cLr$#tc=zPYvDk*MCKaGga9@m5cmE7(2A`yG9TnXt83 zxmV+zhi%6`VOww}j$j*!&v**XBhC@6z*rz0?~WzmUqK8k#@=EdiF2eA*g{F8b*0xq zMGQM*9~{EOnLl9w?#p;hUjZNEPwPydKS9-IR(y4_e=Li>H6c_Dp-2s|Q+woyPw4cJVAl{~_j)GjI*MA8!LLQ?h{<#BH(+ybTTp zirO`N1F(i1LFSWevVrgeRn*(yFyav2O6zlD)wLKABu>!4_ZzWQ0gfx zXfM=>ZlN!xw@^ny`Z2hAAvJKSF#w>c$+bl{DvL(tVX*N$;4#zANN&fn(Mp!mG_vZm#f@W=W##k?qJg#OxUD_-S)m@7s4EHsp(giGcQSM`k;Ngd*D<0ggxQZ~bWdF{>RhuJNt{ z$7a_AZ=L6md%C;c{mt{o)8hGpQi&mC2=JV&#>e180Rw1gcSZrUosfezhq{~Im(fQ5 z2tkaVP-n&}xCX9ae1kSK;+P@uJK7xD8HSw2g;S_9T0P@4ysGtN9w<6u!HC?a158m2z*fQRXw)0zH7=vRdJEXc#?> zew^_VnoSu4zNOYsdr~tgPbtHwBPctF9@t;BGkV$E-FwLcyZg20xuw1OiCwks^WGuu zAf&*?MK(GjoL7+L$Ux^4_g-(KE6~x?+2As|ave{RB4?|c=~24(yQaEqF3>Z^9p(ym zeRK`=CS!xqWbY9412zp4pi|Iym>O?E4|o&Mhxi{-3EaaYQ7y(LcaYo2Cj2mZ4V{9` zz$HLWay1r;eZy;rMB*(5VV?*Q_z&3!gV8T&G?75o;Awa|whdoTW|02G#P)hK0Mr0? zfSJHd@+9yt_>;T^Orvd~Z=$WFWzl;r9<&?4L~tp!7#v5M@z?la;y-dZFd6ttMB-;i4(K7` z@HkvWl;B|)fjaSU@*tT+-XP%iI35p70u7`V$FOmD1DOMa5TCFVqLF}!&Ui<{NJfzF ziFaf$ple^x;8t=Fr5yc1Oa*zMqEH%B;OGf;x1_fk5G<*%Ypl32swr947>;4lViw%;Lvu@ zXrYJDv*{h$`J)mZ&p69C%}i&pSvHssn;9lJmzl|Y4liXS!xLFJT+5ikTE>3NT-VN) z{h1t=o_P))39o@?GwSGxbPX+^@(BD$t)p|=-P0$^R5}jzhmO%6P-WCBl;@Ox!8HIt zsRIX)eaIES6(9iI3Q&P}zzOgb(7C;`UI$Zwdh(z4xtNG+C%co_8+0uyK@%{fownEE z5b>7yLA=HN@SRvs^ol3h^P^n@6keIv>rQrCT&eCbkI8-3HK#qV3O#e&$)29xE$%_i zXlInO4S|s^NU{A9lH>@r6E*@l?MQJ1AbSxVl8fwdG&**Ebk4QzR*ewv;a}mm6W^S35txGM$Mrdpp2%n=zVBZ>UP?9=mn#N zK9P0+>H}*TSk9X`Z72A_c5wmX|3j*xu_o(n%? z-DmG$-G#fs+gR(^2Uycs0P7GlmHCQ!hLy~I!@9%DXI)?hF}gz;kcNJe-asEhKTD(2 zG3Y+M7iBnQ6wQ~ONZUY-r|KwrkO`Iod?25E1c1OQLWKw6=iBo)7z@Itpa(H2AtW$7 z1S|0L#G(m4>id8Ac&qp*mhX$Vs#?e6LLh+<+~wfz?(Q7i3GNQT-QD5f?(XgyAOzx` z$yiHu)ve$EKHZm_`6OvNJu}t2_Fn6|9vF3@7KV@NfoiU=ta?;Csg+b?eJ-y>N%ghy zo%Y2jThue^R$oc)Fwei<3cils37#O&LGMprZC|{%oo}136T7;Z_pqm^H^ev6JCkL~ z;|j0r@+Npwe39(Af7SJBgw|J|qMy_Lu=lE^SJ2}1YOI5w8Qt(by_OM0H_!rOFsTVv z!Ifw@JO|>?IqoycL1|n~elT|*?t>ZJ2H`kU*doFo;gxh=-XR~CpU7uS<;{tvR`NP= zs&HK#Vd`Z9Qad@pGR;y__6jwmee!E*hWwOOa-CdXzAAT>Vg-j7%h;r@X_Kr<*QL#3 z711l5ks1nfd8c5JR*Sb-QY2ikkj6)I4d6BK3RZ)i$a>t4l!6<9oeW}1F_^w(9{3fv zVMJJioYjUaAC&j%6K%gyQY3sGhTC#Rd&((Y3 zs<^JMY8~)loT7KvYwK5WH=0LzW4B&`Q~=NDI+jU_;CYPQ+8GJhYYZVf$WU6B9-&S) zL4mXhSxE`F0)jyfO#r1}D(yy>(gfNIv;t$9GA=W2lVj9D^Km1*)aXvm8^7>$qc@!n z>{P`s^bW>c3dubE0FI*VX(KY6*azYmb0yI3z{j-HP1D%VHJ~?o z413c!qLI14hL)j=5WsBc$1h}4)EvD;$50`(8N~_x$PhBv zn2c}hLyX!qpUlK&ZKFPx)F;i262=DnUSEQ(RA9<`2Dc%}lo&^K5%)Hd^epwMKEO!A z(0z7fASq715UWvo6MnX$RD;+AX~yj?`P~McIC;jPE~@iXaSLftR5bE`zUFJ@2A}TrKVcn$Hgr zUx~FvS$rxkl8#G77zsHvRpW;OEytrISkY0(a#ii1I`G+aN)Y8;h zekS)fPc>!B1I!ubFmo%@U#3mW1DN(NlD7+-K&49NF>;8|oWIY%=BM*H=oE}+31cSw z4c-F)4$zh0C74KG7+XjvoxmJ$H8GPU{2!}JF(Vvr(;MlET3fBBb=ID%FV&rztjB22 zwN3hJeYoCKJFT&5YlGAeT17oVE2$~k1#ONxPz}(l>6g_7YIW_KR!4iLcGSwL4SaFl zO}_t>(aJ96fI@tNGDNMTZ_(RnMKz)Y>-F`2^&G8-9$^F#H-5@yG2ZYI1ai<-*{~tFoqG8CA}1~ovf#KKpVISxM>uq0fxg*=s(WFZ$xzL{5cUK%HP#IoXLVYD!Z@%IX05bNNo;!v@w_)PpJ zdW2CzwqTPI#TUYV!e()(+)+L+KRW#e2p z0$idI%+WWJv8=8wh|54^gpq^y;qm%Pck|(K3YcZlA@q1SVsp_2rR6RGiVlj z_r|an)2Y+oIq<-vC>|B#3Ukq1E;^0!&>ikAUqrC;JyAVa2vy`=ygy%@OW@-9AN(5r zGT)N#!aYHa&}-&p!?+W0CkTX#(F^wc9dH#`10Jxv^&3!H6Fdd~fJd|vC<_sc1R>x% zC=CO^7)ohnFdVD})fj1hVf7gVt}+@K4Vu7;@Cp#Y4eFwO=v-2kTsK_CAX1O)A#a$+ z|G?dGMI)HZCp~9rdGHd374&fhl^L zK2V>m71gWbMfy5zomNgiufJeSdJ}WT8|EgNj4BMg$e2Q=5@ZaX&lk38-;YQ2_IW@BC-s7Qc>*;x=)QxE-9puI<2w^Oc1Nv4l{9|H;=B zd>rJqphM^h_kf#;N~0NQE1R3OXf#Y`D{9Gz^ARWp7qfcbqHCEujDw}&TzZ)Fqi2B$ z27~@|EKMZTm`c8rAEXF1(=<|)d}rN34G%8Q*gOi4#+7k(9AmUJVhouyCc}uxX5g_g z&Jb8ehBTLaW4~+BY2YIm10K>nB%d5)-Zurb1ea+tquWlSc&L(=CbRj3nQ(6?Xrl-hewt`Ir zfEMr$@R2z3mu7-rYy$rG^_N{A#a0TyRuB)~fI4s(>!wur$FL5t z&^S7q^&0^K+`#V7i`)pqCh$D`0|&znupe57@?kyXLVeLY*a}6W$xI`+u(~%#-&p+( z*aTJL9&%l{1Z3jAbFKO7oQ@WvhA0kQM;p;BH7}Rf# z$7X9jvCu-S>$ed?>agjKCnf2>j9vGzsoYLdC<0sPSGKk%^f_HX?=np;3{+}Rg>~6M zHUksEC$^eAa35X;16h{6$+Wox=m_ewhX;Ix^WiZN%V@Gb*bHjJ#;lHYU_YqAk;n~` z7>SO9m)Uf*XLnHrmO>i*1_d-5y+tLtquffSbOl^@t|c0bT5})RnZ0N#8qT%k+M!Uk zwUuzCWJ#B?bUTxN5An66{q^BJ6F_p<~vf>H2)Y7q*0g17V? z(<}$eJH1&=UeF6PgZwgXFvWM%g>)71V-(^?J!Cf7XVf!d08c=$zghi{8C{L%_!(Y` zPwVaVTY5X(nNhN$SHi3HOIk(ksP%Ikgg7J3c+lWu1WR?|3uzhE#h1mYvaCAlguHA`qK*Jt?_}JqaB!L#?e7wBuHU-=ojOVGOTyHgDya3l<@+V zhp`|53`LE(N$3&Ggk8}I^aqvTO0%BdgHq98t_^n>#j|Udp(l*!N^q^2>eObwUXH8E z`1u@X;vaKP?g2lAaoY!eH+zg^>Nb-9!plNsVVQ81r4g%eo$tz1E{0#iAK~lr{kT9z zY+JZrjKkle7|y|6M})CY1{#ChUU=e-lJdlBBpM%#=f+NBfiZ|F{d4>dk2RVaXPE=V;;pznR`gi?nzl(hqA$l@{eU)7 zd!g6DcQu=~mnmMB_EQ_IyY(Y_2R&8a&Ro6>bNhyjfa0))`FAn0-ssJAa*ENHtTLW5 zX9+OAGIyWPCaICp+o)|E!rkysyxjO=d}6cnp44WUsRZ#dw=lE5DWKOuJUql0CXsnY zDO7{0w2V?vOYR|B$?{@Xp68Eqf4Lfb1pkEjyp_Meb>;3eM~~#%a&`Eld^M(9-&v)< za`pLxi~?G54fvPL7v^#n?kZ!*`dmpa1%OT;iX7#)a{ zoFTQyC8Gv=h=vmf8wyKi_plSc#IqSImNfd}Il7A_yi6Qs1Q_dB^$#$&n2SYLrCF>t z-8Iy97YCJM=7laR}1Wj1q87u)60^gF!>L^v7PnKlH%!Yo$|VyxU7u7nfd zBv^)#F9%P&0q!Qu47o5ssnRbj(WnbKSjqe9~sB)XB3s`X2w??83jgx z(rjLPu~&Y+(yC0~ER1^+80A86hACz^aIiXTVq6hIzmnG^iLH7$ zxku_TYN<)eG2QOSc;+u7k|=tI#FGYe3vI~eK9L=5V0z~y(R4Viz$l{?okdU6gUr`1 zGhe$!cYzErfN9VerjnJ|9i}tAoC{LH5=QzDp@a^iEl6i>9)j#_IwqqgjOhYVQ^sh| zU@Ba|IDZAxw=M7j+Q<#yo}t~S7J3bz!uu@Qet;vOja6zEjAnc4V7q%Cx|m<}|KAyw z1l;fj{0ZN}-Eb;A3ri!v|FJsU4?Dt|tcpB*0(LS+*aZ$SZEOI1v>sE6H?$J{K{#56 zK4U2=n{=RsPmv^)w|={csK6HTWoG)9lM9BA_GoBSJ6Jy1!c0C>B)X~Mw?k?`G5wZdW?m6##*6VGE=-a zTrr+-3z@@A;Lb3=S;MJ}!9)0m+$(nO4l*-nG-tc zAJ*Rzh-Z!&1VTY2mXj&#bc5Q!OL~}PMnuPxQLM`jk|Hc&?_|W9Z;T<~R40LCFY{%U zDV~KaFm4*zj0>ia6-)#D$pliG{$aJ5OU9E6tnaGRR7TZljH(FpXu$dtlSb4aZ~xEB z0hMSV?Zx`E3*!bCtLQoQ-i_%CGLF%#jm^MEGMPPRB3a40`wXjpEV<68cRXoL+L8#C znOc&bWDPqGARK{=v&#_&``cSa%XR5MI-POPF}jgW+yxTD$itw4j5hl)_vuS3ux@X{ z+*W7P_LZ7gZ#7_Ud!Hs#!oDA}`It)w&}_OF+y#3WwRVPMVHoSjU9b}4vd-uNbLcEs z6%9kxQ8p}#wj%{iWEEe`ynX>Ayp7x{#_`^g5zaf!su>b{2jLtYwFjgLlC;xZ;Nk9QckxQtQB zC}WH;O0ntagePGIH({BuveAMO^myhT^%xEBX8-z)b8#$t?kl4S|;D4+Z%{0C#m;kbvrdEUBSbz3n#~QqdUZ7IwHdBdxs4n-8_4*aY#`!FNmt`r# z%__czdDRJIFa{ruLQo$zCp}mi$wSW=C7eOO+2qVaJJDTqjOo`jR2gN!WJus#rpkq3 z0HeTVpeUOF07`-|R>3!nj?OYZ0-y>oF<)Fy=g?i$$r9KHT7rhNRF=vZ^CRnuW^@|e zPnR%nsKk`#A3Bxgv?lZ(`AX)|xAX?}qib0=N7GA8pI?yHbSO<{bCk-wNy+k)>=GqiB6L8E?pW_BwSLVSJjUjfHBy51pk9^%E)toRrL`D&C$c*! z&K$B5-A;}WE2~HY`j}YTlnfV%pDTf(Y#1!p@`5HsZ}mNQwKJGqeh+5Q{0p8 z{Z>0vYgc5f^js**|68ytaijmOCdS{7i`safj&fINp{3hi7AvoedE=LSP%RPuuvW>O zeg}$~D#TvYyo(>FoNn1D>|6S&eNT=oS(CQl6ZxmwL2t&5*B-ty6tR@Ta}m9LuL#<0yggJ^|xe+I#mwUKbAUn-MwT>omC;S zYav%G=s&5IJ~L(c+a2y*;pNG}zbUqUwZ~gF{W_F=Q_pj8+52;YokjioR>=w6>oq5} zep^E=U)<~e2DI~B$(V~qMAWks)p!1W`>;5fS1UOBp(Pho@eZ^;i|(3z>hh~!7ugcy7)J0ZIY?~R^T zXiLCoXVrI8;}<30fu6`JwxRkK_*+^MHl)ny%sVGzvR0LyRA`F1UqPk#-AY90V|k(m zGlqTrAx*D!+dnv~AnS4VBCV$E!-t&1xyayb%UdKwEUh-Bpz-sPw5Pb-uXEqtqNyI0KlyWwVxQ?@bNV4e31tTGe|m_Ih8!%SY(y z{cqX%lIcS6Qw^TNcaLWLMDFyU+<&sk>!&8QXW8F=3w+(Zr9BIBZlrvo$>9&s)~rcE z+v|5hrl(_nU3NVn=S>#eGk0xaOWR@i=IKkhcPq6`i`yHg9O?o?dVJrW_g(Pwk1f2` zIGHd#Bg!46$zW_?UdcPAW7)B|gLQm%&sTqx2_%@g__caYGj6av$yO<-P{bksuJV|) zeosv9!^KL8TQffebgg^A>>uATVeH=;a#+O_eaZ7R-mS%>iheDe29)H*E&+8D&d30@ z(#N@{<&3hetU%4v)3+wCOHAXtm+5ZppQq-n%&$(9ZE41joC?MQ>t}N-(|+Cz4yK2` zAF14lQn;A(xylRqiTRiHvHg{wHM`Q&?)sWahw;+qA>Px`Q8AB8%+V^m1)d*;I1PN? zt=22FBP2L*tnb2)K0Yb1ywp)|tGCu2{70+POA|jj-Zg0F+j7=IPP7{tTrr_j#t3&Z z8EDeHXX7U~X`llC+itjqlRDG}yZObJX@ z-it{=brZ(fS> z0*>GWVTlxMz7B_~rIo+l)mj-cklP{)rWqzLzXbHObP4IB#Yi2I?vni_Ghry)I)a z938&Lxb)|)@0M+u|DfO@mboy8pDDj|`~OMLIi%gu&*4+@?V#A0vDKI4O+C>x$6k4b zIWsv`P)o)H4x@i?KW!+PjI82mljLcT7{s|ttaCJ1kMq%frnFPHdlW*C?(aggX$Y>FQzPMrYozI} z>?e<~B?gqRZWSJ8|M>VSb%XlFkkI0Vx_WxDtaMr-{FsuR(SBn$5{VMjynw4clJ=A&g`CXubj z!kaiGp`ox*@ck)#XRBAPR4A-Ar1EdS&)i?{daWiIl-1?iztXZAJHqebGsZD8fUjci zruEJ=r7cg2i74n&*);TS3$67(Q|aR8f^%`0Y+XSgv$)aHwx>{GXYA7)V_x-E4*$$@ zo@OAN$5M)M&vmk3fiussu%a%^`8C!z!N0Y~{QX((B>pokuAdXT23!oN8#vv4>2sKO ze#qC%lsm7$=O$Z=Ee18SzJ88(rv-N6kGtDsKTWzHbZ{rQJNTQ~ zA0~y?gK?R&@nn07Wr*nn&GiJJM)r1G2c@;%nEbNr3is1uKxihYxL_yK`Yshe@6|mp510vg{I2#%p7+!THJ7H4TKGjww6MY069Iwd%K`~?$nG2 zmf{tEIgUAVQTL$Bn*7I^caLNPJLhMw7@i{RDY)-T!0&|D_U`;Qb)g<^l(Y3MGQv7v zKahVsu9*H++cfi=cOz;^``G%Q(vjQfcVGgm$hhU4R^KoGqR2fkA<&7Inxen7e}pt zYuQmx%jcF4K1%rX7_L`jV{0p|jA~aJX0(i-22PZmYwcdp6^#vefI?i|@@MD7nrD>S z&)@wMh4x3E5N@R7%;GtAJy||0OhBdm_{cksLvW3EM%KCHi@%WGFf7AwziF&vKyU+E zJfnm+jC<)DlKs-sx|~bcn%EH*i5>vgCN|Xa0&knwz%D{X(_HZf2XcjP3stSqePK#| zQT}`AZvSPL8$@-A8GnA~cz#<37UIj?v98X)tza-jyYBM1!ri^Br(+D%>d#E_Fd~7b zB@NDds;#m1kL<;F%iLxTEVn{UdHV3@bZ3q{qUay7f6gX8E8;!9mDDEZiTA8)UVcBm zO;~MHEiKcx*CR94JfwUo;H^DNG~+7&De4S_wT(_{S5GZxO>^tw{{%G?RB~Tw?7o&a z$sMe&P`>((N^?U4iljw10tbHWNE(-M)0Zu%CfWWq@O5|^7nXa>8$%sAPvTR8s&MKK2D{gL zXZczxkFA4>EwbO!Tjt+OZst61 z2nf~9fF8@OnQ$jNMvq79%=_)H1D2DPnT_=})=2GhT2;>q)IO*|$wpe0H<8(b`5$2r zD1zVlhKknU_u>g9Tv_J(n-`gWlGY79Vvi7K!8BYO?UC-N@3Rbd2nplDnT)h zCU-BTk1GX~2v}VlNb@pJ=Dl~v;}kwu%gDF-qKqAKnV|CK=HQoSbk-21Kl6@g;jpi8 zW`F)_1u2s-uCPT8d47&Fb?^%gj6?&5Fr0k9Usp^jG(jB+ehy z!5QirUwP?Ah?T3C_e}39Uj@NxH({fHNq8xLn7vu~F!2e+#B z5LS7&{?Frrv!>leLu^~rLBc=&y@jP}Dfw@RMT_{QyWd+&*;hFZ(X)9oXg5a(Q!lQT z)0NOn-yWJ0(8ccYuKR6sEfizuWnW3S*BoR!bYj5_cVKgHa_Z@Km~ z*}{!4_SaYY%}D3^AhUT19SsJ7$v9YS3@Q3U7Q?D^6j%H>nsbY;R$I94 z;Y#8h>z@FZXm;;B9TwwqU0;G zxxyo9{NEbx9N}u<#$uN(DV{3)j)2qZ$;6KNUuh+CTu>4FW2uKlv3rf2>~R8$>_OHg z^zw$;r~0J@^ajs!`e3Kof@0L3+8n(Jun5_5n$@C3r{&}y(KZOP{kjUZvMFiLCfY$i<&pCDD&=5T@35>^T*HW^;zws6Uj|-a1Q2(@owN{XNy){+ z%H%9EGJK-5_w(~kSm-EME~f|=6FxGkpu}!hmHRD{TS$laY#2_PV7KE<@o?BbVQu~y zv{zhh9xOIPjigrASkL@~&SKq)wN0gxUn{XT>US*Y0ZPnoqU@m&I8HgnKk&0#hf3+T zjX_T!$+hLUGw10pOVyCoMQ>USWho8N&o-z07sM+8S~oqx7!HQ#HB4wAcCC2BQOe2V0&%t6$XwMeC+G$5rD^t3!8pL} zHxPYtb@biWy5n!gE`E?D-Lk^ko-3ps@`O2S8m&y@P1{UcEyrvhIYBcKGwMNi;aSq4 z00k`08!GgVK=K9mZReTXWqL>1YdPsCXS$0#Mm2S$Z-uA0a!gxgSyXs}Ji*-yx8xT_ZKBa}CC-F3+#>6YCuqzONx zpubX+B*Mi)B{|vbCpLv+!FsxgPN4_oME}}kdFD3#q_wJW+4!yaT)|?quwAmE?DsYB z7Ulf?BE*027ti3l7p_pHu)Tha#g^{LRUqDuD;mIgHu=4t6HviASJ=;g=Z5*xGbg(g z{VrJyr+B+$)#vI(R76EHW~qs$f4~xN7hjkXBQ^^P0?Ao5)%D_TyuF~3{*ZH7Y6q1- z5&5I=U0L@PN-K(^{A2w;`!5#itHZdRw!8X=j1J&^$Xubg8yP=^M9FRXEqsH}8<2^# z1k(%iBg@f%i{X3J8Q;4*OPM-J6NOCpFL`Y1RAejnD`Rr5yWknvZLi2*Rii+h*vC}U z`V%hoHRc2TOX$T?3OqN&hPF?Eui?wQQ+SX7uzp8$iMm4#eoGOkqzbgp*QN!pT1^kbpE>Hk9%qxOEk-&_}S!Z&p$&aE( zT7$hG=yp>ot>Vo!P6=h;VO)dUr@O!`n9Glqo2ib>ov>PX4&0O3RP&jB%bz7Bmweub zTNMAmwNL)Y%_#oc)HT1PGu`=0JIsBOMC(DZHM}6!vR2nhrDx}E_ne2%Z1E;T7|-Q_ ziQcTNC)y5EA$3Xme$%J2tHtJjml?mqCTaM`(1N@25?^#`n5k;{B|#oMAotquqRJA# zSlF?kFmn16F$lY90%y}J7Thx0m`a<1O`Ac8!fP&lJV@i4sH<{%NmYxDk&fha;zJ|q z8l8XdDtIqc6PELwZDVkK8xkrSLp)Gfr=9hj%lAobqpY@gZ7-ZC-yqmMRK0I#QvHA| zwAIy~j|`s5J$D+;;ssZQuHl_X!=w(}^x`MEZ%OOj&4rez3C$v#$r^c2z-U}Ob)t6K z-bc+&A|NKxVYB0${5vUUJzE6~2az%Yvetkjyft(S$mYICc3-vB>!?E%=Jus~$e`e5 z(hb$;iq4Hu3L!&CHFe`3lPp>o9zf-Jf8}S^TGTFlhW!#})&}GcF$S3r;Fq}#9X~7C zEzMJB<{#5Ka30H0{a)rMp-|BaQoFoLQuQc*lJK*MbGLxxQRdOc!2IHp963h(n7b$^ zB-=_fM``O|*(Gh4Dv^ioWBMAj1MDV`wZGn?wt2-P(d|T0>E<|xjSM@Eve$zh+%cRS z@Iu_AFV$!0JLzR^7Hz1Px73NarBwK`u;7h731xW)((h7T^JVL5V|ea2H*;=1UO#4x zrSmPvLLb4DtbA;;kH!x&e|R#$bGn}x!bsbDp*azaSNd1&gL*_8qw|j2(KDrgvX3ZX z!d-p@pJa6PEfVeo?iS~%|7maZP|$}zN0EL=>=w`pR?Jt(OB=Mcw|?|A`TbXq3(FST zy3XMuVs){nSW-*NsiiFuV@+eN9=gD_OS=FYgpaI>|#DOtEI>=b^fLAjq@%hgn_ zmHjBd1*1v4DTVjZf#5wz)N{Qf^slt8u~*w|Iv3ViOZf3w`yT#QeBzS5&6Q$=a1}^@ zWfOfSuEnhie8TPUaMJ?iUO{eldwkY%R=y-8YA}C|P&s0)kefHHAlF?3Epc3eUtLYf zdHK89-*yz%^A<4(7Y8I`EePa!nX`6AN0dA22kn}^jydOcuD7|8e`jN8MyUEij&W=X z_ETs7Or=AMyTy5#gOyg|6mCDbrM-1;;f4oHM{xx=sBY~^PdPnmZPZ^_B4=>}X(B(( ze8P4AXAR#G|3J%fz7F&lK4g~j^f!4O)#s=`&CuVWyQY3Tpug~K?LWMgj5X?GtesWI zbG2-Xxc6#?CSe!;Vzl-g$Q~}YiFsx2;nZ_CWP9MC&;-ya^BHL$xPpJ?d8xiO{Ag>? z3yc6KWzPSy(Lb}d+CvTyKXc)F#{v#qw*|_x(H{PbcuI-QoSQ$riY4*!bJ&X=hc$Q?Lv1X?|L9brWZ`k0e)fTN&||9`biS zpEy(^2!Y6#6>oQyvef1p>#ezgmR{yjrjx$Jlpdakd_0$m3ZMudS`UV$cmqD4cXtYU zDAzDH=&6iJQ~hdlTRfw19qy*DRQ5tsUdi)lbV5HmqUav(UFuY)1y6<_#F?lVi8eI| zve3^N!`x|P8*I$a;jW{){0l(^W$^;_mG>$CETA@j+H;wI8(4$vNWY`~wcf|8virko zA$3eG@j=dJiKf+E^OQTrbNE{L4YQ0Yrj0@8g+yqN@S%}?KJm`$jz!#$B;qTxX zzu2hheMiPg_qbqiinK8{!z6*aY0_5Iu-I_ZCg%{PF6{}X!ABk=qaEI9|0o5*JopXG z2bIZe%+JVD^Gtt@=gxl0A2No=ruL>2tQRWf%>nQIt*}9UNg7~x zAmptMr&!}GXUsc1;}a!iyW_P<;@6;}@Fkpwc6oNCzcIMLJC5bnCB}l>AFxI6PW|g2 z9}W-uYCFQbt}L0TUGmlh4@^tcsu@SMVCxX+2EwQf_gfv8f6(}4u0@x-e0aDx*}T=> zlIS^my>)1mH!J%kycDw15(|ov@A@Q7QnzbixCE%eZ?gsm*8ttKJ}KSA(j2CTaWi!& zSSFR?UXf1ZyRpisYm`$ZPZ_>fU`Z1u4qQ^r;&1!4@}B=OAM`F!*3Yc>CYPWKS63SO z>UxiAlw=D(tUgn5DMxtBPuHUhmg#@_@w7k<;TzfS@y+zb;H?lwx_J{leLca}jOgV; zsdS(E+Tw^n&2*d`V}k28L5$Gr=m0tLy{l}VP9LJBR8#_}VC3F0u^ zyhkmh6`tm`rdn_g>-@Sc8Cs@ z=0+SkBPCn!&>aOe)Yr5DJx0C(2RBza3DH7CC{-aUS1yf&Z2fc1)xKbeKB;RL-CC$2DPN)Lq z>_rOZE8v!{3}c~O#P-I&m*m7teU5@0{8XBN{0u@f(KG3i^(K0veAIuDGkAg)!acHV zGfy#zWNX1vZbi@s4gP8Fo+fqhs~dywT_2Vn#Tyb4xjn|II%Zc9O}y54ak- zqF3}pQk$t38l#_qK(3lUlT2c)e){*f`(hMK?rSO3e{WeVcL>*&z$F7p=hq(6FCB&fGv;}fAU;Z*63X|o9e+I!Cr2< z*u-4MKH9VW=Ok}?KMtLBRUwz<_2L}N3bgpc|z6l)UUcBO{)N?(c^o9ytDB5=)ZfB7Xi#Jx2unbr;(4OVCS zfkS~8- z#E&oyAwEQl&QIg+gqm@)#CTt<C=C9Ta70uTRK&tra(i^A<8rP7Ej{^zseFL*a3nYIJ~C_*|i^bc3|= zrtw6$?$79#UvKr^pK|C-^cx{7{k!Ki;)Hrq8+W5WNc>fH0?hmR zR6AQ_y?Lguxc73>ssZl4@U;CRyy8BE_wxc6qmM&# zP06@ber+WNOx7=Z70}pnfv-Sj;D_1@wT03MHMCX*#odKKr1cA3<(jRIqDiDD8Hk5y zQ^g$t<;ks#2yKqpLAE=W;<3_vVKc`|OUy~Iof@wF)na^|TpB#)cT&1SUeFXU721%h z$9q2L_u)PHrQ-pATU}r@gPZtF=@ML^&!@8($89ni3&c8zT+iFAkCm*_d2uhd7KEXb zToHad54C=IO=ua%D}I8un!jmZ4f+;b^_h*+v>j>zn*$wQ<__XGj|f*<4K!ID0ftIX zNf~b?ToV;0P^&@4p|*OoI|n?p#i5SAR(eyW&2{KYqZh7D15g-q%sQY4T7&O;+tVcJ zAR1*{(iGn_?(-KY2~=4XE8>FS(@ zf`5^4P$cOeXA^pZlp-%_EbJz=wk43M`McC7sDv@qm!}NWsQFgJ1$ZQFk9!B)3--~s z?(B>b+{D6{kdi(^PqG!FE%Ph*Ok|(f&{35yuP!yxVMTqsJIZ^{R}95jKL|0j4USd! z!+2A)F*v^tcE~@3liUsdtMC>5pc8e)`vu;#2Fp{p_C}0vD<2kcfE3Q!p@&#g#Vc?e zEDINj8J1#N%k;LiXUH${Uta)u!oNnJz(imNfhdyCA-C1HB#Bdqq=m!Ea#d6jZy;W< zNp9-5#`u(Z%r}!iM4l+qjgqJYUtVkg`{H__1b@ffG{r|QhJE4P`Kr!K%lQx#W`rxl z^iw3A6sOfuCt(a-t|oZPx+{?yvO(|rR+GNM4>TOO@j^98t)opa_JG6E75h@IudHKrqUwrF}(;*z-@3DoU07X3*AL8@qUsMR$ zDZ&e}4;z%@?y&rE5ZqVBYW{c>d?M~)-1Et(s{f+v`B`e8+;4g>(?qybeQOj!PvH#c zBh&E~{i4>8yo8~~K*fQcTgt+9-Z6S>FbKyg@AXQwFSky5L;Sq|kbYu3ZsYm^rr1N} zhj5=UQ!R~B%o}MP_eCYz*uxjMPJ@rVbBxN&Kj#b2Q3yDpSMz-Z^UXWtTjE|a%ew_$ zu|`NMU^Z^4e4<083H*GRh1)3~NLQhV4oPD=j;wLBBvoJ8F-g=&c2|ANUhKuBUNjoTm=LA;j1Aru+B7m4w&MTe{e>W@ zw5b}Y?c((3XozSr|29rJS7~Fpdwhg=R>cL@$-o#7xKjMYXeY$P_~8>4kF1&k80C0ePL?5Ah>?~ytf2N0Du zqB6!OV*#lFvfvEt^Cobg`~t+j`XM#lcO5TPRMCeuxZ zuncX`))tI|o&A28V!7&wgA`4QeX2ZeQfqjvfA$;e^F^={DkGi zCFb@pROwE)i602(`wY&RkBA4+e7(2Z3wGK3jj7ppb;;teSMYBMhUHyVt^5K#tsq}> zqK^DIVX#?&O!F!{wX+I+R0w_tpqzCd2V|)Q`?38{Oe(s`N-GB>x zN#9ib7|xdJ+otF{G8&Q00H;jIM3QJ61jD(Z+;FZPtV)~0?p%AJqtp^M(MRL8hEs2) zjo?;UyYhu_VN_NYgk%_}Mml4;&%tJ{W`2Tqwvlcc9&(?CWZWi`f2zF@y8fC!`vcgyjmkUX-A(=(4k)Awn?uxhPzR*yVDwvtI&2)PdjQ5CP$f|ZJ5&ma>y zpEf`Xunj{Od`ob5*ivd?+laqs<P3ZOxDeP4CvV_{+!ZWE{gUqg=`;w?)WiFIHV z<^#{U?~H=4fZ4hSPLp557`+vZ<(|MOps6oikzlg51s9+lMNKUwrBLoQU5PWCk8Ynd2>fsum zBRGH$;=<5-y3**))ifQY!QMr_Bqi1OgThcd*i9U3Ig1pvk-i-N&`as#=tV9W76xPJ zT{0Dy)V5=R{sb3LBCyk;@F7hT=4JVRU51@1n1ks!q{n(ymF z;DYQY&z2NCwxE=eEG-dAagAwz{SqH)|9}@}tu}T!R`Ju+3HoRwQcCsn*Geay@l~aO*2fwB}wH}@VVNb{{mbU5{dtoob zqW-7V#dFXdnddvu73iZdpH@;2dc=agxSF&@z?>82(Mlv1f7Gk!E5TnuA|<@P&@smu z?ym2$cfV(j)}E$uE9B+;dN2_Gqxg)w+{FKfqq7c@B6-?yW>t63>@JJDbJ!t=yZhnp z9KN`_ySuyl0*AXD9PaLRxckb?bXR45_4|Uqh#+RWt1{pBd4h=9IQzZdvl4wt_%2}% zIUrDz^ZSMd|Ni(mDE+nkOZ+GBjl0llmaqWWzm^4m5{*hN5@J0B$`vY=1uM)@X%5*+{sFwpyVN>}PC&3e0#&_9p z&`5Vd@E)i3^|i0bJ}NRU7T+gq&o{q!P$7R~dk?NwGXrU?Ly053#%~9>OB3C8B59cW zD*mXmCFLfy$o=wbC>2!}<#f^DJ6so$945quyk0Vib;ODy zf3OK|4R7Wo6qUR_vCp_p#1`=4F2S0J8Q3rIjA}YvoLNp&N*&*tHaZh!l*xpKdZBy~ zc7=DvEmIRE+T)!I+KC%4!V}-Y#WCS(xHZfgV{Mc-#2VEzzAh9G&j1j=+AD>PWM4a^ z$Hf1H-VsA^RN$t{Eb`0B);~~Iw@`h&mRw1$fH<8Di&@pUk^7bB*aNYpz8)y}t2%a0 zywtiBd`AiFEV2S7^lr!2Qy1k{Yqnho?z*LUvuunGw^lnsDR{JX5tH&eI!>wNaNk9p zAoe5;4ExQ#56$%B&_paAX6uVTy13H%X}z?!a_`V3t8ik1^FQl`Zo^v{bXw8AxTSJN zlJ-t1uWqPW;H?;zu%PuJ#FWm;Eh<1-^&{|Doy7kDAg|p%dn#0xh-$z&V7IWUI*@)b21_(MMw)$P;1(zGjfzdB^Kw9v`}%~d0+2dn9e`sh7# zhlw=dmz;5uX{_t;Oj*tS`6CuZvTeR5YJ#`Rb!4ZALl_opr;flDTqZhEKDRtHc1H2K z;8^{a_|4=^O029F@CRY$KU7*1ERjW=B6ex(Ds>DU;Dk;ZH`%w%R5npAC#7CZzrz(d z*teK=#pU8H{#^D!d`rnxLw8z`U~t%RA=F|l=o^hUyxFRbeyawmcB-60>vBZqut`oY zZXWzVW9(VHAe7zPs}5SH6U^t*iME)K*dv4x>fDYA6oyXPrg8RLgZr zm?J7lPo6ep)6`25_f{RS%E*6lEsSNdXCx>ro(4LEUb?xUpPdVyx<#!uk>&AOY*gSy zXr~wz@c?qi$BJu-4$CcpgMp)gjvVG|C|9y&4fU55;VQpdKRDGpN-h=V;-b8L*B<2T zg&4QIJ0X-v&x2vIi}T9vWhbJ&@y$aaZ!!V- zX5w0LR7S&cOWzlrAO0ZzQzJFVrfxQmNm3i1NKu!orIX4;c;uQ zC>`mLCdN(n?ZQ9&Gs(mV`~<69l(PE!uQfit2O-}IJ3XFCxdSBGdnETv&; z3{Mu-F%nWjcIZUwR8ZIF{W6RHv7Q|>iN=R*k=J!6_g8#>=_Z;bGY4Yzb?b$6#a7)c zxQ@R#@Az@B1g;4?ATE1;RZ%UK!SISOISy!~GgeG8T+^7k;yA0E?Kp`!Wspn{x5Yau z&P(VG!w3J|BOIUru$L>*~*#H#8;uIDC!&tiM=aL=5EAZ9*kEy&b7yf9>^p zT087`CtPIH-}P~NtsL*BUJjW=w7h0==`1Z|pzf-V92K4p4+hfnR3}0#&`I=6)rV@s zX)GzB7%a9PYs*xjz7s(EGI z{$hLhOz0N>l?R1ol2_F*ot<((PH~!=d;2hj|0VA7rcfvJ^LBMnJy#v!i##Fb<77P~ zRM#+QMXnQIjs(hHt$pMAk|NVlFRL-5q ztvDvGF#HU!K$CygcEjZ~D}hs7_JnYb){&~0n+L!7Qp>88mVW8i@JME+cEO3f-H8NBB` zE-1pBZ((Qj;-8mDB&Y$CVq;ZD`>L;Ecr)tu>n(MU$Sz(6Q>v;Q%WL7R6G%{44hY?G z^Q&-r$*Z|3hl`Wecv(|igJxV&XZ8M8f6#HOaYSc(6u;vpkO@BVWs25|agu!$V!R&m zM|ehE`TO=@q*y4@%4ktq91|5~0QL}2ZCzCr_mau@@Vv6KH;m@ucU3*OLq+0sG2hB0 zcC(-+Y6ZQ5th#k*8^y~0cEG7_ZA6E9^RJcn}GrW#iC4UQQaUkE;zh z#~EO?6$P~pEa%d`JR*bs4fELtpuEaz-onS0N=exu3#X%_qMfh4tjwKhicWxwtv%wZ zx%<`L2+=ld0iF*P(yO6^_}yyA!`;H-S6DWx^D8xcj=U!h1{U*3-+h0j@DFOr_XsuD z{}XD4&Z^R|-bxx~(~nm%)9*-XrjgNM_xH`FfP< z>}}UyxFH;ZuO+Ih z&1}hSRtskW>~dMx#a*0$+Q4p+L###*+Q3Tr)@iM;#8jfA{(pQ|okZL)aGoprJMy^r zO5`|I#AupG)2M}AEI}d28++2NkFB6BpTm6CdM8I%BN`i1M18iZ@RHDY-2>0cY))0^ z;%?zD);skuwvB#m_rh4!6#kLZu@#eA=kC(S;EPVe%>p#$aU5&;lr}j&pQU;&Yw6=-BVd~MZ0I@PdUuZsqgZAzRSNj z5wx=o`_tgaPz0{`=YTDNzjQ*`Ri2Y$uq;Q3*>+Wmi_ayyC+Z|x#ZC0K;T&t2Z!T;K zRfP{uJv``rfUHg<^Z6fDU41}g@ehTo!RHijEym&$^!A0qM5pl7a4D_=9d$ZFq}zo~ zi+R-2t%r5|CFJjP&TSjWORhCWhvN2758o%dzjcYTdnXME>`^{_hgQLBk=ITM=!GEY zbmlvO1*#DqfK>1VHegNurC!k;Sj$~#nYiH;w%5swuvYz|syIWP3*3cwVV8YJ?9+H# zcV>n`^wuk4%zUXl=o}OWX#*~?=E^x@t=<;AWIC#{wFVvCJ?QaVUpCkmObmT}B?PGu z1t3!XZf|!p|L6)w66bZ+d0$m=Y664AYi^`n-YgdL1hYrcd|JG;U(w6JP)cO)#pYBO z{uIaQ5AV9}3Da@ASOGKW6e!^_=+5rhz)^22SF~S;w-EP3y%e&g!P_CC!eenl=!2>a z_eEDb8$S>3)FVW5ECy9Y3u_);r$_XTZWgN~1PiJaQF$h!yWoY_{GbO=H>c%5?}eBMIJBr4f`{dwVIV2n;7WAvrq zefa4ASOF9ea-np%+~wqA&Ka z;1q{&s6Ie3tRPGb;HT;qorUc@k22z3xs*qHN%aP@u!l?n2UNT+#zNe&YQq_?mrluB zMHQzQ=2Z{%ME*ieXggatmX-d8#_?k;Y30CFbd4wAXd39<6Q#qu$)&0iec?66p^kAz zwE{Exdf+kdEZ>rAxVsnQ4N^6%><2UHpJ6i~SZ*t$LeCxbXj^z&%FD=#`+p2v!ndUOp;Vmhl53Er%b~A-7Ij+IAm(w z4tB~OkW2Sh`@JrlO8z5zTRSn1qP>>EYE-~Vq+iATE{7-nDBA{ps_Ej6N)kxoMet^; zX81kP(PO<1PU&ZEBk#QThJ|s~07O8$zhU}3WUzX{DRU0dUN0^rH;W7+k?@NIkczT! zF1Q)$9FtE%vh2Qw-gLTv9~A~IEk$X2sDBT92sVRjzU%TXKU4pO{7}p}BFgc4a;U4k zWH4{5i zE4NnsJDT8Y%>@D{v1Nkic5h?i1>;JpGIAPU^A5^w5wGp+=FTQ~TlF=bK~?n}@!Z#6 zMCkmoeb`dE8WZXM35777xM|Jv-L}$;=iV~I`<>eC|Qj{`b|&Zf98nel^Oh%p>uGQH;)u|#QFTMc4Sk3OKu&XSpAD% zbYk}kPqQ01Ied>{jw{?=q1v3s9*LK= z5rgfFsGM<*2$7#84yAXbzV^v%F-$#+5~mQ z41GFyo~~I(t=(1`z7U$AD_|A=Q|E&eGCgPW+QUX$>-zC0IA7Rvm>HZ!bFIl@6h!DH zp^frHWFvU{>#W+~Oq5-r8~3J(yc&w=bD`>VS&WrsEI-`wj`Dh$M;w4Z#5TE!S9-I6z`ACF<67gg7 zMJt;%QSR_=#B9JZi2|ZWd>h@^8e*-r&d{6SJ$+uFD35Jm4FB(|S|@NBZpMT7g|4CU zaY|7I2SQN}>R~`)0py|O5H8=+DYq%c`7ZLZKxy}$ItAOTy>MJbi`D)GJSqNs2z4e| zDeMUjbie8@*bHl6yz$-u|BsN1m>wPJOcZmd3zemMhKk!jQ~Cp>=qzrER}fG0b#9fH z^NE*YnOun*xDvmiRJw{c#2Ei`OlF#4I?R=;oeTO?Y(gq#_V^&a^sM;%{C8M>alrkg zGV*Wc&rrC5eXKOTi!>m5^pOJEXSjrBGwPA!5h_adH~0G75!!GN{P3=R^pyIte2CI z`sgKm+^Abs@fQw-iF$@Rj~7`P#AY6BH3%yMIZWPs6H($BuJxM7hNN@4d#sv?&SCl3s$Ly2hIgoAbO};gy?iV9LSU61Ab0b7?+M+&JL0K4j_Y_G zt=C_v7?jo}-GZ3kzfvU7L*bJwqzA`G=?2yTNU2joTKPj<6)!Guv&%Do%xq#S4*Nmgm~u1|7%m z@u@s2SIgf`|Nf^E@GZlIeMN2f-~Y}{<%aHh{R8G$Mf`gxciaSi6V^$*b>FK`bQyPB zMQL{+7jJS#IqjStFvJ~51H@EVMO7gVKjR176YBFbnLg-0fv4Qo0L~g&4U_2?!7lnc zrs4AHF$}SD$(L}_y!{RBDa|Y{!d#j{BdG_p6o&QH2F@xi@g6GT8cfRzRXnt|t8&iJ zBbs9Mk*&pCdBa+u8^r$^EbNXD7s4CMSKb+jwpZao-IjCXMeHg%L3>(A7ik*Q5C=Jj z&L(pDSl^BWBt=*088}}7{^p_A*yj0$5w+k1rdOL^x zwfTA6H!Ymf*k29S0}TL|+33AGwq4ac3yuuJ%f>n@NY?3>8k2hL0 z4y$C9;LqNIU|u>acRQQH4#AZ`ExK$?hpK9_xZ;0=@7#KD+#YT2rUd2iX3=-72Y=If z>r421cpLvhzp$dISFnKk!mZ(|q5iDa3XvLH>J_2$9A~e_n))dZ5R-VBN~$O8=`c@z z!rduEG7Tfep&QRca5G6<-~V+i+6eRBe?3D}y{qt=;*2#kwx;K^y+0 zSE%aVAfx-5P2XIr=qKBm^VkS<(by_uqk4;my6CDkE$ywQUT+U1N_CL*3-<1 zZZ^-IN&DOb*e&ca)C|@)7QI0Pt&!S|Tdm94hYXEB6*FZF|D`UfLi&nsNAvg}C?*fv zt%?x44pr>tYjgYq&z_QFL4 ze_y$Y66rzSAMnYV3fa{f<3AB7Wnz&D@4$2j!)chF{&qLw2LDIO6<1W1m3KrBaZ03@ zz3>d*rPunjTI~^b_TPglq3XIeW)uN2fLEpV0+GT^D3XSEgoE)xUB*65 zm4g{|0H=uzax}a&?wnE#!p9H=zqmLJQ!|ZqO_QUXIPMlotFJ-`^GdUip}S^VzEL*H z#HpbeHBxm%1OIXS!^@?z>E~*RIs59A^I%7_;fq&D#<_Q~(?Nxybyaxu#h z4>&b7Qx&j)^Dj2isr6&^0zWxhxodC=?{MC7+K^Ajz$#JOih|L~!nyVUXy)xxi6{z= zioeJUmBg|B1Ng<<&AZS9%o!FV&ZyzKAf*JtPIw!#>3*SXbYDnWOg4f+dYL-rmC^^P z2Hn$xWX7-yayU)pW#Cu61G(sxHOVgEEM^@{OQq!(USaNP5F7!5(l`<;U>ItSp-O5- zXeZqEWtBr{j@;{S06zm++*)cp9JStK3SNxqtvg}?E}(hdT{F#NMbN)kmZILYo`yqH z(a(@*C$Dh)Z2s!cWHsle+?Y%0wB8taZ@!MWn~4RTB~8ODig?v!OD9iG0KN87#L3EObhV7rH3Tr1ZE;Ui9wA^y8!v zj-^!^Rm2;FCwzxsSLl%^=_n4h*BU?RqHU}Ti}@mGoC)ce!!71d-@iVBsbRPr?THJ^5Z5^-zZiMDl9 ztifKYXdsf`JF(Knk@UkmLyIsso`xuv)SP$27ThHIP*Jz5T0$*E7Uv8^dAGTWrNm)e z&vOll{zcDp3%!cp;2&~?wT>+Zz#hY{e~XL4rt+cQ zu+BFdhkIjbB32WADUqou?vt^m8=KP@@jNs%fu12^4)_y#zQt)}2KPU+mlnfjRisApgS z`w#qGf8rgYvT?N@9H&0>EAarYK?GIvYCt`E7GDiTPz^DLcDkP-k1v_yx0}$~Kv!Kr zz87mndz~Tp2UPVhk>4o0ZsW!1X>dk+?gKdFjDWK4222{31^x&mQ19Uq?hwc29qSri zr`4*byGGrh1=NV%!AMzGWW&w;JJsUe(1OcRH=YM)EcPeI+@Wrt-7(rEA7T(Qi&Ex9=ISddj}_gCOq5RN>n%>}ub9E*XLkiwabVR{cWhuM6N z3d2TR%G2~p-hqDE)q06-I2qi4a=c3~QUkm^^cp@|uHOgA0%N^TTuH8VBDim0v;I$R zmBS>^gkUt>@{bmOsQp;g*Ad2qZo27IL8xv$GKrA~K0!k`%D3SgPJqsg`t}FSoeVDT$zj$iX&oy zh}TU*v*3#F6ItE4vy@WNhRtmpA;OlGZh zYC?Rd9bdPSag$I_J*bglr31872*fUL-i@L!!^9+Mfius}tU-K@@TM0@*T`GW-b~f;yNnVqU>GG+hg1vwMUS9zD6J;A z)mY|eSy+6AeiZ9z4D&~0EpI-&v}2WseeNBS>BU%B0MU>S4`Nw9qn=U>oaE^=4n~Ru za-$p!b@X(7Ue~3&G?FsW0CCMpBf9D2bjtj_ICa1|))4GSZ=si*#4EgW^b?E22)b;z zavlC5-oP4;r#htc7X28LIuSAqeCQV+`I6oSvt>>`;gYU^oy9!4f%kZgI3(s_9>X>V z^*XW8mlw_i6_+;L^1@r88*x#Q&Ta=Az0rnTvSJLR$Lg4hU#b|g&DMUB?eQ_a)Nj>N ztnUPAWuSt7BTgIJFAq;Sj~?Xa=GSr@X5mCklt5&$JHs|_2vx&jyjAbyy*OBul@a*V z&}Lba;A#1--l0;;DPev&U5(~JA_v^lP4z8x45OVBcuQ@E8HOHKy4%$u`YeAB>j}F8 zeYvDB2P6&M^X8KjgYB$%SA7E8&dyasijG*P_>5bRp5WisWM2YY=x$VdD4%h|6fja{ z#Phbq%4#{si0iynt)Q9kF9hKb42AyOovx_t-brlZpD(Y|6}}2tbpKFmsO!5#<>C*i zd~(06Y$2TVX6feUtkT%8V4KQAJ2(>ii-f$@^Qfk%1f}Ss*yzgwYl0Kqjw%Kw*_*^U z?!_tDlAiM~PYOQQMde`L>kWr$c5ab`f7hkGOz86|{t?tVLQca77|x$*9W|wYIX4y& zmE|tX2;KPzjnF03I_=}sBCp*8wt8z|xxEW7==187H;nRQFR@47#sCkYka8e}Jj6|v z!-sJzu0}r(G<`Oa4;iQU4LgWRBBx=?8}3QH!`OdfuMBjzTgrN9i+{x{w)JTB%Ug#{ zd>cfJ7CMm{jlY~Af_|vBtA`lwI}2moGX*aPjko-XNy_=uH8jO4U%6pHY6otL&7 z@3>*r=WC&@Jl)$UC_NJ1yAw=$WG z?aT5ZtNk(8D`!U^fOT3GvaL|_Dcv#VHZg`jq!lPZImrLK6<5Mmu3ED~CO z+aAZNwJ@sNNv*> z=^ZObDaP?9)s}DLY^;l`@CWYTZd#b1Qb1h5@urn->rSd3l(LraPd7c(vP)uiU5onh z5R+>!xsuu=a)mkat?H(}c#}A@9Kh$iQfiw@j^pef+{Rs^Z$kl?!xxxYQBWtrKdc1u zCnlr9=I(z$5g5W{O(In{ow%7!^I1BlhjT&91)0sgM~bF$t5piFd7qS^6NXDKVQOf{ z(KLm+(j%^o{b{k+95ea|p1ET4Aro%gH12 z!#CQc2SORS%J>v97$2R?i)pi-0fS^K)8L=IIl&H4#vc-gbvy0To4JbcnL9a#`|Z8_ zcj&GrtS`4%;kt9&Tsq^GQd5Wp`)OZ@u;tG zi}tCWlmi~{RK7qqrG@-ry7(X(!bzQ#r$KWw8|R>hJy1@@(Rx70qJ<&?C-68JjqkWF zfjDd@;;7(6y+I@pgK-?r!NT|yYSLlvIGr}hJ(uorI@5NY?7SGOqID};z?Wb&6ozCX zWcKW>Le7lmOqO-v=HTHbIYD&6$$CR59nBZ@Fbt9yqP!!Q`1a7{xB*^jOoOR$j`8ZW zvXs9G^a&0!iLGFwEQTLwB{zhd&;bACLX=G>Q5R`6ZpK^q7(d{9^&*gl8dwwY0Da@% zvAC>f2WfTCr+V=sKF%Y#ET@s=Jb^>*9&d}viwEp?oXkxN*~~o^hflOh@6;#sUH*)o zp}RvS13pks%F2(pA>I)AVU||V&zgg~={CjEPyS-)FcwORNirYYH#?t^-@p#>8d7l{ zDyPfI8etEyi@V+Z=ynlb!zRddI;bDeIXEEd@GB2Flc80T3amRldG zL})x+m&u^1ilzBDk~`=Gd>HDAkMc5a_x962xQKh`BbdnP0w>*gm6uzK1$HbqcIVSA zQA3=?D3&a>q479ewQ#Bs)S%r)(Emc2)i*yMwPF}TI;b(mf_nCY-rRw5q z<0rMLx+?EZ#Yg@+;*7V;TjD*!%>J_w6-ouyoe!pC-m3GeugL75055|5A=2Mb9;a~C zAQZ`0tXN|~!}&3<;x>Gd3ep`MVHdzRstH_>eo@b~`DS>itWX|qW*-rm;fc2((1R-5 z`#7`v54W@1VrxAZ7Rae`yqr&KLxt&rc#5%5nEUIs#%G%GbE*p`Ff9zE?%3J9>r6OC zAJis@vTn<2rn@(K#pnY3CMVl>V zv^N@>*bl7^R&u!Pm4qEuX+9Ggqf20JE4!~h9}gU%W9CeRE==vY6*R%UGKKRP*Xe_X zO{?<-C?Yn?39_@CO9w-DC|0J&N>rDcP^7qEcaur6I!xqU##f)}M_xfO%Rkpz4kzIQ z_&BE?p)c#C#xo4(@yNPdB zHf|PrsG7qY93Xem`_N>_<@*npsy9>wJHb*Ot=oEw@Q$yEc%%yJ8Cv2PYXVGCuegF_ zJPqO8P#@rQX5;p$n*0j?lzXk*hFQn!&c@zmiFj)3_2l7l16YsD~%HTQthmYzD_@7;z z=L9=?15XvCdgLt!waA)ub(E$8CNA$3-_x1Cd~Z z<~aUDPxJtez^Y=rd@jn1cRWWA<0z~GSqMr&{VYMqzOdk<9#7A)#PEsM* zf=y+l-52h8FVqL)VmIxBydva-;m%q<9qg;p!U*x7wT?Tw9U#gH!=2t``i%dYIc}@7 zdVsH;L|E1fs{ND!r^zT?GO$&rwU%NEPHik~9Dc^#JXp_z-Lg77GaaAJY|KH?&Gbbh zHAUUx46-ktXM$290-kHhIiWMGH|M$n%E?dmZ8+gw(6@O559a$+l+qdUjmMvIE*9j( z{FBbnVspZO$U?H5>>y^FZn@_vm62NG8fz|&rzZRdRN=oUr042ow3b)Ob-sCq#D-H} zKFz71zrNsRqCxl?8;cp@l31a;2S0n=_z3vLW~j`|FwV?zS=~pk(j(0MAAp1^DW?@1 zIIpg#8|%+7!AgPAx-{Om@`wrCnX2oi=GG>al8=TcY{Heb z()%bcL}Pv7i4Xihm4{vOBWG4SRB?Skl<*6@5{lQ^#C9xUI(oiNMAK*?tP$%Bcb3CD zdVo%(cS3z@J;soP6gUFja!OsNrC1I4(HVc3fN1Zs$XWPi@; zZRH|X08epB>aJ$cc9ZG$!I!Gnn!U6ViGvnGPC=o`<0s?S7SEYN@iItwM zI%5^*C>GI?`n9SE>trt|Ve+zwm<$i;HeZJrNQaS3Y6$({#8_CQL4cyzPe@*9`udz< z58MAx>^8I30=n@~Vw%Lc;X3?d*eWB=lY8+#Z_vFxiOZdqkk)-ge@jcWgQlkW6B+`Y z1y%J;?C3;`Uj&puH>Fi3WeP$xWWu0$XvJ_6@19-`9=GC?oE`pvV&I1-EHS_E!z-Nu z{t{DgFBIUvbxR17nV^s^#Z_>Id}wdvzM%^`lgI=a>2EHH55zF*Hs|q@fwr=X2KlMJ5@PQ#iRdP~YTEsKhl`LscrT8L&(d?RG%T@JSv#z^kWKIQin?9&LiiVdiYWO) zKBViR-}Siv@6JfB#g$}v-+cMWwA(D0K!epWFW|o6TUJZ!gWLhbOyX?OtJN&;7N@i_ z+4-GTI73MufiJl-o#SfQPa|P5}A29=RrC%E5Ed*9v$17_QE#(Rvy$#3sfQ z(qUFP2LEFp78T9)px`$B+}z_xtU?LAxtz*s1*=snN)4~%eqUYq7;HgTWGLHc2ZgAB%7xjTGgaB@5pEL zD%1tOI(5V@T^{a=wB|f^>Nqc?LwpoQpfEi40siF19K}tkntnpPpo~apeS=%NE&j4* zLN>LGA~8Zl${%#YZGm5W_uyoxhf2Y>cq*VAV_Vidk%rfKXVhz5fQQ3)m=5=3Zr@!o zo;ItC?g#SA(>M|q@K!pE*DRl?#!qw~wU>TDbzY@^Vr?g-c&p3k`6|v3{8T)G$KW(q zz_Vfrb#ar>F?`CdE(7DlQR!NFxMe6n%dG|&Ms@TIH63PI8^m##O|4Zl{e+Yz*Dbs% zVkxbc0kYe>uoNYv|FneP#ayAq9p0j5(;$=XtHo$;qc-t3@eO~$In&v3T-WflaY*xJGAu4~Pd*Tr{EtPfK=n9h4?H|!SQARpi6 z0dOBH$d6*Fc%^fOs#26JCL?8T_)~v?+13c|?DmDRP72)OT{JB82(MuxuB#4EI^!Sp zoEuyxREScV&WMA`u!t8J$Bm+5_(V=}hG0+CmAXQ4(+IEVux>zasI7eEJ88IfnxW=h z{8<;_(eRQg=+-#Wo+_7PjLC-e`j-llhQZ>Od<!+_jIMn%;8xqsge1DjCkSr{V;? zkKbZZoMPIeoz94_towY)T}bW4dC0@F=(FAf!(?kw6?3qo2heklWgmRTyRs}s(-$hp zH4Phuc!r_X6ub>9TSXze`b}q~`511u!QHw!STcw|A%^29KXuh(4V{e@z2y%4!8d8Q zZbCs?$A3ag>?=0l6Zndj9K)VBl;gw@ba^FKl?~u8V;Hy8B``$Ii`=%H$I33ViC%ad z^3qZ-Kb{n)xjVY0cc!l&j#E z{>AZlkotQAX`rE&*7juH6*}iV(7AD}H6F&QmRwX+6D>s^&^jd)6a&oEcc*mfDXlRX zA8zt<04#w0FoUD$7F3X<`G~iUXUQ?<>EBXA^@uykAuvrBr2EESYuaTXpLav|hPGxt z!r?65H>_RUoPRs4DDG2tucpq)#YJsCYm^!%` z=q`@L9GDP85C-WuD-^_@yxH{EVBHy;+rru-F2j3DPYv`0K7e)krCNgZoT|q1`|?J* zU~ca%J*RK1pf;|;ixAHxVGL}82XM#SOixT`9fbCJDP7~g_#=(x<8Hu^jTx#U_TjqXVgK+2 zv_em4#+At#4a;2h2Lko$oQ>SwQo$g+&-3&o8qO1Oq2a9^vr_20jiaTD+Q_fXhjd-^ z{!r2}9lpaIp_$IIJ~eR&uhUid9bAT8P@li(+i=9F$#-(zQjp5V;%FziyqfdFeVBqD zWd$CPJC_AOH=KgAVF}ywUMhrpjWXbA{xEIy6I^CH4M&)LbQ^L}IW}3GmK~}6a50qU z^`eFbxD3kCGwU=cDSFM)1%=Tkz$tO$?KF+L&=8oa40QyT!x-Kq>=mSK%*FHv${Ry% z?xG8wmzmtm->f%H=~8?gGo>>H!!-V=Yno-afOLRK&>D(ENmbPnNhh*RYhOCUIZ$6T z`Ub1Pcs@r+gY;-UNzcY*HWSqVUV+z)G;U-z(M{+XbTeFq6Y7h`DyVoBOrLY?g^Yc1 zmgR@^>V2}t1I;{$;O_E9vjXgZgy2xCu??Z+$ zS$b!j&Z5!y**FT*bY%{Ly0{wKqAl8?1Fqx@eT(MsBgm_6;z*ppGjuyFX|s;vGOO?y z+lodMx1zn~N6c@Rp+YHH8bbpAlye7DSr{!1Y_+)@R#-Y=A-YXNxdO+CCQeB@w==4% zBG>~r3yH0?-i9Qbd$88}QrpTT?H8pT)z2w{Q-!o^s6pzY5KkXC(oDIuxr<%{h%oKC2w!mstPle%k@ZmoEjjHn!zKFrb1eFS7 zB;A-R(Ge=jS&$;2b-t3n2PAjR_1<|i)hPNX&8B8nx%(y;m7oy&&XLrxd*MG z`{El*^iB9{T!%UO7`^A^=q5fqT{904#ROOL4h>EVpPz&meMf<-insyz!+ngu+w*4Td$Hnv_!pty!?Ybp@6a4rZt#3 zCC!H7qDAlntTjsEP1?Z| z#g8xWTx@Hs=l52Bio`Z}MDk%FJ>$H%5e%4$KhzD_Nr(8H?CC(2z^nB_o{rtYReJYT z-BtzK)=_JPD}RF{H`V{iYB-^*+6|hlpj8MzH;>RFxCJTD8h2oGh@%*B^Pluy+$^5E z;T6>u{W)HgyI$J#3W~G7q$7A8-{D|cQ+M*n&NPRDv+^swO=!JQWPCzxjqMOkR zs*j#V8x;-)SJay+MXqoh&&RFCHuT_&sW4vHx*by#3qg{6`>+H~Vp7z_PjsXA{diQDxU-9+EzCTcO?HS5XQ zn;0K$JK+oK4s)<}gcr(3{ZifWG5b=CG{{=&B@MPN<|9V1s-gz*2JrW-Y_L2Ncbq9C zavfG-Q;N@-$6zUGX>REQQ_bPrM{S^cIZdSDq@kO73#0e|elu$D%A7?!&r(*s7y2uW z@%UI>-~;AiI{v@+rksbR_!eDM3ca*iWsj01FU!JK^A!t(7zK=*JjhJbJA|ZH3Uy86 ze$WDIL0NuIqiGTEfKOQ1ILKwqCfr;7QCp2Nd@0AC#~Zux1vis>Y!APUA?QzTv|YcT zOkRgYWX`ThYgtGe;iwAbN;=X!X4Zq3#x->U=W$ovL7L?}>HfF*6$V3&@YHggk8?3z zkILylRaI$KR5iw-qK77UnAqBY#(*!h$G$ikJE}T{g74-EU6qPM9#ugc#}@suC3-@Xd+@nv$Bqd%91oC#ru zYwjn1V6C)o3zwA{{h~*iCD31bTr0HFebZ0>p?+{5&wvM?l7H{V#khvI>aDa{rdGji zoN0u?AWEmboR5~9XXqUyVo{@$S}F`!kt$JT3glf_M;vi0_OgsutDz-jSbx(iwBwHE zAed^2QBzRyK{J4gVShQ#Xw}4K0v^y~sJ67mfihoY_TzJSK<1$uzNTf=P%^3v*THs1 zF_k2~F_SNIlH5TtveIu}tLoak=Ak*x`ZXiAQad@!4C8Rs7oYPKF2YPvl0Q9ie>0?J zt4%79YFRzalERBkjfdziSIdEv&f* z>#34pD-E)WsK-P8`2ae@cggxm@Rq6z>x8IwlGl&GU$~|!6(bA9!58B*eAFh@0-yi= z&MsEEh_7){&BJp-!END;y3P+x8|Y=!!o0$F&Dk3UDL2SZc}01v7215ljh>?3+2GB`Xr7Mak?J$F<89mX+i^G=055oX71!um<#BmB@AVCz>jE9*>z| z176&dYmklJfR45bv#c@n6FaC|>I)~D$8|5-&%w|EE^!4ssD^VH^A{g8PU9sG(FM&o zcx#h{MQJ9zB^&WR171=r<>xjq7Yj@8e*n+nhOoj7dI8f^NlP7UPp_#P{}Kv{q~V(B zD7c6VU&0TDBlaZ+dZF*}Rm>xE@PV64_qwkt@Mh~BGgyznV9PG(WMPU9IEzxKYD+q?+?RtG`a=S8xDlL3h4G zWu?~-G#X-2X-Q|v5$0htWg}fL%p6GXxE}_oo4nrKN&mqF)xwek5A|_c$6de)+wpwe z3v1i>;0$u1S90H%u{pmqQ@M(fhOglyFQ-2=h>J+7G*hRIrr=}xbCg;K)o6@9WG0fk zWM+Rg7Ut7@UIJ@rvuP3Sj8q+suFyk26;5%)a9%)p^eWE5Abcf$*~-kJTeu$<@pe8X z`xFevr8Ufg<#dqlP<^;08B|ZS@q_)zSI^^Om`~Xm=@goCmZqwqs)tI4Bck1od{%no zBb<#z=yuLbu3_1)>@k~S%ueiukHMc0NCzIo1M!inVd)L?&D-W*@#9<4Mu!Md^%JVU zBaJ7o&|IKdf(GysJZY2_{_=n|@L9W9k5XBcfuFDvE`@GT3g!tpZlShj1y-t~+OEEe zqIPl~*h~)6o~PnM(avvfNQEICU-4dDPM{H5e|fqc`UVfFx0SmbkN@ zF$v1dam+%GpAzU`wvJMZ?KU^B-@_RbR>);mtQKe-~J@^M^N%x=5&!{vn z;MtO6rd&ZAwhDKbmYHg0dQd1ll+@{ug{8w-q$R(ji8753VHR(Ni|B;&paU1Cqg0vu z3Z0L@4#rODrA4@t>8!u&0RDyPm|rb9JUyo% z$|PGXZd^1vTO9Gb-l!XEPj*&2v7nH50C?g&)r+d-tka>;QgrqeoK#O^pT!64^iy-b zd5mAHNJ;oV=%+S73(3gGG)`1_Q8d~Wdl?GvQsMvYc5%J}9n}MYW+^89Gtur#0^-7e2}v&{sW?231Yk@I-yqnyK4ECA3p6s=m4> zc~DaFU=P=WG`=ldd>I$Y4m%2k-KM?5{8?%oe&zGLPS!3~XyUMbLZQ-?H>k&Gh1a}C z$f6NDL5!&Gt#E%UIEi_U`4FNfb2>!GaU7vN@1`I<7XGMAwBT$G)y}dXHJ~7MqQ$fv z)`%hwt45-n@sv&D!4JnkGA)q3X^m^(ifGK2KEMXyj=E3*`r<})6C>d=&6SRRo6q2W z!%1y}CDJ=4$^;bVmwbo4cm)i`o~oOXAAC&UiU>?HHz*Wr`AFY6E@CyIdhiEX{;XS#_(R5rgt(Ev#7s!NsP)??-x~TG{ zF!WXNhK4W;ud4_0vyqPRPE1Q2!O-^S|_N9;W(WN6>H&Yv+?!WY zGK?|OaSLS7DE)*>sqKh38T_~zpP;JJoLqPvHHTCTMPF$PZOm&_9yeez*5}z~DD<;2 z)GcXoyR{Q#6 zb~M3UD~$LR>~J-L3Y2F2MYwM%=aa1NNJr6XIf%`~7wmWg+rz(nlML)W`gmldpxrB!)VO&#L=`YY{+N^p(0$XTbuG5}XVsJ}_o zeGtS|C2Oj~TzD$Iz9dwU1nUB0g?~E19BC94!3!(FF*?FW;4(aeVlaiT37fd$zv?#( zlf?577b33C{V0G(VIRrZ@?bH(q9;^`tK42FEtT@|8(6DO3fFdkx3Uf$IWPN=zj)j% z;d?inq>8~??V=}`%Xz;lg1fjS#6xem&C9tHuc1eJlVoH`6{(g<6T3p4IE`5c3IF-UG zmg&L}$Hn>YW2U8(^z&HVgkC{Eqn&Du+5A9eX%8ntH#HG9P?C7`XqbVmA-}lXbj}Co zpew}lM48oWcHjx{2Dfux-InV~zsQDV3^WS7)hGB#DWdm6R8m*xMz}=1FrEl&o76)3 zO%c)gVLc1G*|=jP{f*{HrVf(U@-?RsA2R-`UTO(=QHU)u;|8C zkD|x?mn*|g*^xQoAu}KyK7yCb`FZ`0<8XaE1(;#qPrAHPCSRd%Oo5XF8)Mbw1&QLX(4cXnT-iX8SppT(+zR8 zTn55pL|w)6E0=8M*UgZi8>tUK}m0sRwrw_bJIqW;GhYdDLCYCx|gO=*sk! zSIU${k>;Yf00Mar^g`sT<`LQmg`^*j#PXtsU}5@EYPa;rh2re9@S#c;W_1RZD{m&f zdxcQeCQ0&Ru!hG<)}%{bzfyCdBF&TB+YUpNpUlZbaoBJn%T4&%vIV=6Cn>q3mc}Bu zt=rRA@`82hBNx_hXqjjQjpn$JUy(B{p$KZK_Sl@jt2&ZOLKXzzXpT1XkSm+$YYB!w zWKX@yRJ4&W^6@+sK;lVl(C9Kg9ebrldPE~j_mw@xq zTwl-@9w?-r#~8^st^FwtdY~7!gCH6!v&B$cE$2{E=z=O^7t2#@A&s#!4Wv<`=1gI! z6Z{|S#VxoNA5gNjw6wcV&<~3U`8Z%JJjfaPI!%)`8O@C#k2EDy^@P*WfE$Xz!TpDcu#!UkV(2lV1bTvj;wKP~_p zB~dra+3Lb%I%$sLva*hCp#mE;5HDGtLxAa{A5#t9FZz3hLogX?N;W0(Z1^j>^rd?A zoCn}?JcrR-nL_voJ`qiqqL}honn=+>YL$U={~a zHJ&NipC(BY0I_V9IcO-}T9y{GKb*k@>Jj9=6Zskjfs+ta4asI7P{Lnzp{mf~FKR3u zbO036uk<`&&l$!Y$=}>hjy>n$=%5~`5s4cL9#&K5|4C5s0j|$hVg84W@eUbg`Kz(&> zc0dQri~qnSY2Ll%o-)`=Tw{mqc_5?PM7D0Fj~BP8u)=59AE=xLhh?2pUF!Qa;t z^=nAR=e$#Y;sWXwF2gpmUYgV8nf-VqPZiG25Qh1V-|-i|r5n~QG*TR59*%=SJOak! zBG|@5xFU~|ZaxYt$>Zm_8lR!=oXRl}EadYLMuG*}z)ENb_A))kbUk>XGO;!mlFk)C zcW3~o2+ae`kly$fZbLfN(@&@e%#-~61I=+UJd`APt3L=aG#8z_$)q-gt3p5XrKK^C z7mi;DRaHqnEPUg91- zp}x$^D@m~~^TG22p44)~6SmtdMe3JyUzyq1wEWHs*86)7h9wfBW zTm5I$0dMn)-a{$eOZIRMgb3$!fYvhcrTMP#aa(Xxz+U|3%ee8CBF%^~z07D~o? zii327c|tWO_#~ee2k$28TLERH_cv9yRS}q@x6xVIMdp6ec|KseDhcj-FAG1IUtG?2Mfj|pw9YZQJ{vNpsp2Cq zB!im3HVQSH$oxFRzucRzQoOA2U2ac(Wd>H@DYcQO3gb1A?s-xAZvi=X8zGJF=!gFJ zRr-G~;e%v!Hiin%6y?n_qlIJ&DpCckWJ$$0T$}gF>^_DElFi=I-92Hx8Y6oiBi?54 zI&r+G^alKtRk*fmZX!{P?!i~3b=^01!X$l?a(}xdq66UL&|W=;mK-mRnL=&E+g#8Q z4dI@f;_W%PugdC|6wE8BjCO%yMpe}UQ_0JWp(-*T=b;(AlVdEwLwsK!qy;itn;@Ss z#dm2_CI<;8Zk8l1PZ!ut+{s&Y#g9ChZTO|E>{6ah{@`xZSJiO}U)D1vS2kl5GP}?% z?kgOmX|*(+hcFjogbivTsM!!hTdB5iNK=?7tdT65+5peEFpV`6c!5z&Jp@nK2@7~Q zosqV@6Y48F3=+Qz7LK|Oi@7!ZrbXNV3mMVykap1&+LcSp;)8AZFSNi-Xop)Qz3y>c zI%Ce}HfjN06W0l#y)t`qn}Jx+AM9TKc>Szp^$GvA#KT@ zCP|wLfpM59nf{scaaYRFM|5)tR94Z@PRX*@!pnVxRdz{}p9SBgZJrg9`6G|rrGvDP z{)Nse2@hcbQAStJLmvO{{db|#oUPIXZ%XQYqSDew&tOmC-KVnNNjy+g;swd%rYlPq zs-l`1!(oNCl|1bOMP*JN@*O$GLwL)sumk?#>Qs?kz{Ch+7O=UB3|YZ4*p&N=Bl$8x zEfoU$cm+?8781+{VKAPAc)lxLwW>74hT^*|IS4L@BhKV8LS+ZxuCUJ+eoN(Xh4BTd z&=|?8ClDl$y&`XZ4^xG4{u2*-&SPNR|Gr=59=_$Dx~EV?IM^EZVJ3}%!KxZ&=Qau5 zgYrsJ_~*Wp1UJG$$=Y+$J>GFUcntP95aW3jMRIr1{5P5ZSrh?7jLFjV*Yi$xmA>9s z5;_^0sFtdO$`YqfAvbDHt*HtWP?PW_d=*mh=MPXtG#^I;D2X>=OH7lQISALp={oZp z&H`6;033u~{-Z9ETC@2NZIJ94t_}!SMd@gc5x<@(+`boF;jwsF15tq;^yK~2iX*rb z3>3FL#U>AyRJ8$ZTaFY&tm_4N7)r9~Gm6SOvli?@bct0lKDj~yoaoIw`wwKw;QSeP#`ZLJK z14RD`{0#PCHOZof@KiOzOR^J(Xga&gJa*x+xXRdt=lCAI(`nLCBjgD8!5h~>JGKKK z*ud`6;WkMtA#9AZ#YT3@Zaf#(Pb3#{+NzKjzrhK}%d_#X6xphJRFP2-f$+Wqcry3Y2O3z;SGWa^fiy zxUgi#4xxp?!r)usm2{sA^p<1cf~-d-ufQ!EaFd`uE$f*#8w@eeMR-t$Z^mumshOc-hNFt-qY zI)k<_UfgOu#c*ewh+D-me@YA5ETr~RSgWI)%i({oCGU~>eJfto38E;3ev%30jV+iX zJ7pvPOd)sCYXme?7bNj^5Yi}qE>5&WIAyn7$pyK}7rf1)+(CG8aQ-UqZOO zw9&@$-1XQElWC7RjLOL~Dnd7y`@h$w`^&66KjJ z7~!YR)Ds4%I+zavWM5;%VGRz}S?sKmg)8!kzWPa;SfpF$$k{(YZ%l!`atFyW!Jf2} zmkRO!E7V;?a`>9)y(AQsS$f2iaWx*3l-og~I%X&Fm6N&4{|STB^{l4^2s zuwW|M%G#CTGW3K;p$qy#Q(1*dlKLx!^slo6PhxNBnw#+^gh~Un6S^^=z3glt*X7lc zKSSZS^s^x_GoF0v-yPFm#y?d4;+S7 zF(0Rxqxrk^%Gct`fx0w|Rt><0M$vuI*;G{yzj0pZ09z%Y{}DF2D|#3QttCt6tL|6{ z>^NQ@qh7Luo8TBeP)A@T^&l7V^-=I!x>^@Wp%lI(K42$nHI>?OQR$`cgg>u96Mm|z z@eaw?5?B-5C266C-;(Yt=j(tfe@SY^XTe?7A;H7Hg9d02R zl`XU0gC^=r(pDc!Ke`V7|NHabmWz+HfStGxj_^L2`fixdc+D$JcLIo##@0^S(-^Rq zWVpcLTw2tTPdZd2_r&|^4%`-hSW70)#oTXXEGfKyUatQ%_oB73lXf^5pFq{zPm&6w zZ58FY!7t%YnG@*=4w7tnU>&06*CO3Mh#R;=~VTU@3eD<$1pJ?dHN2FX+6G zzY|F8mHuXfyTxO^^Hyo!1^)L+SS2o`?PN`lVF#6;{jG24u6S$(aopih zPP$5I7%0cNinZY~C5TdPLQ56Pd$lLu!GbV@lw@^j^ioyPOS0iQX9!DVh_h~n@win~ zGnR`BaV_EIoJFI-2R(&955oh@FO$1MxN(zcHx`CqJE+4Cs3YCvN_Z5C%gmgRD?KfZ z#6}w7CYmCB`7#zU5R>Q-b(YDyBKy={c5jtv!&7+4Uv`IePdK7>;U@T(-6);!;WB8$ z&xE4u%h5Ck@D_2h>f+*Y{8aRPLo($V+RHp1lq0?16VOw*><-V=*|bVBtPX_J9&HOt zR0(;s0be7l}7j^kV3Oh;~+!W`1WOm~KX+k5Vo!#Mjkjpi7*;6KEnoz(^`QJ;p zi!jp}d;q>e+4J}^#Gn^W$N9WM`%nq!j7wD%^btqS{dU%)*dF3#t-4T2Og0|jW|`PM zLV`nucV|laPLun$5qCYr|8gas&#!rn5N|$7?_Z*{+`rDqbLvW3_kzoCAKt)NMq$FT zlDZdVMo(~Wam_gKq7j_HBlt6&mUS8@G~q>{Z}0>)1iI2`2Jv89^`1R+P4cF(U?Y8e zp=fs(^b%bM^A){7+QV}3<^95bK3r2WU^D-LUcyK=;sm>RBDOI`z*7B`Y$U;b#e0hY zVlpljUAW7Yd&p#*15fFM!=!gDlh#+5n@HZ>VJB+G^Ci{Sh!XdSmg6MjE>ble4IZaaR9XN7t zT};~cGdQQ};sfbbMX9~e*hW05{2@R0hr_}TR*EDa8Ymo4TaqM8_`-laHUW#`MZe+57Ff)oT}gyqbNS3nOq*uKn;|2qI2vG z8X9AD;mP(qjUS0mY0mZC4q)IbbQ8s0<@=J0j-s>II7__RPB?TLBw%-0i&qpxx!-6% zRDQk-%$FX13989jI*7Br;9{ca?y@VpDL{0Y1HO{gKP4yT%j}QF2{4_1i7xHITc&6R zcZQ0R8JkfUSrV(Z=&X|XR)Wmw9N~b8LY_rIQ>dQGnc#q>v4o^_QOTR!Z?0KS-rA2t zq{H2Y)36VIa}^%LN4W;vlj|;pMX?Z`<>vqUuKi=&UwEQ6`pM4bex7R^)D#yytCvYf z*p2=eEv{P^CPOt2*Z=Y_u*C-WOnf$+7vVPXlxUf;l|rnygdyI^?pB5_vZIG7PKa&} zTom`ZBV8sEJHQ$0A=7q3_eTO28V??YSU?sH@gM}tdl zYmyvkgu6uDHj-5}gngTHQ)!cUyt7Pr`7_PGj-I=(WB{}IE&c=D zC70&Pu6W2_uV4=xk$;~9NBP?Wif}8eh82YUPe3PJ1oc_rc(agop0#7<+rRshTcrdh z{E4X;{WfY)_>R!Ep;1A{ezo}d_uHbcP2Qb&)8S=@r^}!AyYcIG__gqBy)O4XdjH=3 z6Z`j;J6q=TiX&t9oZVLH5Uwn;^!7@!44zZk81r-E4Riz4X@VeQS)k*S)~`2Zcund!3d`T7H4%sEzE45(mw8I zP^&oCh`g^KdSh(BW8e2)SEs*u_O#vonn6MLojjI&s2MW-d}WU>7Y~F+9nzmCeW~wt z|INcMFO&A%FX9ywUm9KCpG_~8_`xR7p|pCNRgU_nPOg?!F{i}T`r6iLYSdX=xVFp6 zR>{5RbqQ*DyrG-xoeE~nz5Nw(8VqgFqvGvacGYd%v1Xp8MLRst z?_6e9o!|M)(mR}Hl?$$PzR24GyB+RYZkv9N>2xq(Gux}=X?HcuD{)2g=YWn`dlS;4 z+*1nusQGJt(2X~@pHrn=g;>)m;Z6>!;;S)eoOq8`2_|ie9r6hDr{Nk znutjOJ%c7jol1C_;2oYX;%UN+j2W>rQvOZ-9yN!Zly}m`zXxN#<&8;eXtSF-n;ux$ z#;WGV#-#MLTY|&6NA{n2}+V{11d*4t^Rjl-{Qf%L+|1)9aRUa%`JsUt*9Wmz-bvx3fdZgZZ~sebne{ zi5cZO*Sp>{wN2lOGiv)ZC{k}uvzN6JD!g}XTYG%9j52QvwJqMbV&fuBUCKHwvu|A7 z+xZ6{P@Hlh)${M4@W2F*n3y=5Y~^S9oZvV0(VFn{pGNqc58m=B+wa|nHC{h`3Vvwv zw$PvSXYFq!e`(-`KsI|cTN6mw! zkJe49Wz)p1Q9zR>{dnZFUiaHBZ@s$v&;IYa&TMs|b?dfs+~e8~tY55tpC&U~ZfJD2 z#;S5&Rkl};D}TeOsMEAOS5#iR)pq|tZ?z4PnyC1gsy!dAMvgqZcPnO^3-%a`0{j0~9uYOKn%YI)I_VnA@ zz$w8FKQ4w?zYqKE5au1YBC0_|?TC!nGBN#=52ySLv&)#MH>cG|Tb%Y^+RmKoN%NEY zXW6H1*E>|zzvceU$>^K@4|@F_pA}?jYL>Uu(8H5wWv&G)_y;hWE4tUf3138K2WX!#U3s%fPJT*^hLp zdCM53Pny3|D`dT}ZJIqNxsaZkJvs4thTGr0Q7fV^{!LABjn&~>BRvu}MwAJy8a^xL zR`}1vdkIs5oqyZMB&2jpsvh_Fk7rI*vf&7I3ya#E%ik@3NWQtY!}6T1@TtO&@?0Xe zfJd3j72V4(t})8BLZccT{;S`ux_#*)m6z6~24gD}sZyp^^;#V(-YV0y+`S@(OOML^ zizUN+`^rUIi%GWlPpJCGNrM_m(f`{L?~e`rh?@_i4wQ zcXwW1y?qPr=eb+aC-LLJD-|9-5BVB;>0Q}xZ+yA+cQzPSXQdf1u`LM&@#EVA7 z5);d~R~lIAW#wOu!t3mBP`~xvPP%uVK3n>}={=$Sn3nU}4eeytes!1Eo#(Xg)n-g5 zn|7(q&NkZLxQClvP5V+wCA>=c7dcU&X|boy?v9(C{qnoyO*5+UHHFrrsf99oC1*uV zORD?#*smAg9tJr2Py4p{#|OWWzF&V_{Bi74;@gHF=KAXIm%nj9nBQ36ufOW~5BWIT zw^iV@pr~Ko-@OAie*OEc|F0L{xA_nGoe&=J$0yn+dT??|yi4NSkZ7LQ0NM&is$VFiXe6L@UuXykR@j^4*Muu|_?WmD?E194c!! z(#c}8J9COLq@bfiJ)D+jx1)33dU@A7x`Hh@6sTQvNd9TYPdkUaPWBHi2aNl$FYk)H z_BJ~p4I5fY*>86Ag|X@Padf_Nyfrfoj#&b7cBigNdl}b1dU*B$9F}@2x^Z0fE@U0+jKbZG8c3y z>sZfebfFf-jWW$E9IpJS!OOa%T-~a;RGL=iLHT?QhSpi!;D_5~*Z3M0s`MzcqI|xR zjZ2rSbhg-Am&%0~<{w+s!+tR}&(28fmfA3-c640Yj_5LBdsAM8KmFnCf5xvx@HD?V zFJHa0J`1{k;nBYPTOO3Xck@x{w=>^fc*a0F zbXeCqfAfM(-!!gJw`0A<^>$bBEPk~(7y0ef*x{CQMqv-fPEO9n+U5OfIj9N?W9+r= z&MuewJ7G=2iP%>`qXYj26#aE8d|}A7K#zcmAyvc52F~}5SO5nMHyxns<>_gE3j2#=Y_aupIGlI1%>}As+c)1HV;U~BDr0lrmOR5e z%}%&6Z*d1N9;4@&{`lGU1CFq}k!MMsDS19v!nFr}u&I{kjzf8e*)|IAswH+0XscON zb+W%`n{Byesc5rLUBPJeiFaqu)2tfUCDF;8+Sv;;x2A@s)lN-KU6b9-`XKvi;(>^x z(YvFEMV5)|6ty>IeZ=O_u+Txjx`ow@8JMs=^~c}J$y?Jmr_M?Fk#abxf5xYDG`C=H z%V&pW&JCU0<#)-Mh`}GjArn8}z+w(2Cy&18#il{yiul)_0PB z-yn~OqZ!9}v9%5c<@sE=ZpoyQbIL7uy;?80p;I@To)2z4)=QW24VUj*W7O@s4(lUL4gW=1zE_z%D`kgV%&ji?k2LkS-y% zq30vYho1;u5*iZX8s-wRz+PUj zrde8`#jr|`d8sdF*SC&@7nTEgirZOiKG+N}LiKWU8J@FiVb{*?mCa(xr;pNm{hk_N zD|svkPvAqtlw&>Tx>TKlsWa6!ZA^!p>scGJyJY>&sFCHBy)Nry#?`;6Nta{Y!*+yh z3t1n$Ea-hmp@@YsF)?={cZZw}whcQU`80fZc$t_Le~Kk~#W(#^Bz{d zw~uV!uKhdrj;+2lFV{5HZBxBkwQJN&s@cEBovQy<-d*`qrI!_smVH@jbde?bt2vI% zGeoV2uCUjP%XCj&m9#A0C;oCg$J~l|99}h|e`t%K+QCc0Tq0*hHV)etR4pjsci-PV z1ONV76?|1@XGrj}->ZJD3ThSV8PYZ^Fxn+cUI z-8aS|dRo+wh*ja^qie?ZPjXK@9G4M0if{#~e3k$VNb3(R-wo#&Ww4zoP_VL8mjo)JZiv|To`uvGYPW`(k zH7oOn8LEfU9>YCfzM=<9%_tvQWmL`Jx}V(2HT~XVa=Y7|gSx!xa;QsW#{wPtw2y0( z-1=p!3M~dU+12oDgR}LQ*DF!KMgyCAlWV=KzPa+K@)ye-F6CFecaesLZ3~7tUbIWV zFZ41yBQ5FgZdo1gM9+i+u`?nYhMfp06tX#NRrsK=tKsj%HwW?05kE%!{2Ms>_uStr z0{;Zq|N8LL_WL4#&tGRlT7^Fh&j=qBnGpRxW?6J_bmKqHNl{5j$#ws3PFxc0Q+gHexq;e zy*gFz*7+zFYTDel-DnxDlHn6QGX1Rc%qMyf+%;T{yvpBF(Qc;QecM4cpH&-W8npxWCT9Hnx+p3>xKpI*4$s=|0 zRYP^dttuZaOHVZ&ww|{uY%eXv_Py>Bs2TPNIR^_T-S?^W86-s$O+E){rS!reCBHcA zaAa;&OVq-sis(P%uf}inp0~x}qe5o!ZUir9PiC`&%Q!}kCHNY9H7ki>45*~4i6=4p zyywFbV6Lf0Tc?bbXAVU6?fjnJ)7d-2JL}_}>8-Qs8Q=b^ZLArtiTn0P{cjE74M!S| zH6Cqk?u36o+FjZ!lD?B=4xJu8s5xr-8(8a-L7&kVNS%I57`xelLVc7jiJ2akm6-j< z7@skc%tslgGG*Cg$3~4E%2s7OAH6ggJ<2y}ZQ{lx&Zrv+Eirc^Z-voBxk7+HhpP<= zV}kw+9~*8v`UrfzC(UIAu346tCC0b9R&~wD{^4}D7Vc(*DZr@HW0jDCHk?Roikg?dicrJbgwiDxzWnr$PO5s7?961dWK%3pw zo&-cDdJe`Pvk$!!Wka$Mg&rTLuXB?7pr-`d1mA>kBAuvJ$YIzpT#megav+}~eut$% zD$i9l6T5Yoc|k<*g;lo&mhH znJp6IHC>q|TDx1jT5Hp+)ofSKRRP0&L%Bo6!?#sNO@sEmuFy!Zq*~dwR{I1{>N@K= z=$YiHg8C8bP>rbT*h}PYS}v=HiwnVq1xB0?-yOPKR3)kn-4d1>suC{e^SDJEZSY~v zR{nFLQg~6=#GlNW&e}phLY+bE#{7hodZvPx?PV60u}?ceEmE$Mk4Q)Q`@c`?bhdtL zd|m%*gS}yLgRVZj;auH^uQO_{ejQskt@&nqde@%L$gZ7TZ#vt%Sbh0}Wy;+;fhFHw z;cQ0iBu=HC54^^C5+aMd9QP<;;ixsE6Eh!Vy~^C2)jMX=*j;1W$IQ%}o&GKTVtT{q zh|v$yn$oRlvr|7L?TvpPn;5e&QXYCfq)<@GVY5p7KU2;UvvEgI8(DWaYB^`P zWJIkxt1i{>bfEs4vCLFq##y)8R@)!hIlwhXC0GR(I2XH~yZ?Y4K+VA&CGd%d2rF<8 z(cOpv7!f9eM#FpIbhsN9iP(VbL>@yr5%Um>5R2e`P=Sl&k~+)55)gErb?1AN%LQe5 zp1a3+m>#638oCX?j@XB|0=w+uIHY#KUJZyqvD*z>^}qKbAyA0h2&z|~KO^W~7rOz$ zJ>OmB&I(5tAhws-w%HEarrT83GK5N z7sn&xJFB`h49Jg4`+Apj|K9PNcy!x_meQuv&7YeSnin+nHkumgEx~Qc;{LWNZ8dGX z#1Gq}yB_u&9ncR6)jfK#c`rcn)F3g0xxTi*4(|NWj;Pu35lJVK!IT@L=coUX*^n(8 zqZ)H~%;Icb)}74I%=uFIQ^a~UUX@CvE>S2vA4E!AYG<*iE#G`bH z!Nm?e&<9)u&|s5mCiD(=4^|9WJ=@%GU1yz>oOhjHoF(8g;BVVZ`(6MGvRytNCA1GN zgnxy8fPR5Cz|=4X{5DJhnV~P9F|OT?DzEeX?cjiagG%r)nCr0FR+^g(5A+vxIodqU z1NA`_e}u2B8Xi>MP#@K`>H`gOeWHH4zF6O(m+B)7+YHV66S`vEZsT0r4%Z9BL)>=q zLu!$KNq~?}37o)~%G|;_%5LRc;Z+KhLcQ>O$V?G6v?-Jm4ultlE5qcWQ$#djF^|QK z4qn144czDd-nWdrfk4Fm~37p{JEvEWmgNObzLjJbz1Amwpkq~yYKcN99%tgL-oM$+~#r`;5hs;-_*c^ z!5w^?s62ulV~%S{e3^VFb!OV$^rh+lrj1JfCBx$-?|-w<*?!sftcI-rG7~edrb|cv zlPpV^5X+8y5?U!d%##Oa2Q6Y8^-uN9C$7T&j5NXeygYN>`M|N&4qBAPQk}x97rio1 zSu%W9Q9E>0&Ke|0I(sMf416!@sp+}fQ{Qu`_iKRgwcnCiRYl3D&L7p$pEby8`?Sex(<4EAchDK;NI$zfL2GDL+rQ=Vx40|M!d)*nH@) zd#>xP^DY?Sh_yekZnLa5>GewOB8^d9qY>!}^^md3qVgse&RlNT>&>ex>f5R?^(<|w zKH1P<^sz1ko;ZD=Qurs70lS)*PtK;Gs3&O;0~Q4?V#0&q!4tSAc$GXBf4P@c*`e3M z2oaYfcoFPyK=e{56twcu{0H1X4vD>=SwMg1x5Q@zzX21DEQea0Cx9NyT;mmOwCcQK zzigH?N3x(Vvj^3EspFnl_`hpntp{3vZav-dsrg;grN;LS#~TxxceNaCb&GpC89l2d zEct^Go`GO5cHM&~VE2$kG$UgXhbqhnD~@c6UKX=HmKk52Fe+(&@`KUyGycwM%Wltp zp0y_PpA25cQ2L~d;0#iFMe0AJD&n6P`0&^A}^9fu*0NBWX9OXN^o%qzxwb!+TiWZ@LKIkM?T&%LV|cSh_%3Gaw!~ zE-e^5DT|cfm9HO~p}3;R9KNQkQ*~?E#&MSEw!J_B_{b@7u66Bp&-J>)2z&;75iA(? z6cz{XM;u2_!{G?2glD)n7_GOf;E-B)B%BJL2*W}=Plfxf`=aL}v>RFm-GN-t7bp>` z@*MW?JbCUk*L<)EI12;&;GH2 z7RJ_~ZZ?`z#@Wa%=B*W|gl|I#q72bt(MD0C=vqjRuuG7`Z|0N*on@R4X!V=uD

# z_F-xeOsE<}+OL_j4bj@iBfAwZWbT2x{iVInzAx!M?(HkX?d!zPTHBjVjY}JTtlwUr z>D5bTV_W0n#?y^MO>bI{cE0Fql?yfbrYidm*H7?abUoodrNAG<+!Q>QKPlvSXnw@O zXrDNILP*lBWZvjA>GLxWXD-kDDXTwgIO}=V*Q~6pri{I5zA1kuR>e(?JsX`9nGwEL zbcHWxzX+V;XCgl%JjQLp@X^;%WYkT>A=qK}T1SR0-n`JTT1!%AjDU(Ma+_2%@I+EI zARL4SlVqu~vcW-VfLAf3!Bm-B)+3uNW6Sbn3*-z%v+|<)uI{`MFh8&c*c$+iqY!KY ze{lM`MDBdgcPJ9hLbjq-V9sGT3U9C?u7MPo?V*6&tFU}&j z2D*eGV!Cmu#2j)oWrOc+Y8tKE{|UX4pbvXOsp2)RW|&@j`Sc{EquFxD}9CJ4}=HJWZ{VqF5~}A878s)FMw*hgL?Mh&hq)Jb7l?i3~yJi3~~l?(~LqX~yiVKe8u{i5L@={V^jX zZE4EYr2Kes^x5!Mp_m)XCNOj9YX9>zzF&^-d-5B?CoBbx^3vT_=Nus3N;SpmpKG#J zrkpEULGc2D=Qj&D6JZRB%dXDlDm?%1CfIVIzq%I}mDwF_6>H5N12>68;l@1>S}|ih)p}s6)t!$Uy`d z*@l>iI0AnR!@xd36xbfv6|Waf_IkuPPnMhQ+70?TKzoDD*S5tv#aeF7wQaFa2O=Eb zz3Iepl-P4@Vb%uA66s{?}-#Ju#l$-hMA|=7SB6G{-Ff z4&>X>cF@*gJ8NsODQyKdrcG~|@AcS)h9&xqI*u+`$JP~TA8X#LY3f4t4Gmm(O`q@8 zOO$nsZ3?gq%=I?;dB}I@?N}3zPDmg=B)##WQ@d$b=()_|pa;R@xNKf6?*@Oj;Gi(V zOHFlQtzk36T18dD5`GPr!zpJ&tU5+XK&sybpE804^E0vmI_TQr_}jMIEHz~4CTr4E z@ya8L7jlXWC0*U0(6jIVuF*Ie9S!b=b&bC@8XEp>z&F_I&o#1IuXNygbESETFjbFs zu90ND;fVK;kv-U4Qll>};5~B+XN~|DijK&M%8H4K`w}0P$Qku2Wqz6`BQbkj&a<(z z$GsUFk<*p6CPO*eH^rVPjXNKGE8<$HRG7^l$DJO$HmHL!i(XC}^d)I?^YF#>{QwmvxZ7!l)-lcn1R!hYzak@DVgEjCQBM6iUEpj z-%#c7laVdzGOu%2YJGI?buRr|V}tpvwbh;l&Ua1qn4w*8H6j^x8~q#R6Q%}p#Oo@T z(9_UF^d-~?(u8;luZNw3HNy_TcOo7mA>?P|b;LWE)YIV_4fA4Nw7l9c=ImsBxm*D?Mi*7>o{r#C(~I0GQlT~^A5oA$g$DEb1Vm%>^@%Ajk8X-Y%~{|`ixVIYYjHtQSEL` zwwkS0sF!Iu`W|DsIo2w+_Ss(Al|Us(b8my*!Jnb-VJ_fMgiFM5GL9mm=F(aNZZhPo z=j@JP5_bacIe)saFN7HigocGJ3eTH9b0UzX0c(l4p56n)(#KKcMg_FsC^0FCwFFxx3oky&1*=izgoAd&RKW7p{eNZe>k}`>iKE9wP7Uu0EfUQQ5eu}u)to98 zKp*rYQzjC-v9D40;nN}SG#$0JL<`k4!O*NL)fRfmVS;L|GDndrj~u)(a8feTU)f*P z&ysABI3>~2vx9xI)FGClN3mr%URkazABj-AG;IA8FMZZp(6(##IS#gSg4+yDM%1I; zVIE^KxF^_f>=eufbU*4g>N@Hvsvi}OZbu)(puGF*F^%X$s2W5he400%@}a#@3bfao zN4s5x-Yoj!SnLoult3;}VOQ8VHnCM;EwQY%*eo61V_0dw1}t$9Y)6_Py#M=UUo(}dETUYWV>a7n~RM` zFAvQ$zztg6Tdh%}&=hD}b;*X^#yKXMd8V0U;aSt{3dcCtZchU=7XBOJ8FDguKh}wV zM*5vnLVZjV1{|ep15=r`tUUHzjt_sikR+NP8Xv|9EAh@hHe|J68E+QnT9B9#K)*yo zQFDA=5Fg_1q2D0runF!|=SD|`{kb*ItTf!vu2NMgX32UaXM5juw|Xi2Nb91OSo$z$U9C6UXLZy2XUVoJmuVY}E^C=%uKOg6j5>hrB+T`xqIUXE zW|RbNnsZ}6zp-V96Nw*#Gb>ZV%3<>7#0?V-HADl zE=Ao$-b5JSTf7~(5jNV(G=N+0YH^yuC~%wOEc%qz`H%=^uwEDNod zZO`pYfDJscH`u2DGaX;RT$kAWi^s)jp$aXZ?zXdyPArn_GL@)4L0L3nU6D zOW6FAqAw6N$v(snLD0KzxO_#|V;*$#+uz zPW@^0>@;iI%=D=AmuV+P`=Wh;-bt zPq*Q$pc!Z0Y9g5AMzm?CX_7h75@m(FEvCyBXrE%gYd6~G0EK`APy%A_=byk_;Gp;W zaz_AI?d{-eT_vs$u8l5*bBy!3*SD%1%N+zqB@hpk+XL)NY%{GQi`-OdJZ~t`bM#fZ zGM!PkNN?8H8E~e#=3AEM*1v5V?ODJ@hZ5ZCa=GV1$uJ0Z$eT=;kT1|VxP3%ErN{3y zy@;8^p2KP2iufc!gCJFSP`E`13C0QL^0Rr1xCc2;f+w?^Sw==3eY?M#T1HtyP9-+u zxR_1IZrC-?Mpqr!1sH8}Evd%k+KdtYkYzyBH`3kNF<$(8tGao7v!Q8h^T*~rEpJ-W z#g*+(IzM&4>Dk^FEvXwQ9qg8G9iF4QrcE?vS*sipPZFvOZ}B-vD+tuHl);6(5@Anh zTIB4QAL73z;*)QrJW6Gb&KT`8`fKXJ)E`rQQ$MCerC3LWCGCwD$EdtbrdCuYT*fDG zm$8eO9dx$;O)8)Aj8u>B#LPmrKpR~v9euWg7O5%1_&^_~%h!~tw95U%R0V%%io8y? zU&fI=9CS*vr6U7916!n12lvSo^3{sE;YA}gs)L%1x;#UPX|H9YZ8{JOl3anF3g{A? zjAWtQs6}WwdIqKvlZkzX72!_fw77IHG0nlJ;(Kr++y-nZhKjk4PDVGPW}#Y<6r>w| z8(P=Wb_)v%~8$H$VkA%6ZL+bunF5XR1^0xD2G(L2Hx6 zWu9*?HU*gSjKzjZJwaciqv~z?c%#cyV9{H(wmf@>eFAU-@OA9=_MrJ7$H{XcJriMZ z$Y{(IJb~2dlSyr+rO~??@E`?yDrc6LmQM5F{Q3Od{Kx#a{8B!LznnLPyFYk(Q1kz_ zn6=bP6t6FlD8x^AB5ph;9p!-Ug}iD6rdn4Uw`f~O*ouL{Ba*z{^zQ%NbA2KV2Fv|@ z-O68KB{UdJF#rEs|GVEh9t-=wegFUatqI;e7#ITPfsimKM21-*UziP|!n9B@Y!C{A z^+1WR&(J2=H;>7)6jlaZfvrQj-~z;T)LKL_mKvm?6Tpt{(LE0=|DraB9FN{Z`_X<= z*=bvkeT#nSaO)b{54@ZG6|NxLp1H=lZMsl-lNm{VBXCD)sv9^8-KSrV*b$}0AML(c)hj-tS3{fN zGwh#D7g6IGO(86iA>f{8xm?hpYFjx}08fW)?BDYB!6#;$06?+2IolD1O1@;SZ7E-z zvNib_J;lWU%6xtZI}m_i(9RP-e%_7LkvxBPf@;#*FQY5hD`yE{nc`NcJheFM?&6PjaXh=_lbNtSj9>^HOg)nwRSz;@5Q1_}S zt-eiZ!=1zsjB{JLU-u7VV{D==uxnf2f0BRy1&>VLo&9I@ z0C9&=Wa=llBNoQ|L*Ha6kPK^Y_`M(1H2P8EI^wV0jSartHpgh}L1*rurtWOtc*3pJ zA0{`A{*U<%c%iquTz;+XR$9xpg=J;y}Xl3?XbFT@_8GAIx5b3}Y&1dZGxQYH(2|t^2 zzhCUY7=r^(C!I*y6jF)aXPM$U?{k1>=N<5kw)v>8f{_8~k;M_Ev;~H^4p3Y=*lv68 z!kH2!&hE{|W*U4{a}GP@7X3L8ZhG#xgdZWlfkDbk-SdaFNSz=c>CYIV|1IFL8RIUZ zp5*?-j6yEg^%xb%!XR=umU|s`u{Wn_j2Jy!WG@8<)r9^ih0KlSR;E{u*&03y2u5)zAW9PTS7{Rs~UXj3hYVS6}eP$(sqFJ zY+Tx;GT~KMl;p?$dK=)M$IeC082;Gxt8$1K9FI<$8B}cjqwCQ3-+?UN%0zRJ0h~4H z7@mqQ4~>kyK^)s3TIT=kd;L@UajF=3R%PqFCu<{&PG`=(m+}I)p*N+ad}JOqDdG^l z-F&cpNoN-j7dk2@GqMO0wg34od$^1~AuTCp8h)QFqz7S}$9DV)&dogF4m37sovPkK;ZjPwC;u3It%j-9uGqK%* z`KDFX+?t*CqKv1jho*faRMlw8a{DiE$4!mQNJhN+;w;}VFqa!Tg`4sSSYE#O;T-W% zHg#U#jG@54yJ4SuIftRg=Wwz7J$I2aIj zy%e{UP=}_B{qYa~jjg9&-e@W!m5jTdqrr54Oue(Ux}I=v#;PR?Blju3R26il`3z;9 zPK$F!J$B{)bor+~q#!4SNhxg4c$3<3ka1;!=a<+x$KbKjf-iSbPsY!h7Zo;2sxF=X zso426xp9KZ|Idb%x4T{qfQu(!e%YTn*S_QH%%;b%@KIf3{E3I(oIDhG{8+su;6i#9 z=eF!t#ml;WY~+s(Yu{xbG$&NtuM`2B(~=gA8Qp1ezkBiKp_&?gdHf>Yyzhp4Hy)>{ zf>QomwS4L!{DD@|w+p6@u+2F$S?}w}$K(H&zcZty$;$AbOvgTVfA#@hrx*S5cIsP{ z^Gox)*_!UyON(Yt+KamNm03QkKap`F3kYARWITOae4>XGW?9OeSI%mdgx4<`sSmPc z#0wAg%)Ge#P~uaXZEnoj*dwsvCa}6xy)0_olFHfefD!SN4^w0t1;9_$Gn(nqUC4?D z;v2Y~Nl&BZSeBQsyuZK2$Ux2$E%`lqrP1C>l3^&LMtumgjXIow9L*B1EQc2`}FiDfA{gsq6M8}Y1A1?m^{So7vP);42o$~8QmoI!(Th^$im>~X&m_JzJ`k1RVnJp!lEhnCR_;X zMO-yyfSr^m76Mw*D!T7?v-QJb!*2nh0S^$%HH491_eO3^+RB(lvRgfEXdN^?Og!%2 z(G=z`vqn-a?*Q0@hpwZN^VJpA21PU@H|N2)&ghFi`@L=3pRmktysMynPYLqw>vszj zYl(pG1Jnk~SUuW?3nD zn(iPZoQo-bDNnin7>lV<*JgPR`kRjTPO0Bf|EIj17!ya#xDeOFz2{d&noFh4u`=ZQJyQWB@f>$0zyk@} z1GU`ppgdxMyV2c=CvY8c=!9kblRjIK!R~k9IO}<(wEazOe^d0(e)Mj^xA=20+z=se zI(uQDgYc`x-7!$%TQ>3I%B~ij1l;R{fu|5K$}JX(Tg3PsALh#Pa7q922yx}HjlAC} z%dj(16nLS1>+n3We~q>NlB^uLl)E>2bJT;7$HE_l_c)t-IhN6nKm;#Gb&53PfSAI-a{C$Q;4N*sswqc>8>P@GM*k1_FOmJZM%|_0{4Bx4_p0 z$-|n%M+U!Qjfr9=JEdm72StLKf z#<&eJ4ZMC|J6=jyN(g|ikLX&)e|-2^()Y>3Wu6krLN^N#!juSQWFouGo%j9M3iiwB z4_WPD!=v<9jDTsGOX6ea#fxq*PhsZUR@epjZk{-HY|IqF2fw9+1w>z;$*Aj^Ut2>y zxGJxA{^KCgHwp!zp@LO{d*K&iM1oG_1c~W=;EVFNzqc$KLh4=_N)3e$7>UEDhl4>A z@pL=Wnv04FJ`{T;1`=GM@rY5x1(cUKvjNxf?t`kLszqjg?DrS%S;%yLADJ9HM#HDwbzG4I3k-8!VO6m z&_WMK-;MbvaI&Sdsq$6X%i${a_ch9|x>#esWeK7(pdjpnXeNCka)PbE`HN4vXl$&1 z#KYjPzJtU=q+0SuSncqc#y>y!*8C@N!oD!_g%d-Ygsr0NsMwf|Y^+P(_4XaJRQ;YV z{xF2m#hETyKf!AJj)yD>8yjT9{|EjB9>Di-E=JKJJ9tn1e;`jJQOH}6yR>EPnboU4 zAMK_BPyF8SYr`a><)RZ2`=TarKO+jHx2t2z{(JjFgMFYxv&Eca-wv65CUKde%Q-tJ zO_0EukG>V?6E-9AZpeP-F$#|4BoI+Qn4a}c|JL^ToH$nh53!j;3qKZqFm!o1AwtXj z0T-pR)}Jn4{#y6>UU&G2$+Q&M;aNg_z&tL{b5Hs^&_k{_aG0-}+ZPrTN)O&m%_1(t z{e{S}ejhZn6xN7>C*qepvciR4Hw7$g54oU=nl| z=kWg!yq)`$ae*`nJ_i;@fCA48`$P4-1OB&(?=U{FHMT-!clYf^PV=0BUVu+KBzh9P zCvr&`IwU(tK-SndNdBs!y@`IOsy)?z$)I$0!t$`u)U%8w!B>M8(>k%y@aw3Rl$F5` zMUR9>81dv+==1K2)+Y6pfobj9=C|Kh>jwzQf(0>g(NSSq{w78~X{qbr@aLw=iq7(y zs=*FF?M&BXLzP_^(}+Y&K#1oFg z=Gh*~Zq;TZoVI46_=epi=?J3j1t<@h+OXRG}RwwjpY zCG={aU4a;3fiRZ76ulX|4*QiFBm_gfctLc6g+@?+$9m9eQ~f4M>!GN`7^dj=QDYKT zgzTW%ug1TFiLoXSqbF@4vGJpb8k&Y*8Fre@BFG%u!B+g!;MrmE z+%GA}I*jcnr_B^OG^GbGFZcY-VI)zLu7tMJ9H@nEA2$uWF$`-w_IjkOs6J5L zjN0Z?N#@}KanF4_x!)o)gr_O*LAsTPxKD2n!SSuWyId+=vxaHC?imkWReQ%Fq1s#l zzHzsbsNr+`0i?(ItLuXMj6STx`fhINgU{VE7CeMPps;bJ=&fXCFf*DO@g|_n-EQ=A zzNNJC?+KhV&{L$3(A1h6U;*&|Ol1SxdZYCJl9>@BiT^|t2T+hp!6DG(8M1EaYy3F* zrQ!psce?Wt`3Fig?ln@0Kf+9o62xo>`Urn+D7D4|!u=_fda5C~tOT`PGv^x88L#EgX^gBVieI3N=U zhYF$F+QZFz-k{!`ZTeByfaUpK$KOKaqFZUJL)XS*LsyUvTb65y;7y-qb}aJ(rri8q ztu=0fxrh_6@61A(ST+tg;IECC9;IMTMPCNG?75yqxWhcPYt4sc<=4J3m1#&MMOh&%jnU0`f}3JnsLe55geChNA%AlYDaXt0<`MD>;fwIM ztn-+9V1u3Kd5z#W&P%MHAG{5%IW2wfomVSBJLg7ZU4u81Np>#V0d@K!Vj9y z`MnN1o^Xoz4gD2m_8GxIZE1!^*UJ@w95_GrUV~yo^pD{DTpp**=pw+?b5GZ`?yz zj5$vmZ;Qg%DgO|B-1E$DtzGaDN?Sl0d5O!W`>Ma?PNGf>CNNtGovwqx3)f0y2I7UO zq32BPf*L`4o#r|$7@LIo4Ly^*fgKu^7S|wHOX{-WbibJnLF)+9aY3HR)~~ivh-?>>$slKx~Y2)Pk?BV=m8WmK+pKzmJh-SX64 zW~10YI=L98UvN+bYk}WN+;vFedJi=r-ywg39y<4cpIyDMzmbt1w$UY9_5DuQR0&!| zHM1R$VG%@M#%iH1d}7E7`Zr7=uwNgg3o!m*Mw-W2FS~AGm(qS?Zwc-RcuQ(S&4TTL zU51Ci49;Z7RM6rci#U&>!xmbWEAL6NduI*gDi;`z*m6Dp5ds*o!b9QqkQIzQc$u@q zxK}68-_m>ZyUhJAGJe3nCAf{djycn}3%3(_5565X&s_ujXzK(vxXa)Gk_1Vjrf@aPi^eoB~{8KatgM+c=5JSG@KUX*65^5IW zzLRJ&jXWDFP+T3ErEb!iEgO*q{(<~<(SBhS>m?};0xfDCNxgO0JrtIK2)%ofZI!NTWWCC&o~NFqo~dtlc9VY!ZWH$N&#4v5Vk5JJoE`CBOuAA1Sj$!P4|b}hXlIIjtF=e91Gjyd}g8Qi?pBh zL%L&Hj?T|YL!I;A#r>DdV@>y+h|IGc)!rGoDPJ&} zo`NG}!|;~#BRhufsPT3-tP6etmf@z_lZ-ob1j7@!_Sy3&)oQr~06J zZJnCd;TGAcfkw%>p}95@shIO?__XM~QHqde);wx4VF*sMX;deMdPg!XkKEhfzrgX( zCC6>^ZUe(O$+Xw_r*V~41{L}I%Bl-yv*P{g&^v${ZS`=V+%s@`KsR*EvIzf%T@%iZ zogKR%d>6+-t0K-t-U5zjH!J*=B=k}?OD3|QrQV2z$-*AEMavvqsy zr`^B6p29x5ZvXGO2?mbowfR3wn*Ee}BW^P-nYo5B!_N=9#j#4eNj_(wSmGFv521}6 z2t2(f{uxq9T zrNxefsXL7efoJYQ*nggSz}GTjSZw%cer#Xk91lgH1IgWfGyLh)6x=y5M*Hg^t@n@K zlLIq{0Yic3GUX@klqh#XV|-_rko|@73B_^$XZzWpRG(1C8#db_T>{u&&^|EPy2Z#a z)SI_Elx_;#g)$S&lmNdCzBh3joJ#G_gXg|~_vP z+__QexV<4e>F;nipD}W86?HgLmT-{VuZ0!nVa9O*|HfXJ02(!~0x6 zxt~G~*E?IDQEpIK-h%(RSAa@0K^LWqv>IWdq%ppme46poFsD)55kszMW5nQHaZ}^D zwkuMGc?XPxAMxGFx-2S*{g_~h{J@?@rXUWw&wKjeKCnBEc=Kh$SaU6)aa{usneS^* zn)~J=SQGgIZ54GZaXscV@;5lk{m!&g_Mf<64x)a^{XkREM|4~psJ zo~0~AZuFdoFsRe0B+oKig>j*2zO53R3x-&pY8)z!QRBH!n(4oVmPa0sJ&BNd4uY?Y z_QBU}`|9+Kn7(jBBdn6Zr2fF*@PUY!xW~~8cr4%B$a2pK*je;4Y&m?KBhg%LylUY) zS{+(*k-AFxLa%a%kXz|T=mttH?j{0oM}QTkBk~#IXWtGsSbNUu3*cW!&;1TDE^vPi z`zaSmW1$a0H{t2n62elomWW7EFQbM9F|?EDFVHB&7~ECTbvzaR z9$05iHn}Yv`;TTHb*5sLdcYwi>h}PZ0^} z&tgvF16KGB<32->ZJKeSVMP07`0v46vfKJ_#4_5??8EGpfs=eH5tnU2+6{v> z;%8q6J{_)~Fz~x=4fd#CDXW>=&&Lalf)%_`fdKId@(XGj`Fuc2;8~i4G!FF&JYq`H zZ_(r{3Z(tgrW8<@vw-e=IF=u+8p{UPYvKke?-(DlEAycobUS>5n^LK^=9W3|tZ=x&%BxtrY2 zIL|K$tqpxCC}z88hcU~o`{mm@M_Teal4Q><73i7%3GB~7Vmizhhk0cS9eUiF`}tGV zzqMDpG^+Kk>o_vyU)puXZT2GeeCBk&Ho{`UGhe?TnegB6w9tz|H+}k%NzNHYgP*#3YJECpD@NO@;l-F9{5^_>zBp(_ zPzF8GM@|||n;HDC$QG^%p)k(iXFzL#cEg3?hTfxXT=8F$3S%Vtng4nAT^1$aD3#@- zMppq(6y~;T-xf8j=#nW0%v)VIpqU6QKHI-7D3bG(CG(r&n@1ndiwIvH@wZSF5Qagz z2=+I|10&-8MdC}XC4H|A<4|w?nwiTPS>Ckk^F@)`p-G>!1ARJt|W*QO*2HCUI;i6_rd2Pmh9p3sPGp04&fD$OiO|!zavbdH*{J< z%;R)z3bdX#^b_-umc_h8okNQA+Z2!C^YFHH5c~^IM$dW zm&-N1Tl&{owVEGY;;sNgBFIVF#JtDaM0-GmaS-7pai4;o_?QO6G&;Gr=62sk{RG9` z&PY*;s?BxuH#Sy#_d|Vvdl7WPJz6g*!sAfCL({F?Wk_|jTi0l>cDA*ymS;Or-T`ze z>l9-LZ6dopL>D(cERLKqoUPi_Ga!ZQ%Jt9G4V$V_+ zQA54U02j>`U3)L8-=}|uVYRrjd7rq@JRO-z<#C_^de$~hV(`b9UE#N=lK?B#ce~QX zbCgg$OgCHUlx$bkIQF9+km$tuo>jnP&=OBJxr{bMqsF~QXcpT`;S-5Hq;AYk zh#bH|%ph!~9iukl?tu69n=Mqwi~*22RG#yjA&Jt2fg;RD)()XoFqIc8#D;~3#xrlj z?S_o@{033$4Jktx*}G7`PTy{81IuuMz9t+AQ47d~y!Coa?x%ssR8Mbrr~Oa+GRIr9 zTG`f~+xSTY>@z_%)YC#&&lV3|)V-lOy|rA)8jc~Z5Tu4-g6|4W z^S^RRC=tk8*7KsIN?bXh;kdNl_}tO&2pBvE!l2g^zxXO~hY%v*Uywp{mfuR&WZD)? zH!#vwV(&8F&`*)6+tf`tVxC3rIm$^2%MR)1S8z~tj87XZ#d<*Wv^2ZGRvO+R?HwGv z;7$cRfM;WFlOm}Rr9{3qlBC>HR* zeap3Upu>){=`1u$r+J(u&01{xOj=uHUg&MjkW;hbQ6Bz=>Lvo?vo}2IkzWeDh{IK{b)615rS53*Xh4C4)1fHq) zXN1kzG=5|1>{Nc5Bk5#h2RD^+09y^w4X(0nwCpxV>TipqYNi)O6}463S_R@IU0F@o zk|m#8a@XePHoP!00rk#s1*f^BX>0c!ZLE=C)?4F-ZRm9zT{LIRjL6Ku-`EUKOE9Nv zC&W)Or*8>+@9XK6=EG#?3qZPgwi8}`AJ?ZOGVE^@jh+SU>3XzrH0nK zitK5C2EVcpslbd`U?~?DhzJ_YNDW?0hm#G2uBE<~3_{wYv=(-~;e)^S6=r;BMn^xcTbhMw=oo-V#O>2c|snh#$;qrM+6I9*`*QC@htdUa=#`jTNy?+zo`;TWBO zx=ozU1h6mCM+?WEPwWOX|BkAir01h z+f%G?sqd;z$l#KdvW+^U>mZKD^q_9S0FaX~^GQjRda@5`yiYuVO8AFprU2M7;ic%O z<7O`~t@qwW+puVPcJ`gskS!B;oZDWqHFn#;_SS7*H||^Qu{?eL_>9WnEu2SuV~8pw zP;k*di|$GDqHUpGAgB2tyceLEh!Xei-m*4+Wm5H%X8*Q{4a5p@(So0_AHfCAvMtT0 zyQm$SMtO@?!qgucSc%bbX~8b~D7?bDLOD(f*6eZQKwOxqKF0~+9@EY1+Dn?gs@}UJ z;nxA){k=A{d#lGxEY9mM@D|%n>5B$-gGG4+6zWZdXw_?LTMJr0e|-J%;j@QrZpN6mMT?r6T5n1Z>Kpn_>q6ucdj65m?z`dy0FFVgVQ%}skBv(z z7jUsUj{x6H9+4kHe@>$K31~e8m3yparTV8U6Z;4GUkZ)BnOuMr5C3vj56MP`008h5 zuy(}TQLd_LK32A~G_zq(_itxucG=B+BeIR+KcufT_YJ{@dW~KYrIecPBMHGT#$TDb zC`}&j<)2JWV`xH7#Mi{8g}&f>b6;>wtOt}1bf1IXSzD3V4Cs%tJ z8`f>rHCV@bB>A5R-x^^HIu|64${l-S!tDt|iQ9PZh^fS38l55VW%(?kz7A;Qyd-^x z{&HmLYQ=9GCe{5Z7MQNsOSOF+yymzTRMUp~tfmbu@%7g#VYQE&%BnZ#C%p4@j&?ZH8K9F#;Okf5LJg z8o1JPjrTaekP!BizLl%io93L4*uYE+WlX*MCuq*y3|dmpSY-x3B|PjReG)bvML^e~ z>|l@^G<VC3n%9RP;BOT^+WCE1gP?wKQOY+)F`^1s!7spl zzgu#*B|;TvUkADC^NIb8uMAkn3?gpz=!Z?i)iI3h`3#%yKA!=?WuMJNA(2Vw$2>t? z8a~%sApM{iYqHqao7*)vWw(0%>wY77t$buSV7~;cL`R`lgI)}w0TSd~{C%G%SOUb? z%9d_!-QPN;>uV2B{Jd3PPi@YZX6TnGPqxQ5D>~;XmMN}v?`aiRjufq_Aa-1n*U7)B z7P&B_HJpdET<>U1GvN?i1!pG^roWp#XEI@Idtz5| zYr?*uSNJbORefCjCc}PfGZ2Qsp??Ba4G;!8htwmnfZZSiROd0#D;1YOtn$M!8=2$i zY=3o7Y4{oemD)$lr~box6$wb7M7;I;2Kxeef=#4`(doWm!a+Z0U|Vv|_`&qn1X6rp zqUYG-iT;7#h)uYEnGwNn1!WX(d@9jGo5J`+dgwU`k~=!!T3}8VPx1E;a>|S(nTZUi3p#p0k(o83Ao{uHQ7041Wy$8Rh|A1eSrL zM}8VM*Ku<{JnhQs?zGwK&7ZnHHtcP$lu$b>s%uLF8w@g)k=kDa(BrlIe0j6!4F3QA*#1qzHiW#2a;gNb?Fm;%m!{}jMA;!8prHkswwJ+NqO3F1i z%@?h8x`6H#t*@GowT6fe_Hb15O&I_>DL-gij4R@JV2>apOda+k%``7AB+?yBkgq5e>(`H3k$ zQk&K2Up>79R0eJ+>s&6+G(@}mh?@ljPOoRwXe;6#1;^P(BVe1r*9SIhS9ScX-`aSs zP1*9XV#AmBuan=LEiiV)^c~iplG1y|DHZy)M!S_c#E0xfoJVZ;FnFCMMgATaNBd;b zW-pm`G|Y;(;$H9>iIqtkBC`Xp24NyE#w10g3#$WmaOVUF$Z|lNYDMeRma;CCWK(Bk zCqTtDmg~B^k9FMZ`Jz42w^m=EysCJqTWdc$atumFeZyz_#gjl7Cu$ew7JjyXa&E-c zlC7wNgI{cot}~-CwtsrAbiV7G4?G8)WA#>16-%|N3==flB%8Z=n%mCn2pB$qdXV=r zLKG_t-Q`E{81+;!VuD41T6QyYD(4yR66XT-9dW9U3&%q4AGrevBsWJLjXg}Y3`~?Q zQG6d*>V1n%=S%#{s6~DX;xgENtI^~KIEcCG8S2W?uhC{%nS=L?peD(Ou$QNQ?o|2> z`I@J<-1@b-?0wyhy6Y9Ns+*z)^F>fHLIb<&k%x{&q`D8gDnaj2-@M3}=~y^kkB`JI zg(g`abViqF7tg9b+HMkq#oHwP>L<$Xw%xVYnyyMtXrhm%`HQ%}e(CM{=aWUdz+NR0yX`pF&(?8BmTX<#(Z{eAV&ton}l9EbOkHxcsGTB;& zfQI!2dc#nBcr0WNAl^0I+$Uw!cz=HIYF*CZZ`s9%3+swEHxR_jM9=GdI^|Bi|e+yy*udpw8M-GTO$JdBkFZE6s>49NT^F4RLFS-ds z256I4G$PwYGbgF%NY=|1>LK=B?i>WtyB?i4Bv;&OPHFV&_Ox$7Ex@Uva|SNhbk4dF z0q{Jy4t@ps-QyuN2V4Z-h$-+f_^k1K0k{e^)44Idv01`2RwL(gV4wg(@x$IAu4C_D zukpEqs7GA)enooX6YBjLFJgj%zw_dJt|Q3E8@Q9c_x<41gA4~p&ws{CW_~2kVqK2B z7voR8Z(rJtP}aED_|D_a;~l2H#N;B^VmA1?821Cl3BCxL0?smKP{33zvyj82wgOt^ zV$lZmR>%ASb6=j4BWEc)mEH1>(g(5`Dzj#mva0P?f$qtat4UV|?+!k>{@C_-(rdyy z#mj#l1w1_T81$0xZp|m+SKW{86}LL+rdzI+{S)--bpn7iU|MF=OiiXdh~V=dW)}H0 z^H2IOzauoK|8ACxC_o}t97^p$a!?$~zdU8Y z6WR>Q5f+fo;$kQb$ku^=xw`pjn^uvk->cl$#cQ9?Uf9lW`chc^?#tJ$-J8MVy(d66 z_C@xr>Oi!uXg$=ylmPl}f?OC3BE+#p?`_;{-)6het5+}5h&7TE?GFHh>bW+xj`4(@J;R7_JleAb*@UsQJh_Hsv@`Hzv{mH8LyT+KbCv>7pG0%)7-wj zX0)2v-XYT|gO%&mM|xW2=6Uvb6%*!>3w@(WvwZeqzagn8E&djR&lPh-Og4QJwTr~XbR(Fk zR@8h4$b%lXe==E{Du3zZ*NKjq(B z^yq2chjM7e!yg6t^M8G*Ia4dGJY6kooiAUgXZ4*iy?1OHrFleS2|iHrChFhx7Wx{d znE!r^-}G>t#?LP*fxLgKg7v?f1>rHzucz*!V#mbU%$Aw!?_4`gLXXVRMfuk23FY1x4IjA6rdqxZ1Hr@>nM6k#&}IC2J7F zXu>DniHJRsyfDAe$w6XTf+yUg)FQePj z#R<1|RN4#ZjBEWWS|x50{SZ|u6oV-I-~Ql$ZpH*sE9y1O8+(FI3|Q`eoRQ4(VgF<- zAo4saof9-rF;PJPUEt*Kp&l2cxs{HRhoxO*O|`2ePX-D-20UI3Z8PL_{`HIf-u3Bf z!;wDzNC@b^k!8-^W`Q!XvGWJ<)7~Fd&A}#;yWeu5E4SfvTS<>n3~$M-Z*C6i{-Sx% zFGVZ)?PKt59n%13kdaL@B>%pHUjSlOVz+t2;Qt>yT} zpAv>HTr*WhmEfeyWakuYZM3n^a?zqSC7XwA!LD3jyyq>-kq|=S&BRS%b1Bz7B*^L1 z$iSb0uLJS}biq;4f+%5N1pS29#9@;`qoisI4O#j=DY9j6Wl`~@pQ7*C<=48G_U+K) zdkh`-Br6OvZ6e3N{pHRUHxBw8o5(sCy?ugd=JpAj0z18Kpgz#2hJ6fg4Lg>6d>VgJ zVAvIWJ>(ZO6=U-~N6jQeK)c*Gfh%FrkS(JQz;onGpA6q=gwL3BUSa4iNU?2>Vy!&a ztQbtRg{ak%4k=!KTb8D*>HF?df$zdnV2OyI=xK!0_$6@Na5>Q6m5SFO2Y`I=BGfmG z5Q%Y*F*CIH70aZ%MUSd$KL7QM_&T&ar{{1_em%b2qng~bs##jUr0&m_7HO(6$hp=H z9^E-C?Ej%nZ)z;S7gx3{Rc=$Sl|NFf==B=<0aqj1Km;HRHqT=xR6N=UypDcI8wlc# z88fbG(*B8-vGc=ju%2)-V)u{hP4kU=5Q&M+j2j7wp?M)U4RNja4YSo;xj^!7S6@f7 zgkgC$1hJQ@666PZy_~lPXIM|_zw7tee}U|%ZScz@GeI<5K6MS*fi1+Cd=~m9qfZXZ zR6@ErioN#vh&V4l;6^i9U)~Gqd#|aI&sX&6=Gdx7A7a`5fx%8;ieEYm5B`k55kL@} z_D3;R`cL5S>8o*LJ(!S*!;=g`QJtt+|EGg(>XNIvH%kvFb}Ph+^E#*Tj&ZhezIBH~ zXFuW)y4w+SOq7SSA0(61Kd{Q~WBhv>|mT5*UfNzIhp=rD8$dlD7px&s4k zUJ~xf-+WKyT?MsS!v z$S;@NP3iJgqD3Pb^F4LGbg6W!^0|7tB2fIQWp(X@V#m9EclTcY=LYjRBhQ{Ee+_=D zdi3kX&)1dDSN?bU!MG=iSI`f=gy@rG%o586d z?C@fzd87v$Arz&Io4`#Ygtsz1S+~PkV}nz2qGp6NhJB3M7$FS&z&=dffW7Rhmu_u6 zs+kTA^3n{~YRcqH1I<#<_d-2G?QPixZX|@Tq6EuB01@4hAHz>^r+Hq{zvv9kaD0aXd&aKLLM?R6`!xO8l*Cz+7ey|+wdC6Z z^n#s>GL|&V2adlN*iDhL0z+qqm+^WTcUe^aYr%i)g_J7NGV)Uj$nQG-J0i*{mtrfR z1Db`ckx8gU|+|ShKuou3CJrYJQPW+>EU!k#jQ~Q~Yt*tGhU)p?E z9qfT;CZ6s))f*44AGw4u;6S*qups9e!%wMq*TSx8J@;jqhRd!mfb4#X_J-18xM2S| z@Ys=MeQL!HvAlNk?J56E%uZK@r_-~2zi~U`52x&jO$+@aBs6rC-|1KGVQ?1fpGg;Y z-|AsVZ%B_wvn7Wl2b59Tlk#EF!p>d=vbS6RL-k&9R+cW^VYmir$N!Bp!HNNgphr*~ zczgdAO+()f#Gigz@4rSKIJOV%2KkMCusl_LRQ8y*0G4CVP!|Ur=5x83R15Mvz|Uh0 z^*YbMy~AD{kpKIPZ{lqA+m1F35e+Z3_5CNoSWt|UZ0Rzd=sT;g)++j@4)4IAN%Ilk zERXdG{SwG5&oAiP=-C)DCdOmx&aCj6@KxGs&zVki?#R&E=V0a^> z*1kawYFbeHZx`31aV%9eHQDROiEc{A%kC=g*p48VP!7`L$YXua`$tE1r~H>LNqd!H zoy3}XVyZT7BYm=u4};FzOPlWTacGUp>~;_NS^rZjx(~L>%ez1HJYd{Bd>``a*}L*L z*PjOO?|V9UW6!qq}~{^zyCJP>Ec6L-|#o z<=794r%VcZ8*w{$m|pGqH*5*shg%jhGbmkfIdDP9qo9`o<$m8#r<|>Zqt*(@1kZ)w zFsDy{*x)y3uhnT}*q=czQwt)#ByNpw312Tb&UDhtc(^cG$YtI(HZTCfo$Sw{gcJ56 z?+je*epT_XW{G}2Bp9gBE$n>IgZj<*5wq2i1REghIWATQIo#Km`C4G+{pp=#ovb}F z>?VaW{Ro?&8Nl(Nr-MJ_n=0DBPcMxVZ7?2j3QP;S(QO0bvnqt@r{s)GV7dUJ;95yA zpQ&gr{D4Oou87#-Q{p?Fd7l%=8b{8cIo zQ-3b4nAWwik7C}U&XPUU66_I!*PLLN2e8#+8+Il^K{`YI&g8NuaBgyE2R@I8N@P#y zoNZs+mA!V+hq>BWqIs%C>LsqZXT~-MJqp?%AD;3dW^7PR;MvGCac`nBf^Kjt1J(u5 zSf5CF9+L+QvarTwtyB84MxOPz$wbXPjeDA(H{Pj-wbrQOhNOr_gcJw>1VS9ZBhEVx z-e@^;1ic6K2vG%(M(jXLfj5B4Tt((K6{gc&G3))9TZDfuJbYN#*l@8lD!1e9!f#1s zA*BlogvEXh$r7h3S2<3lG!YTLem(FVS9%>K^;)~fT9Q8Z0+>Xjq7byz@(|Y zUtJ?kxBisUVLAe@rWFa^2@eMRWCRhnBG-8ABCX}U3pyZZbrpV3^;|y;t81iWi*C23ZWfYVggDcq#rD2-NgfNeXdL?T_)WpCUt(70__k? zEGNc)DfG0C(n~-<8Hbo(e5PTd@!$MH{APKk^y_5Swq0%4I-f`$>udUFJEp0n?cC$YZ_#dUL zqhW;m;@77(rVJ+BOuCv8I2k)}c`TL6Ma@CWaOtQCF0(P+jCU-uEj0`%e#t(`Kgrb+ zVdvb&GbPVI?S6eShx%=QG3MuRZse=iFDu`@DNr>wcZ_RXTY07GdcD2gqi%A;!}cFN z2$@A%t~_Rmg6*T)g4abI4|Eg007D_e#90Bs96d!xu3^|X-&vkS3oLnnZW!pDHgXbG z0Mj`?Sid?v2cXuey%UU$Ll<$W!johC5?h0~#5VLynl55?JS%J+&mg!G{vcwOAeFMm zlRAV^Q=9v1Z}q%#{R_YB@=}qz&nVyZx(x)aXK(G09kY_6_5I`-3Em8^B*%oMCyz-A z5Awku1Q|zwL;v(0k$T8ynk$FT12Pe-s9yumF%N<#n}67~&?DXl2u-9-?7Z-j$WnR+ zAj#1Iy+vteOu=7)-+|vm7orM=H)@x39qu|SyCaViyIOrj>(z@LM~CLya-~;mdVVm! zwB*+n*~|7NvNJozb&t6h zvWkC-UoE&4@R)Q9kf~nPK0%_eME0-KdUSeq*u;vS1L9xZ65WfDLhMUoI?nAm>}AD; zdfowi2E6i2^VyFFdCf%UVKulZm{Qp7p^xSW?IJ~Lby9*uwybHu5 z4q`m z-r!jyd=#60Hqa6FB8(HVQE;0DBR=#j^4h?l$DW$lH1&8&n7JtJ16aaqr-&ByC?)T@XLil*GI1*hc* zg5L?J)3T5t@F}m0^fv!n)N-OfsfqlOv<=$|7r7;Ut~PO{ttCvi$@oLgY+Kh%Z0Cy3 zb$sevt-e1Lj-8MFHJGWM)k_1T30R+tm>*sed^%~sdou7J-P@K+)kkWlHXN@`FMd<9 zt$mjY(!HsC)3;?spX(pCSGFvwTU>jzB~y~5%e7qr=aRvpy>Z84wua9P|1Ttf^MVHC z#E0aCZ3w)?*~{9?Lb5^`vD96pz1}@YJ9v{TTmP|9`x*Y+|5;KF;M+fyB~7jMkrfSP zHPy!Yk%lSFS40maTV-`}k)ajvfr1S~$0$O*1Vq90(9p;c?qsjx(VMUAHNAysO*BPRce3^WbMDQuaeZ!GS7{)f9F*Gi5>(sf6W-ed3=w#OB z%+R=bp#>2W5*UfAqHl-n3cSnz!Ma7NK#@U0&=%AUfNVHTdRz4DIWGU7a!_%FU zOzp<)&duOSa4KXUkOYp0Z$=iPPY_>n%`smRQ)0G;jE0>{N*GT~nHTti^G5_fO_Y`t zdouKK7|w@l1-*Xrcd%7QDc`-{z5rhmDsz(or+6l z`m;M|A>=L+0(T9b3EhOT_-cq-(RNrBoang@-HnI_sQaO&PVEWhb6HYPRr}Z)P5!i( z%IDAXhiV2I5LJ^)rk561jITOVX{v~<3aDMtINZt*^>kIqCt6hC9IQ8X4BL9qm#yR58j;V`2%A4wo@oflr7ZS~#{m!G(+48sDFBBksuU@Q`YvT3Z7RB&E zuM_^Ls9A|)6UIl7Sr5IrI5Q(kDBuq;5746M?`V6;TkvO5J4XK2#Wx9S!({aS<&F<3 zN7n<%3eA9qBlnUSdLtoA$x`1Jqdzq_d&j|d`7y}DUTDu5_+6CsenXzK2GH_7&8zFq z*R801R57&@*KtQu>)X(U538 z56kl6Ux{dn=;MwM1&DV80_C+PX44w&J-}uV*!+i-A&WCSv#xb)9zKD7$NDc2$31g3B-&c#78(4+!co_61=q;W6$2;Q~2_`Zu`-XT@Cg;re@o6(&5$Y|7FuB+c2J z{2}CH)a8snCS|3~iQ6Adi75^x)4qaCy?0fw^r2Rgb)mjk{oN*m>tMSzo)sMxSXqEM zRDHB5w(4`ER9qpUcV;xp8&aBPHJ@vYseM?fuXs~+qGogLyV{KUdrhW}m%13>c62s; z<;WhuYUE*T3wl2s3bn)Op5xH~)EC4w6pWb3&lxi?S2a5+R*HOFLx&p547wmL-o;`CHOlrFO8UQPM1zY9H%&Y8}clg;V{^ zybqW}k_XLC$WHzxr*elvD}QLn27Dn$^N#rS4Au8ea?8o-+{eGY#(?)6GMo|Q_YZPYf34zX zOH%cv@`N(UuY*N^vVztI+4QcWs^XHB^{}2>iY<~B(b>-BvLOAiWyIM8vUpG9%Hkhp z)Mxmo8Pj>=0m;|-Zb}3DPDDz~&Y9HEy4L9-x2k5yriu`4U%JL=iw9WHeds7}BEd>pL+z)-84TJq z0tvij;N6f7dJeW`WY^%s;f)|WI1gdX8?k2eoRdDHgKtOXLmrF-k0gS&LN7zE0>^`DP%hfGNK|@sX4=^5#EP`6X|c0P zGVKu;Lp~-#&Ve+x?mHzd<8+;;gXAutujiw)cbvm8Q(WUR=_hk2o zdJd}ow#On`Ng0e}<|HP96-L)$R=bt1bI38iO9-P7zH88R7q|gx1r!c6o1zUrH5kP| zJw!3E>%Qn{dsXw~22^uJS7z7C%CTP`{$Mn8cmCP>S7mU?>$1Meyo!URr^`JWZgiA& z7PdFG6Qx`GxP!k&^Pvv!d)!G09vQb{xr~1NJm$D?NkliVoY~5TaoObkBdgUS$!*;U z$G4?C@fY}q`_o7UB-vvWh4;$C9`ad3J;Z4e)&+jzHTk>z`viNVyVHePduP8& zH-|Zra_pYBSfE zWV~ZKW}a=&1o#uS1hyx~OaPA`Oz0EP=*gV4$kw>)Vb{2!oN3%89AD~7++j~8bn3u> z0@?jYL$?Wkvq&Y|E!i!(FCA3gGM*pVj`QP8=f5IM>2Ef00YGXsr_JvO=9kwwpAqsq z9|`I`V1w%_~d6C7u0P0x^l-Kg_`-Gc} zyF^&$`_ylk9P68p{SMgUh#&k2Tn2nOi0|jR){TA~ojz39&$F%VtCv2jzV&hcv&Sz( z@_;|q72>O>wLfexuf0`vxO`R(wZ6BW-9TtK*YL6BUrDiP2sj^~$9&KAq3=b`1k|7& zGX{Bb);#jxB!%B~Y6yvfgbhNqjp7L9T_*ulGVnu7Q9aft7~kt#)uV>^y&b6;4*$Y^{qGCW9{%Q&AJ9&DYvQ9L<3O<9m*||V|@ok zXT!DtMXqH|#-MuOoZVv2bJp6m3S7(K<~v=mp4pXJTw#t@P@1#sWxnF-CnW2YEkpe z4q}IQTXb7T#V>lbKi13(1NA(P&2cNrFf;Ir~u14D> zli2V`Qz%DCHjB@RsBN6)>Uy6_#*f(_sGq<7LRF=eH~va3ds+3SDyyuvaO#i6-;)Z` z%Zh6I8fMfd)lF-L^eol+TNXQjLvP?Qe!Kt*ZN1lIG>=@*9QK=rS&kSBT{0rHOi{?X zuc(MaAly!(27Yr8HW&=N0#_lY;a0J(hmDET$Nn32ChU6P%z!jj0^2Eg8}5!fmO3zH zQ&!M8X%LK6&yR^3AEOT~5Uv$!guB@1uwl+C>eGrX`h!Nfc0e)PNC4=-(EdP8vJ#_@ z{hj7Vl#SAMh0wTt7=h{`^McxvLdVZY-yZ&jI+L8we;qwGERP8!@1`-?e=*h&P$(8u z;+kau$@l-prB0iuJSca`CGuyAo7x!LYY>5OjRoWO`-OmV&E&yh?;NI^0>=J@nuM8w z+mAsa+accW;oj7ycX_xMkKYdGANz4L_solukGPt}7nH)=ZnDg6L5k#7*_ znfGxM;0_Q6JQcKi$g9uN@%Wea_+n)*r%tw!zCEumSOl z79{NB?xatkj8dOblYQEtU~8-7WcxB@!0;Qer=wDVl)1D=`f9a5JzY7}g|CLnly%(A8^zJDsKT!m%e%gKC9N~!5w6|Ys z-zHz9ZBepi>s5!1du`io*Zci|(>?Rhn}@IT{8wGuj&ZN2Q5ic>(}6X}3%)#Z3T7oT z-#e6%D%>3YSHve_i@%lKCs-Y*!f2W}=092LRt$mKpZ< zPex)$E6C-{TcOt@uX4s<|3#d|LMiu1_eOw9w<2^T(>If%_Mi{^Ym%GJ>MJ^Rm4BA( zuIXv7=-ko#rMjegTwK+diTysYK$+G8PGd?WIx0Nibs4D}M(<=9sPc35Q@5+OJ z-tcX6v8i%)-Hp2Vy1$wX-7S4zhPNTSylvPMuor*_2p+-U!$S`N@Q$n6Gvaw2QA(Km z4WVY3 zIAKYs7yX0(zVK6V8Bu?Sh=YM)cY`YEV2?rDbDc|;Os`2x(ib@(sD4If;5S|nF&MCGFxlfM^)urzp#;@| zUW8wXUxzM+PlZMTlFb9nPre*^bLk_vu%PJY=hZoXejR9_bcZ(Qm(-RLT2}XrcI7m$ zY)lv3S8XxQGA}fqHa@rK05Y)g{?8&KQ)Z{`P3ju6BY9Rv+*oMnHUGo>S<%;Gz6zf+ z3K>prx*(3l#BV{YgI<6cpdO>MOglw!C0o8UynQf3JdC%iI%rbsQy&;rlzoIq+xN5Pc66kQHOWeFXUH~tR>(@ZZK%y@sr^;yYU_wpCq}AqSs0 zcPp>8mUo;{F4oOg$7+bS9LRMHAA><;jO;T%SIJZ>dJmcKj%;w7mlB-|D+7OoP4!p{ z9}n&V|KUaU-xHA?GM)@UnK7BvC#-99hpz><+hfQD>C>q`>0NF*elGDi{N*6r^)EC4 zcZ!w}SQh)wn7QE<{x3;a@YOyjY6hFge<*m&8==MH3w-W!J_Pbuf!M5`{u=NM_0KP(%pBjY*n0Arl*)8?m(=QkRi%eMuD=hxcIt_wY(vLj&9g71xhwPK z1*h|I?}Ky9pHU^9m21k~zgjEWI#8ykU=RFh-<#;V!4liy(Fv%7@S_7MYE#GJ#(!F` z>T7{&$PUK`U3{Nlpk#1P|Jgw+DucH$VSJJ(A}g>Y=uPMe;eT`$X%`(IqD)*iZuz9y zQeo`%?jJKV2Wu@xn+OUTnG2 zy}RR(VurEIa9cLgJzIUqc6Dg~$QQ^dVpMqA_@z_FXCT6r)J-HWe~%!DU_E04Rm?2& zUqfZ#-XcFh`kVzSn#dr7+L{O0W}SAGR;h)nKgfToU-jSg+QOa{^q$w~i-cYs8Uq_8 zJ*0X0G@#6=iQa4R&oO@^{Sp5{Haf+!4`us51m{f2oB4gr*B9@0<%&yb9iLlK<*SS6 z)$=+mVr%>J#`vc5opV*ey?gq^z59&_`|II7@K=N{0Y73+j6Idwn>;^dY({ZfW8^x{ zbxua;yvRF&msth$RR24ib<77O5T?X%7E zr9SFOnmJ0jQquQu=m&f`CL13@oK7awrZD!A+hD!6BX$)0hR^>{aMy2Xux}f{B?akD z5xcwF**e#o>+a6);#qfhr`uf5h0Zy3c6Ya;7zl`T2nhJ@{S)rvxXObH7TJp!IZg=1EexIg+il4PLZ2=wY8lTounlN2e z{o98NB#WU2R;$}yha>1ah9=M#+E4ox)-UPxOn19F+gj31GY9eR!JA#dLqz2|gR64i z_@c3&!W|+nUeO?9c!9JYvVj@x3iq1rHjfP^PzdvFhCO$A!ksJ~{9XULuXC=phFKnE zpeSAzzM7a}_m+a1`>k@pS^bpCd&Jm-8XD*C5Ih_GHEKWEJauvAJirH-1-*lGLMK_+ zLJon^@B^4s!c77a?|>JQS}hsQvcP8veRK2?6?Uts);24>@_d3_Ikpe1UQv(04@`MX znv0^P)v`p9X4tj=#=zR~z3TIDgLS-bg&Q9qU|2F05BQFXA)LUAaXR7-rn_wi3ylL> zxM7@$eQ0;^ni&dcFLZL|nZ&;@qY2P*r6+U9k9)Ddsq;;%RqKuZRg)hT_a_FqErS=h zg@c~`FZvgbbWdbTQnB1 z$8ED~y7OyCv>l#FLX!;wMY{H~sZaYc-NpI4uE zn|I0aPkSHAAMVV&&}!aoRp#;iV}@6bKL2@+J~QoiZb@NXU!z;&g7z)^%iuPK3quX0 ziNDMEK$W@Y)YrkCtycz6`dz3A(01`0-c#8-BhOf=KCE$u+c?O>%A@-HpSoxqRW3$1 zEB9ZXw*y_G|0YGJ$ma|#>`Z-=(&F#tC~;By*#-LexOj5C&-oAgtZ~?Y8`67C4o?_l ziAuca1E;aQu6=m;vU06@E&l>1Y_v&YA@39H8<`pZtVo{*nF7sh5GHG87-W~F4%YUajB%``MJDL5`oL%i7bZJvG8ts@Vxcde!>b)tkG6_B zO$TXMueX8K?s!^1ct5}kA7yvn{szq)mrYbLwlT|yQ7E2;E6l~PW30XAznt#uslxT; zl?BEh@4q>g4D_@Qo@rfLZPT=lV>w0`+}pu#d(m&szaah~o)9I7=PS_K<Mp7SgEZb{V#V)+_!uY%y#QN+;KjbB6YfzLP%$4P!#^FHu$&qv+L?tAszM50VtM z1on)01-IDZ7;Gal8haO=YW~;gry}!Z?Z*o*f8G3{>?gVUQ(t6nZ`JF3P)R^TX7jsR zO67@KSet)$NdI}hbmk)Cp|dS(JE(6e#awE6+3pvEO8P`!XLs866oEWl$fNXL?aP?- zFeYdpjHhv)jCPBz2sv9_rEA4Kx5;r%b;)$X*oeqiiDB&jxsQ52bU`_NazVSv zovQ6F*-Tr(Xd1+new{$x=~6q=kTNZ z&@P%bn(hKOK}^sP_)UZbx(NG#lww8oG@GSQx*aaI4#AzI204worrIuKxY0|f8&Gew zPsSnK&QXzIIX``9=|HWZ*UTU5hL)HggzrRuM6L#T7z8FN{1V=b5>7crUCg@Zw9bzZ zlIMA!VT(&6y=6>LkK(zw2J$fF2I`aU$9NWJ0T(7%Aj0xb4AyjM+J;)gy9{HWQ~~Nq zK8=GLd^)yQa8l49+#>-fKd9_gdi6X*0CX1qpv`vw^RZEh4$+@N3ca)K$}Lyh0X?m} z*SkJFf2xk)jQThR+~+t z6UB9xtI-wU6iw@e#e#psc3NzL`T@VrA24CNOE9(QV>{~THd&8U4Zx(&i{W|-z?5pNGCO@k}TGLZo+VW%c zuEEP<+l*S!#l6Md$jj<|qc;4t>Qlx!(+{i4A&ql$ZEI! zv(}_sNIDxhXqkb#%uMmv>GMCQ^;VbJ9S$;wA(lO<8I=hZD80rOj77;LYM5*nfAdJy zAgKR&@83b)q!uWqpL5A^i?{nj&V#-%t3bYGIohEZXxuKW4`~*87p@7h93}xR)7=vM zYF8D1%8dT{?CZI2h>U~}xv$I8c{xX$GrOe?sPc%K!47EevbN*(@FtI5(; z(~go!$i`hkzl0*ET9rkd$G@MDD zI7IJ>7JLIdF~6az8p_~=3XjOnPiz|+;*^P0pk0i7=Pq}(+dCHro68h0n$dB#-vYm0 zk1y^&z2si=+-#go4zBjqmZ6xz=|O&E7q?#7x=`3_Ow`}yb$65d)5bysPCWau;0cOK zr0rA2iU>l!tkCcW)C+n7CS+fMKB|flSMCA6X5#&*UH80>(DCnpBd8Lvbh;22f;A^FVP?jSrW+QQ$PSDcMxs@? zD<(85p)?Na&7ss_tt_`YZng2J5Q#5o9EK6QZF*>Y9VdjF%|9wUH1fD7t}U(QY0KLt zeiNu)B_^xL0#N^!{>|f`r3dBB(lqIBdA_Q1YW1{orV&z#_FxIUQ(_h;!{a?7oc*Ns z3oKJ@6&|a6tlfV&k)1)#3HG(@wN~SnyNGP?d;MHv(oC&(RNk++tjB@f&||a&n|RxK z>@Mqh&NsZ+K4(2u9tb~7Xhfj5)f^yM-vQl^UxNHPjg(UrFQsrCxqG|_c82v}rHnHjmP3GOn6Hkr%0Df9Cxj?a1lc36y!IicKDRe%%7FUaN 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

w*c~w#=kJ4e>M6E?lt>8 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 literal 0 HcmV?d00001 diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 449a2714e..4076dbc6a 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -124,6 +124,7 @@ const char* aboutLine[]={ "Ultraprogramer", "UserSniper", "Weeppiko", + "Xan", "Yuzu4K", "Zaxolotl", "ZoomTen (Zumi)",