use std::path::PathBuf;
use std::time::SystemTime;
use crate::app::Application;
use crate::core::command::CommandId;
use crate::views::view::View;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExternalState {
NoFile,
Unchanged,
Modified,
Deleted,
}
pub trait Editor: View {
fn valid_close(&mut self, _app: &mut Application, _command: CommandId) -> bool {
true
}
fn undo(&mut self) {}
fn redo(&mut self) {}
fn can_undo(&self) -> bool {
false
}
fn can_redo(&self) -> bool {
false
}
fn cut(&mut self) -> bool {
false
}
fn copy(&mut self) -> bool {
false
}
fn paste(&mut self) -> bool {
false
}
fn select_all(&mut self) {}
fn clear_selection(&mut self) {}
fn has_selection(&self) -> bool {
false
}
}
pub trait FileEditor: Editor {
fn file_path(&self) -> Option<PathBuf>;
fn set_file_path(&mut self, path: Option<PathBuf>);
fn is_dirty(&self) -> bool;
fn save(&mut self) -> std::io::Result<()>;
fn save_as(&mut self, path: PathBuf) -> std::io::Result<()>;
fn load(&mut self, path: PathBuf) -> std::io::Result<()>;
fn new_buffer(&mut self);
fn last_known_mtime(&self) -> Option<SystemTime>;
fn poll_external_changes(&self) -> ExternalState;
fn reload(&mut self) -> std::io::Result<()>;
fn display_name(&self) -> String {
self.file_path()
.as_deref()
.and_then(|p| p.file_name())
.and_then(|n| n.to_str())
.map(|s| s.to_string())
.unwrap_or_else(|| "Untitled".to_string())
}
fn prompt_save_as(&mut self, app: &mut Application) -> bool;
}
pub fn confirm_save_on_close<E: FileEditor + ?Sized>(
editor: &mut E,
app: &mut Application,
command: CommandId,
) -> bool {
use crate::core::command::{CM_CLOSE, CM_NO, CM_YES};
use crate::views::msgbox::confirmation_box;
if command != CM_CLOSE || !editor.is_dirty() {
return true;
}
let message = format!("{} has been modified.\n\nSave changes?", editor.display_name());
match confirmation_box(app, &message) {
c if c == CM_YES => {
if editor.file_path().is_some() {
editor.save().is_ok()
} else {
editor.prompt_save_as(app)
}
}
c if c == CM_NO => true,
_ => false,
}
}