ImGuiFileDialog.cpp 162 KB

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