yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
project_file_editor.cc
Go to the documentation of this file.
2
3#include <fstream>
4#include <sstream>
5
6#include "absl/strings/match.h"
7#include "absl/strings/str_format.h"
8#include "absl/strings/str_split.h"
9#include "util/file_util.h"
11#include "app/gui/icons.h"
12#include "imgui/imgui.h"
13
14namespace yaze {
15namespace editor {
16
22
24 if (!active_) return;
25
26 ImGui::SetNextWindowSize(ImVec2(900, 700), ImGuiCond_FirstUseEver);
27 if (!ImGui::Begin(absl::StrFormat("%s Project Editor###ProjectFileEditor",
28 ICON_MD_DESCRIPTION).c_str(),
29 &active_)) {
30 ImGui::End();
31 return;
32 }
33
34 // Toolbar
35 if (ImGui::BeginTable("ProjectEditorToolbar", 8, ImGuiTableFlags_SizingFixedFit)) {
36 ImGui::TableNextColumn();
37 if (ImGui::Button(absl::StrFormat("%s New", ICON_MD_NOTE_ADD).c_str())) {
38 NewFile();
39 }
40
41 ImGui::TableNextColumn();
42 if (ImGui::Button(absl::StrFormat("%s Open", ICON_MD_FOLDER_OPEN).c_str())) {
44 if (!file.empty()) {
45 auto status = LoadFile(file);
46 if (!status.ok() && toast_manager_) {
47 toast_manager_->Show(std::string(status.message()),
49 }
50 }
51 }
52
53 ImGui::TableNextColumn();
54 bool can_save = !filepath_.empty() && IsModified();
55 if (!can_save) ImGui::BeginDisabled();
56 if (ImGui::Button(absl::StrFormat("%s Save", ICON_MD_SAVE).c_str())) {
57 auto status = SaveFile();
58 if (status.ok() && toast_manager_) {
59 toast_manager_->Show("Project file saved", ToastType::kSuccess);
60 } else if (!status.ok() && toast_manager_) {
61 toast_manager_->Show(std::string(status.message()), ToastType::kError);
62 }
63 }
64 if (!can_save) ImGui::EndDisabled();
65
66 ImGui::TableNextColumn();
67 if (ImGui::Button(absl::StrFormat("%s Save As", ICON_MD_SAVE_AS).c_str())) {
69 filepath_.empty() ? "project" : filepath_, "yaze");
70 if (!file.empty()) {
71 auto status = SaveFileAs(file);
72 if (status.ok() && toast_manager_) {
73 toast_manager_->Show("Project file saved", ToastType::kSuccess);
74 } else if (!status.ok() && toast_manager_) {
75 toast_manager_->Show(std::string(status.message()), ToastType::kError);
76 }
77 }
78 }
79
80 ImGui::TableNextColumn();
81 ImGui::Text("|");
82
83 ImGui::TableNextColumn();
84 if (ImGui::Button(absl::StrFormat("%s Validate", ICON_MD_CHECK_CIRCLE).c_str())) {
86 show_validation_ = true;
87 }
88
89 ImGui::TableNextColumn();
90 ImGui::Checkbox("Show Validation", &show_validation_);
91
92 ImGui::TableNextColumn();
93 if (!filepath_.empty()) {
94 ImGui::TextDisabled("%s", filepath_.c_str());
95 } else {
96 ImGui::TextDisabled("No file loaded");
97 }
98
99 ImGui::EndTable();
100 }
101
102 ImGui::Separator();
103
104 // Validation errors panel
105 if (show_validation_ && !validation_errors_.empty()) {
106 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.3f, 0.2f, 0.2f, 0.5f));
107 if (ImGui::BeginChild("ValidationErrors", ImVec2(0, 100), true)) {
108 ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f),
109 "%s Validation Errors:", ICON_MD_ERROR);
110 for (const auto& error : validation_errors_) {
111 ImGui::BulletText("%s", error.c_str());
112 }
113 }
114 ImGui::EndChild();
115 ImGui::PopStyleColor();
116 }
117
118 // Main editor
119 ImVec2 editor_size = ImGui::GetContentRegionAvail();
120 text_editor_.Render("##ProjectEditor", editor_size);
121
122 ImGui::End();
123}
124
125absl::Status ProjectFileEditor::LoadFile(const std::string& filepath) {
126 std::ifstream file(filepath);
127 if (!file.is_open()) {
128 return absl::InvalidArgumentError(
129 absl::StrFormat("Cannot open file: %s", filepath));
130 }
131
132 std::stringstream buffer;
133 buffer << file.rdbuf();
134 file.close();
135
136 text_editor_.SetText(buffer.str());
138 modified_ = false;
139
141
142 return absl::OkStatus();
143}
144
146 if (filepath_.empty()) {
147 return absl::InvalidArgumentError("No file path specified");
148 }
149
150 return SaveFileAs(filepath_);
151}
152
153absl::Status ProjectFileEditor::SaveFileAs(const std::string& filepath) {
154 // Ensure .yaze extension
155 std::string final_path = filepath;
156 if (!absl::EndsWith(final_path, ".yaze")) {
157 final_path += ".yaze";
158 }
159
160 std::ofstream file(final_path);
161 if (!file.is_open()) {
162 return absl::InvalidArgumentError(
163 absl::StrFormat("Cannot create file: %s", final_path));
164 }
165
166 file << text_editor_.GetText();
167 file.close();
168
169 filepath_ = final_path;
170 modified_ = false;
171
172 // Add to recent files
173 auto& recent_mgr = core::RecentFilesManager::GetInstance();
174 recent_mgr.AddFile(filepath_);
175 recent_mgr.Save();
176
177 return absl::OkStatus();
178}
179
181 // Create a template project file
182 const char* template_content = R"(# yaze Project File
183# Format Version: 2.0
184
185[project]
186name=New Project
187description=
188author=
189license=
190version=1.0
191created_date=
192last_modified=
193yaze_version=0.4.0
194tags=
195
196[files]
197rom_filename=
198rom_backup_folder=backups
199code_folder=asm
200assets_folder=assets
201patches_folder=patches
202labels_filename=labels.txt
203symbols_filename=symbols.txt
204output_folder=build
205additional_roms=
206
207[feature_flags]
208# REMOVED: kLogInstructions - DisassemblyViewer is always active
209kSaveDungeonMaps=true
210kSaveGraphicsSheet=true
211kLoadCustomOverworld=false
212
213[workspace_settings]
214font_global_scale=1.0
215autosave_enabled=true
216autosave_interval_secs=300
217theme=dark
218)";
219
220 text_editor_.SetText(template_content);
221 filepath_.clear();
222 modified_ = true;
223 validation_errors_.clear();
224}
225
227 // TODO: Implement custom syntax highlighting for INI format
228 // For now, use C language definition which provides some basic highlighting
229}
230
232 validation_errors_.clear();
233
234 std::string content = text_editor_.GetText();
235 std::vector<std::string> lines = absl::StrSplit(content, '\n');
236
237 std::string current_section;
238 int line_num = 0;
239
240 for (const auto& line : lines) {
241 line_num++;
242 std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
243
244 // Skip empty lines and comments
245 if (trimmed.empty() || trimmed[0] == '#') continue;
246
247 // Check for section headers
248 if (trimmed[0] == '[' && trimmed[trimmed.size() - 1] == ']') {
249 current_section = trimmed.substr(1, trimmed.size() - 2);
250
251 // Validate known sections
252 if (current_section != "project" &&
253 current_section != "files" &&
254 current_section != "feature_flags" &&
255 current_section != "workspace_settings" &&
256 current_section != "build_settings") {
257 validation_errors_.push_back(
258 absl::StrFormat("Line %d: Unknown section [%s]",
259 line_num, current_section));
260 }
261 continue;
262 }
263
264 // Check for key=value pairs
265 size_t equals_pos = trimmed.find('=');
266 if (equals_pos == std::string::npos) {
267 validation_errors_.push_back(
268 absl::StrFormat("Line %d: Invalid format, expected key=value", line_num));
269 continue;
270 }
271 }
272
274 toast_manager_->Show("Project file validation passed", ToastType::kSuccess);
275 }
276}
277
279 if (validation_errors_.empty()) return;
280
281 ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Validation Errors:");
282 for (const auto& error : validation_errors_) {
283 ImGui::BulletText("%s", error.c_str());
284 }
285}
286
287} // namespace editor
288} // namespace yaze
void SetShowWhitespaces(bool aValue)
std::string GetText() const
void Render(const char *aTitle, const ImVec2 &aSize=ImVec2(), bool aBorder=false)
void SetText(const std::string &aText)
void SetTabSize(int aValue)
void SetLanguageDefinition(const LanguageDefinition &aLanguageDef)
static RecentFilesManager & GetInstance()
Definition project.h:244
absl::Status SaveFileAs(const std::string &filepath)
Save to a new file path.
absl::Status LoadFile(const std::string &filepath)
Load a project file into the editor.
void NewFile()
Create a new empty project file.
bool IsModified() const
Get whether the file has unsaved changes.
std::vector< std::string > validation_errors_
absl::Status SaveFile()
Save the current editor contents to disk.
const std::string & filepath() const
Get the current filepath.
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
static std::string ShowSaveFileDialog(const std::string &default_name="", const std::string &default_extension="")
ShowSaveFileDialog opens a save file dialog and returns the selected filepath. Uses global feature fl...
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
#define ICON_MD_FOLDER_OPEN
Definition icons.h:811
#define ICON_MD_NOTE_ADD
Definition icons.h:1328
#define ICON_MD_SAVE_AS
Definition icons.h:1644
#define ICON_MD_ERROR
Definition icons.h:684
#define ICON_MD_CHECK_CIRCLE
Definition icons.h:398
#define ICON_MD_DESCRIPTION
Definition icons.h:537
#define ICON_MD_SAVE
Definition icons.h:1642
Main namespace for the application.
static const LanguageDefinition & C()