OXYGENHUB
◆ main openmsx / src / imgui / ImGuiOpenFile.cc
5.4 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "ImGuiOpenFile.hh"
2
3
#include "ImGuiManager.hh"
4
5
#include "FilePool.hh"
6
#include "Reactor.hh"
7
#include "ReadDir.hh"
8
9
#include "FileOperations.hh"
10
#include "one_of.hh"
11
12
#include <ImGuiFileDialog.h>
13
#include <imgui.h>
14
15
namespace openmsx {
16
17
void ImGuiOpenFile::save(ImGuiTextBuffer& buf)
18
{
19
	for (const auto& [key, value] : lastPath) {
20
		buf.appendf("%s=%s\n", key.c_str(), value.c_str());
21
	}
22
	auto bookmarks = ImGuiFileDialog::Instance()->SerializeBookmarks();
23
	buf.appendf("bookmarks=%s\n", bookmarks.c_str());
24
}
25
26
void ImGuiOpenFile::loadLine(std::string_view name, zstring_view value)
27
{
28
	if (name == "bookmarks") {
29
		ImGuiFileDialog::Instance()->DeserializeBookmarks(std::string(value));
30
	} else {
31
		lastPath[std::string(name)] = value;
32
	}
33
}
34
35
[[nodiscard]] static bool dirOnlyHasReadme(zstring_view directory)
36
{
37
	ReadDir dir(directory);
38
	while (const auto* d = dir.getEntry()) {
39
		auto name = std::string_view(d->d_name);
40
		if (name == one_of(std::string_view(".."), std::string_view("."))) continue;
41
		if (name != "README") return false;
42
	}
43
	return true;
44
}
45
46
[[nodiscard]] static std::string makeUniqueName(std::string_view proposedName, std::vector<std::string>& existingNames)
47
{
48
	std::string name{proposedName};
49
	int count = 1;
50
	while (contains(existingNames, name)) {
51
		name = strCat(proposedName, " (", ++count, ')');
52
	}
53
	existingNames.push_back(name);
54
	return name;
55
}
56
57
void ImGuiOpenFile::setBookmarks()
58
{
59
	auto& dialog = *ImGuiFileDialog::Instance();
60
61
	const auto& filePool = manager.getReactor().getFilePool();
62
	std::vector<std::string> existingNames;
63
	for (const auto& dir : filePool.getDirectories()) {
64
		using enum FileType;
65
		if ((dir.types & (ROM | DISK | TAPE)) == NONE) continue;
66
67
		auto path = std::string(FileOperations::getNativePath(std::string(dir.path)));
68
		if (!FileOperations::isDirectory(path)) continue;
69
		if (dirOnlyHasReadme(path)) continue;
70
		auto name = makeUniqueName(FileOperations::getFilename(dir.path), existingNames);
71
		dialog.RemoveBookmark(name);
72
		dialog.AddBookmark(name, path);
73
	}
74
}
75
76
std::string ImGuiOpenFile::getStartPath(zstring_view lastLocationHint)
77
{
78
	if (!lastLocationHint.empty()) {
79
		if (auto stat = FileOperations::getStat(lastLocationHint)) {
80
			if (FileOperations::isDirectory(*stat)) {
81
				return std::string(lastLocationHint);
82
			} else if (FileOperations::isRegularFile(*stat)) {
83
				return std::string(FileOperations::getDirName(lastLocationHint));
84
			}
85
		}
86
	}
87
	auto [it, inserted] = lastPath.try_emplace(lastTitle, ".");
88
	return it->second;
89
}
90
91
void ImGuiOpenFile::selectFile(const std::string& title, std::string filters,
92
                               const std::function<void(const std::string&)>& callback,
93
                               zstring_view lastLocationHint,
94
                               Painter painter_)
95
{
96
	if (!filters.contains("{.*}")) {
97
		filters += ",All files (*){.*}";
98
	}
99
	int extraFlags = ImGuiFileDialogFlags_DisableCreateDirectoryButton;
100
	common(title, filters.c_str(), callback, lastLocationHint, painter_, extraFlags);
101
}
102
103
void ImGuiOpenFile::selectNewFile(const std::string& title, std::string filters,
104
                                  const std::function<void(const std::string&)>& callback,
105
                                  zstring_view lastLocationHint,
106
                                  Painter painter_)
107
{
108
	if (!filters.contains("{.*}")) {
109
		filters += ",All files (*){.*}";
110
	}
111
	int extraFlags =
112
		ImGuiFileDialogFlags_DisableCreateDirectoryButton |
113
		ImGuiFileDialogFlags_ConfirmOverwrite;
114
	common(title, filters.c_str(), callback, lastLocationHint, painter_, extraFlags);
115
}
116
117
void ImGuiOpenFile::selectDirectory(const std::string& title,
118
                                    const std::function<void(const std::string&)>& callback,
119
                                    zstring_view lastLocationHint,
120
                                    Painter painter_)
121
{
122
	int extraFlags = 0;
123
	common(title, nullptr, callback, lastLocationHint, painter_, extraFlags);
124
}
125
126
void ImGuiOpenFile::common(const std::string& title, const char* filters,
127
                           const std::function<void(const std::string&)>& callback,
128
                           zstring_view lastLocationHint,
129
                           Painter painter_,
130
                           int extraFlags)
131
{
132
	chooseDirectory = filters == nullptr;
133
	activePainter = painter_;
134
	setBookmarks();
135
136
	lastTitle = title;
137
138
	auto startPath = getStartPath(lastLocationHint);
139
	IGFD::FileDialogConfig config;
140
	config.path = startPath;
141
	config.flags = extraFlags |
142
		ImGuiFileDialogFlags_DontShowHiddenFiles |
143
		ImGuiFileDialogFlags_CaseInsensitiveExtention |
144
		ImGuiFileDialogFlags_Modal;
145
	ImGuiFileDialog::Instance()->OpenDialog(
146
		"FileDialog", title, filters, config);
147
	openFileCallback = callback;
148
}
149
150
void ImGuiOpenFile::doPaint()
151
{
152
	// (Modal) file dialog
153
	auto* fileDialog = ImGuiFileDialog::Instance();
154
	if (fileDialog->Display("FileDialog", ImGuiWindowFlags_NoCollapse, ImVec2(560.0f, 360.0f))) {
155
		if (fileDialog->IsOk() && openFileCallback) {
156
			lastPath[lastTitle] = fileDialog->GetCurrentPath();
157
			std::string filePathName = FileOperations::getConventionalPath( // Windows: replace backslash with slash
158
				chooseDirectory ? fileDialog->GetCurrentPath() : fileDialog->GetFilePathName());
159
			openFileCallback(filePathName);
160
			openFileCallback = {};
161
		}
162
		fileDialog->Close();
163
	}
164
}
165
166
std::string ImGuiOpenFile::getLastFilter()
167
{
168
	auto* fileDialog = ImGuiFileDialog::Instance();
169
	return fileDialog->GetCurrentFilter();
170
}
171
172
} // namespace openmsx