//! 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, ) { 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, close_after: bool, sender: ComponentSender, ) { 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, sender: ComponentSender, ) { 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(); }