use {
super::*,
crate::{
command::*,
display::{Screen, W},
errors::ProgramError,
flag::Flag,
help::HelpState,
pattern::*,
preview::{PreviewMode, PreviewState},
print,
stage::*,
task_sync::Dam,
tree::*,
verb::*,
},
std::{
path::{Path, PathBuf},
str::FromStr,
},
};
/// a panel state, stackable to allow reverting
/// to a previous one
pub trait PanelState {
fn get_type(&self) -> PanelStateType;
fn set_mode(&mut self, mode: Mode);
fn get_mode(&self) -> Mode;
/// called on start of on_command
fn clear_pending(&mut self) {}
fn on_click(
&mut self,
_x: u16,
_y: u16,
_screen: Screen,
_con: &AppContext,
) -> Result<CmdResult, ProgramError> {
Ok(CmdResult::Keep)
}
fn on_double_click(
&mut self,
_x: u16,
_y: u16,
_screen: Screen,
_con: &AppContext,
) -> Result<CmdResult, ProgramError> {
Ok(CmdResult::Keep)
}
fn on_pattern(
&mut self,
_pat: InputPattern,
_app_state: &AppState,
_con: &AppContext,
) -> Result<CmdResult, ProgramError> {
Ok(CmdResult::Keep)
}
fn on_mode_verb(
&mut self,
mode: Mode,
con: &AppContext,
) -> CmdResult {
if con.modal {
self.set_mode(mode);
CmdResult::Keep
} else {
CmdResult::error("modal mode not enabled in configuration")
}
}
/// execute the internal with the optional given invocation.
///
/// The invocation comes from the input and may be related
/// to a different verb (the verb may have been triggered
/// by a key shortcut)
fn on_internal(
&mut self,
w: &mut W,
internal_exec: &InternalExecution,
input_invocation: Option<&VerbInvocation>,
trigger_type: TriggerType,
app_state: &mut AppState,
cc: &CmdContext,
) -> Result<CmdResult, ProgramError>;
/// a generic implementation of on_internal which may be
/// called by states when they don't have a specific
/// behavior to execute
fn on_internal_generic(
&mut self,
_w: &mut W,
internal_exec: &InternalExecution,
input_invocation: Option<&VerbInvocation>,
_trigger_type: TriggerType,
app_state: &mut AppState,
cc: &CmdContext,
) -> Result<CmdResult, ProgramError> {
let con = &cc.app.con;
let screen = cc.app.screen;
let bang = input_invocation
.map(|inv| inv.bang)
.unwrap_or(internal_exec.bang);
Ok(match internal_exec.internal {
Internal::back => CmdResult::PopState,
Internal::copy_line | Internal::copy_path => {
#[cfg(not(feature = "clipboard"))]
{
CmdResult::error("Clipboard feature not enabled at compilation")
}
#[cfg(feature = "clipboard")]
{
if let Some(path) = self.selected_path() {
let path = path.to_string_lossy().to_string();
match terminal_clipboard::set_string(path) {
Ok(()) => CmdResult::Keep,
Err(_) => CmdResult::error("Clipboard error while copying path"),
}
} else {
CmdResult::error("Nothing to copy")
}
}
}
Internal::close_panel_ok => CmdResult::ClosePanel {
validate_purpose: true,
panel_ref: PanelReference::Active,
},
Internal::close_panel_cancel => CmdResult::ClosePanel {
validate_purpose: false,
panel_ref: PanelReference::Active,
},
#[cfg(unix)]
Internal::filesystems => {
let fs_state = crate::filesystems::FilesystemState::new(
self.selected_path(),
self.tree_options(),
con,
);
match fs_state {
Ok(state) => {
let bang = input_invocation
.map(|inv| inv.bang)
.unwrap_or(internal_exec.bang);
if bang && cc.app.preview_panel.is_none() {
CmdResult::NewPanel {
state: Box::new(state),
purpose: PanelPurpose::None,
direction: HDir::Right,
}
} else {
CmdResult::NewState(Box::new(state))
}
}
Err(e) => CmdResult::DisplayError(format!("{}", e)),
}
}
Internal::help => {
let bang = input_invocation
.map(|inv| inv.bang)
.unwrap_or(internal_exec.bang);
if bang && cc.app.preview_panel.is_none() {
CmdResult::NewPanel {
state: Box::new(HelpState::new(self.tree_options(), screen, con)),
purpose: PanelPurpose::None,
direction: HDir::Right,
}
} else {
CmdResult::NewState(Box::new(
HelpState::new(self.tree_options(), screen, con)