1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
// Map, wardley map editor for linux
// Copyright (C) 2026 Rubén Beltrán del Río
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use std::path::PathBuf;
use gtk::prelude::*;
use relm4::{ComponentSender, gtk};
use crate::AppModel;
use crate::actions::Action;
use crate::constants;
use crate::dialogs;
use crate::file_registry;
use crate::tr;
/// Loads a file into the current document, updating the file registry.
pub fn load_file(model: &mut AppModel, path: PathBuf) {
match std::fs::read_to_string(&path) {
Ok(content) => {
unregister_current_file(model);
model.set_source_text(&content);
file_registry::register_file(&path, &model.window);
model.current_file = Some(path);
model.modified = false;
}
Err(error) => {
dialogs::show_error(
&model.window,
&tr!("Could not open file. Please make sure the file is readable."),
&error.to_string(),
);
}
}
}
/// Saves the document to the specified path, optionally closing after.
pub fn save_to_file(model: &mut AppModel, path: PathBuf, close_after: bool) {
let content = model.source_text();
let path = ensure_wmap_extension(path);
match std::fs::write(&path, content.as_str()) {
Ok(()) => {
if close_after {
file_registry::unregister_file(&path);
model.modified = false;
model.window.close();
} else {
update_file_registration(model, &path);
model.current_file = Some(path);
model.modified = false;
}
}
Err(error) => {
dialogs::show_error(
&model.window,
&tr!("Could not save file. Please make sure the destination is writable."),
&error.to_string(),
);
}
}
}
/// Handles window close, prompting for unsaved changes if needed.
pub fn close_window(model: &mut AppModel, sender: &ComponentSender<AppModel>) {
if model.modified {
dialogs::show_unsaved_changes_dialog(
&model.window,
model.current_file.clone(),
sender.clone(),
);
} else {
unregister_current_file(model);
model.window.destroy();
}
}
/// Dispatches save action based on whether file already has a path.
pub fn save(model: &AppModel, close_after: bool, sender: &ComponentSender<AppModel>) {
if let Some(path) = model.current_file.clone() {
sender.input(Action::SaveToFile { path, close_after });
} else {
sender.input(Action::SaveAs { close_after });
}
}
// === Helper Functions ===
/// Unregisters the current file from the registry if one is open.
fn unregister_current_file(model: &AppModel) {
if let Some(ref path) = model.current_file {
file_registry::unregister_file(path);
}
}
/// Updates file registration when saving to a new path.
fn update_file_registration(model: &mut AppModel, new_path: &PathBuf) {
if model.current_file.as_ref() != Some(new_path) {
unregister_current_file(model);
file_registry::register_file(new_path, &model.window);
}
}
/// Ensures the path has the .wmap extension.
fn ensure_wmap_extension(path: PathBuf) -> PathBuf {
if path.extension().is_none()
|| path.extension().unwrap_or_default() != constants::FILE_EXTENSION
{
path.with_extension(constants::FILE_EXTENSION)
} else {
path
}
}
|