use std::collections::HashSet;
use std::process;
use std::sync::mpsc;
use std::thread;
use crate::commands::quit::QuitAction;
use crate::config::clean::app::AppConfig;
use crate::context::{
CommandLineContext, LocalStateContext, MatchContext, MessageQueue, PreviewContext, TabContext,
UiContext, WorkerContext,
};
use crate::event::{AppEvent, Events};
use crate::ui::views;
use crate::ui::PreviewArea;
use crate::Args;
use notify::{RecursiveMode, Watcher};
use std::path;
pub struct AppContext {
pub quit: QuitAction,
pub events: Events,
pub args: Args,
config: AppConfig,
tab_context: TabContext,
local_state: Option<LocalStateContext>,
search_context: Option<MatchContext>,
message_queue: MessageQueue,
worker_context: WorkerContext,
preview_context: PreviewContext,
commandline_context: CommandLineContext,
ui_context: UiContext,
watcher: notify::RecommendedWatcher,
watched_paths: HashSet<path::PathBuf>,
preview_area: Option<PreviewArea>,
}
impl AppContext {
pub fn new(config: AppConfig, args: Args) -> Self {
let events = Events::new();
let event_tx = events.event_tx.clone();
let mut commandline_context = CommandLineContext::new();
let _ = commandline_context.history_mut().set_max_len(20);
let event_tx_for_fs_notification = event_tx.clone();
let watcher = notify::recommended_watcher(move |res| {
if let Ok(event) = res {
let _ = event_tx_for_fs_notification.send(AppEvent::Filesystem(event));
}
})
.unwrap();
let watched_paths = HashSet::with_capacity(3);
Self {
quit: QuitAction::DoNot,
events,
args,
tab_context: TabContext::new(),
local_state: None,
search_context: None,
message_queue: MessageQueue::new(),
worker_context: WorkerContext::new(event_tx),
preview_context: PreviewContext::new(),
ui_context: UiContext { layout: vec![] },
commandline_context,
config,
watcher,
watched_paths,
preview_area: None,
}
}
fn call_preview_shown_hook(&self, preview_area: PreviewArea) {
let preview_options = self.config_ref().preview_options_ref();
let preview_shown_hook_script = preview_options.preview_shown_hook_script.as_ref();
if let Some(hook_script) = preview_shown_hook_script {
let hook_script = hook_script.to_path_buf();
let _ = thread::spawn(move || {
let _ = process::Command::new(hook_script.as_path())
.arg(preview_area.file_preview_path.as_path())
.arg(preview_area.preview_area.x.to_string())
.arg(preview_area.preview_area.y.to_string())
.arg(preview_area.preview_area.width.to_string())
.arg(preview_area.preview_area.height.to_string())
.status();
});
}
}
fn call_preview_removed_hook(&self) {
let preview_options = self.config_ref().preview_options_ref();
let preview_removed_hook_script = preview_options.preview_removed_hook_script.as_ref();
if let Some(hook_script) = preview_removed_hook_script {
let hook_script = hook_script.to_path_buf();
let _ = thread::spawn(|| {
let _ = process::Command::new(hook_script).status();
});
}
}
pub fn update_external_preview(&mut self) {
let layout = &self.ui_context_ref().layout;
let new_preview_area = views::calculate_preview(self, layout[2]);
match new_preview_area.as_ref() {
Some(new) => {
let should_preview = if let Some(old) = &self.preview_area {
new.file_preview_path != old.file_preview_path
|| new.preview_area != old.preview_area
} else {
true
};
if should_preview {
self.call_preview_shown_hook(new.clone())
}
}
None => {
if self.preview_area.is_some() {
self.call_preview_removed_hook()
}
}
}
self.preview_area = new_preview_area
}
pub fn remove_external_preview(&mut self) {
if self.preview_area.is_some() {
self.call_preview_removed_hook();
self.preview_area = None;
}
}
pub fn update_watcher(&mut self) {
let mut new_paths_to_watch: HashSet<path::PathBuf> = HashSet::with_capacity(3);
let curr_tab_ref = self.tab_context_ref().curr_tab_ref();
let watched_lists = [
curr_tab_ref.parent_list_ref(),
curr_tab_ref.curr_list_ref(),
curr_tab_ref.child_list_ref(),
];
for list in watched_lists.iter().flatten() {
new_paths_to_watch.insert(list.file_path().to_path_buf());
}
for old_watched_path in &self.watched_paths {
if !new_paths_to_watch.contains(old_watched_path.as_path()) {
let _ = self.watcher.unwatch(old_watched_path.as_path());
}
}
for new_watched_path in &new_paths_to_watch {
if !self.watched_paths.contains(new_watched_path.as_path()) {
let _ = self
.watcher
.watch(new_watched_path.as_path(), RecursiveMode::NonRecursive);
}
}
self.watched_paths = new_paths_to_watch;
}
pub fn poll_event(&self) -> Result<AppEvent, mpsc::RecvError> {
self.events.next()
}
pub fn flush_event(&self) {
self.events.flush();
}
pub fn clone_event_tx(&self) -> mpsc::Sender<AppEvent> {
self.events.event_tx.clone()
}
pub fn config_ref(&self) -> &AppConfig {
&self.config
}
pub fn config_mut(&mut self) -> &mut AppConfig {
&mut self.config
}
pub fn tab_context_ref(&self) -> &TabContext {
&self.tab_context
}
pub fn tab_context_mut(&mut self) -> &mut TabContext {
&mut self.tab_context
}
pub fn message_queue_ref(&self) -> &MessageQueue {
&self.message_queue
}
pub fn message_queue_mut(&mut self) -> &mut MessageQueue {
&mut self.message_queue
}
pub fn set_local_state(&mut self, state: LocalStateContext) {
self.local_state = Some(state);
}
pub fn take_local_state(&mut self) -> Option<LocalStateContext> {
self.local_state.take()
}
pub fn get_search_context(&self) -> Option<&MatchContext> {
self.search_context.as_ref()
}
pub fn set_search_context(&mut self, context: MatchContext) {
self.search_context = Some(context);
}
pub fn preview_context_ref(&self) -> &PreviewContext {
&self.preview_context
}
pub fn preview_context_mut(&mut self) -> &mut PreviewContext {
&mut self.preview_context
}
pub fn ui_context_ref(&self) -> &UiContext {
&self.ui_context
}
pub fn ui_context_mut(&mut self) -> &mut UiContext {
&mut self.ui_context
}
pub fn worker_context_ref(&self) -> &WorkerContext {
&self.worker_context
}
pub fn worker_context_mut(&mut self) -> &mut WorkerContext {
&mut self.worker_context
}
pub fn commandline_context_ref(&self) -> &CommandLineContext {
&self.commandline_context
}
pub fn commandline_context_mut(&mut self) -> &mut CommandLineContext {
&mut self.commandline_context
}
}