openMSX
ImGuiOpenFile.cc
Go to the documentation of this file.
1#include "ImGuiOpenFile.hh"
2#include "ImGuiManager.hh"
3
4#include "FilePool.hh"
5#include "Reactor.hh"
6#include "ReadDir.hh"
7
8#include "FileOperations.hh"
9#include "one_of.hh"
10
11#include <imgui.h>
12#include <ImGuiFileDialog.h>
13
14namespace openmsx {
15
16void ImGuiOpenFile::save(ImGuiTextBuffer& buf)
17{
18 for (const auto& [key, value] : lastPath) {
19 buf.appendf("%s=%s\n", key.c_str(), value.c_str());
20 }
21 auto bookmarks = ImGuiFileDialog::Instance()->SerializeBookmarks();
22 buf.appendf("bookmarks=%s\n", bookmarks.c_str());
23}
24
25void ImGuiOpenFile::loadLine(std::string_view name, zstring_view value)
26{
27 if (name == "bookmarks") {
28 ImGuiFileDialog::Instance()->DeserializeBookmarks(std::string(value));
29 } else {
30 lastPath[std::string(name)] = value;
31 }
32}
33
34[[nodiscard]] static bool dirOnlyHasReadme(zstring_view directory)
35{
36 ReadDir dir(directory);
37 while (const auto* d = dir.getEntry()) {
38 auto name = std::string_view(d->d_name);
39 if (name == one_of(std::string_view(".."), std::string_view("."))) continue;
40 if (name != "README") return false;
41 }
42 return true;
43}
44
45[[nodiscard]] static std::string makeUniqueName(std::string_view proposedName, std::vector<std::string>& existingNames)
46{
47 std::string name{proposedName};
48 int count = 1;
49 while (contains(existingNames, name)) {
50 name = strCat(proposedName, " (", ++count, ')');
51 }
52 existingNames.push_back(name);
53 return name;
54}
55
56void ImGuiOpenFile::setBookmarks()
57{
58 auto& dialog = *ImGuiFileDialog::Instance();
59
60 const auto& filePool = manager.getReactor().getFilePool();
61 std::vector<std::string> existingNames;
62 for (const auto& dir : filePool.getDirectories()) {
63 using enum FileType;
64 if ((dir.types & (ROM | DISK | TAPE)) == NONE) continue;
65
66 auto path = FileOperations::getNativePath(std::string(dir.path));
67 if (!FileOperations::isDirectory(path)) continue;
68 if (dirOnlyHasReadme(path)) continue;
69 auto name = makeUniqueName(FileOperations::getFilename(dir.path), existingNames);
70 dialog.RemoveBookmark(name);
71 dialog.AddBookmark(name, path);
72 }
73}
74
75std::string ImGuiOpenFile::getStartPath(zstring_view lastLocationHint)
76{
77 if (!lastLocationHint.empty()) {
78 if (auto stat = FileOperations::getStat(lastLocationHint)) {
79 if (FileOperations::isDirectory(*stat)) {
80 return std::string(lastLocationHint);
81 } else if (FileOperations::isRegularFile(*stat)) {
82 return std::string(FileOperations::getDirName(lastLocationHint));
83 }
84 }
85 }
86 auto [it, inserted] = lastPath.try_emplace(lastTitle, ".");
87 return it->second;
88}
89
90void ImGuiOpenFile::selectFile(const std::string& title, std::string filters,
91 const std::function<void(const std::string&)>& callback,
92 zstring_view lastLocationHint,
93 Painter painter_)
94{
95 if (filters.find("{.*}") == std::string::npos) {
96 filters += ",All files (*){.*}";
97 }
98 int extraFlags = ImGuiFileDialogFlags_DisableCreateDirectoryButton;
99 common(title, filters.c_str(), callback, lastLocationHint, painter_, extraFlags);
100}
101
102void ImGuiOpenFile::selectNewFile(const std::string& title, std::string filters,
103 const std::function<void(const std::string&)>& callback,
104 zstring_view lastLocationHint,
105 Painter painter_)
106{
107 if (filters.find("{.*}") == std::string::npos) {
108 filters += ",All files (*){.*}";
109 }
110 int extraFlags =
111 ImGuiFileDialogFlags_DisableCreateDirectoryButton |
112 ImGuiFileDialogFlags_ConfirmOverwrite;
113 common(title, filters.c_str(), callback, lastLocationHint, painter_, extraFlags);
114}
115
116void ImGuiOpenFile::selectDirectory(const std::string& title,
117 const std::function<void(const std::string&)>& callback,
118 zstring_view lastLocationHint,
119 Painter painter_)
120{
121 int extraFlags = 0;
122 common(title, nullptr, callback, lastLocationHint, painter_, extraFlags);
123}
124
125void ImGuiOpenFile::common(const std::string& title, const char* filters,
126 const std::function<void(const std::string&)>& callback,
127 zstring_view lastLocationHint,
128 Painter painter_,
129 int extraFlags)
130{
131 chooseDirectory = filters == nullptr;
132 activePainter = painter_;
133 setBookmarks();
134
135 lastTitle = title;
136
137 auto startPath = getStartPath(lastLocationHint);
138 IGFD::FileDialogConfig config;
139 config.path = startPath;
140 config.flags = extraFlags |
141 ImGuiFileDialogFlags_DontShowHiddenFiles |
142 ImGuiFileDialogFlags_CaseInsensitiveExtention |
143 ImGuiFileDialogFlags_Modal;
144 ImGuiFileDialog::Instance()->OpenDialog(
145 "FileDialog", title, filters, config);
146 openFileCallback = callback;
147}
148
150{
151 // (Modal) file dialog
152 auto* fileDialog = ImGuiFileDialog::Instance();
153 if (fileDialog->Display("FileDialog", ImGuiWindowFlags_NoCollapse, ImVec2(560.0f, 360.0f))) {
154 if (fileDialog->IsOk() && openFileCallback) {
155 lastPath[lastTitle] = fileDialog->GetCurrentPath();
156 std::string filePathName = FileOperations::getConventionalPath( // Windows: replace backslash with slash
157 chooseDirectory ? fileDialog->GetCurrentPath() : fileDialog->GetFilePathName());
158 openFileCallback(filePathName);
159 openFileCallback = {};
160 }
161 fileDialog->Close();
162 }
163}
164
166{
167 auto* fileDialog = ImGuiFileDialog::Instance();
168 return fileDialog->GetCurrentFilter();
169}
170
171} // namespace openmsx
void loadLine(std::string_view name, zstring_view value) override
void selectNewFile(const std::string &title, std::string filters, const std::function< void(const std::string &)> &callback, zstring_view lastLocationHint={}, Painter painter=Painter::MANAGER)
void selectFile(const std::string &title, std::string filters, const std::function< void(const std::string &)> &callback, zstring_view lastLocationHint={}, Painter painter=Painter::MANAGER)
static std::string getLastFilter()
void selectDirectory(const std::string &title, const std::function< void(const std::string &)> &callback, zstring_view lastLocationHint={}, Painter painter=Painter::MANAGER)
void save(ImGuiTextBuffer &buf) override
ImGuiManager & manager
Definition ImGuiPart.hh:30
FilePool & getFilePool()
Definition Reactor.hh:98
Simple wrapper around opendir() / readdir() / closedir() functions.
Definition ReadDir.hh:18
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
constexpr auto empty() const
ALWAYS_INLINE unsigned count(const uint8_t *pIn, const uint8_t *pMatch, const uint8_t *pInLimit)
Definition lz4.cc:146
string_view getDirName(string_view path)
Returns the directory portion of a path.
const std::string & getNativePath(const std::string &path)
Returns the path in native path-delimiter.
bool isRegularFile(const Stat &st)
bool isDirectory(const Stat &st)
string_view getFilename(string_view path)
Returns the file portion of a path name.
std::optional< Stat > getStat(zstring_view filename)
Call stat() and return the stat structure.
const std::string & getConventionalPath(const std::string &path)
Returns the path in conventional path-delimiter.
This file implemented 3 utility functions:
Definition Autofire.cc:11
constexpr bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition stl.hh:32
std::string strCat()
Definition strCat.hh:703