diff options
Diffstat (limited to 'src/dialogs.rs')
| -rw-r--r-- | src/dialogs.rs | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/src/dialogs.rs b/src/dialogs.rs new file mode 100644 index 0000000..cb3f191 --- /dev/null +++ b/src/dialogs.rs @@ -0,0 +1,158 @@ +//! Dialog helper functions for file operations and error display. + +use std::path::PathBuf; + +use gtk::prelude::*; +use relm4::{ComponentSender, gtk}; + +use crate::actions::Action; +use crate::constants; +use crate::file_registry; +use crate::AppModel; + +/// Creates a file filter for .wmap files. +pub fn create_file_filter() -> gtk::FileFilter { + let filter = gtk::FileFilter::new(); + filter.add_pattern(&format!("*.{}", constants::FILE_EXTENSION)); + filter.add_mime_type(constants::MIME_TYPE); + filter.set_name(Some("Wardley Map Files")); + filter +} + +/// Shows an error dialog with the given title and message. +pub fn show_error(window: >k::Window, title: &str, message: &str) { + let dialog = gtk::MessageDialog::new( + Some(window), + gtk::DialogFlags::MODAL | gtk::DialogFlags::DESTROY_WITH_PARENT, + gtk::MessageType::Error, + gtk::ButtonsType::Ok, + message, + ); + dialog.set_title(Some(title)); + dialog.connect_response(|dialog, _| { + dialog.close(); + }); + dialog.show(); +} + +/// Shows a file open dialog and handles the response. +pub fn show_open_dialog( + window: >k::Window, + is_empty_document: bool, + sender: ComponentSender<AppModel>, +) { + let dialog = gtk::FileChooserDialog::new( + Some("Open Map"), + Some(window), + gtk::FileChooserAction::Open, + &[ + ("Cancel", gtk::ResponseType::Cancel), + ("Open", gtk::ResponseType::Accept), + ], + ); + dialog.add_filter(&create_file_filter()); + + dialog.connect_response(move |dialog, response| { + dialog.close(); + if response == gtk::ResponseType::Accept + && let Some(file) = dialog.file() + && let Some(path) = file.path() + { + // Check if file is already open - if so, focus that window + if let Some(existing_window) = file_registry::find_window_for_file(&path) { + existing_window.present(); + return; + } + + if is_empty_document { + sender.input(Action::LoadFile(path)); + } else { + AppModel::open_new_window(Some(path)); + } + } + }); + dialog.present(); +} + +/// Shows a file save dialog and handles the response. +pub fn show_save_dialog( + window: >k::Window, + current_file: Option<PathBuf>, + close_after: bool, + sender: ComponentSender<AppModel>, +) { + let dialog = gtk::FileChooserDialog::new( + Some("Save Map As"), + Some(window), + gtk::FileChooserAction::Save, + &[ + ("Cancel", gtk::ResponseType::Cancel), + ("Save", gtk::ResponseType::Accept), + ], + ); + dialog.add_filter(&create_file_filter()); + + if let Some(ref current_path) = current_file { + if let Some(parent) = current_path.parent() { + let folder = gtk::gio::File::for_path(parent); + dialog.set_current_folder(Some(&folder)).ok(); + } + if let Some(filename) = current_path.file_name() { + dialog.set_current_name(filename.to_str().unwrap_or("map.wmap")); + } + } else { + dialog.set_current_name("map.wmap"); + } + + dialog.connect_response(move |dialog, response| { + dialog.close(); + if response == gtk::ResponseType::Accept + && let Some(file) = dialog.file() + && let Some(path) = file.path() + { + sender.input(Action::SaveToFile { path, close_after }); + } + }); + dialog.present(); +} + +/// Shows a dialog asking whether to save unsaved changes before closing. +pub fn show_unsaved_changes_dialog( + window: >k::Window, + current_file: Option<PathBuf>, + sender: ComponentSender<AppModel>, +) { + let dialog = gtk::MessageDialog::new( + Some(window), + gtk::DialogFlags::MODAL | gtk::DialogFlags::DESTROY_WITH_PARENT, + gtk::MessageType::Warning, + gtk::ButtonsType::None, + "Save changes before closing?", + ); + dialog.set_title(Some("Unsaved Changes")); + dialog.add_button("Don't Save", gtk::ResponseType::Reject); + dialog.add_button("Cancel", gtk::ResponseType::Cancel); + dialog.add_button("Save", gtk::ResponseType::Accept); + + let window = window.clone(); + dialog.connect_response(move |dialog, response| { + dialog.close(); + match response { + gtk::ResponseType::Accept => { + if current_file.is_some() { + sender.input(Action::Save { close_after: true }); + } else { + sender.input(Action::SaveAs { close_after: true }); + } + } + gtk::ResponseType::Reject => { + if let Some(ref path) = current_file { + file_registry::unregister_file(path); + } + window.destroy(); + } + _ => {} + } + }); + dialog.show(); +} |