diff --git a/local/recipes/tui/tlc/source/src/filemanager/dialog_ops.rs b/local/recipes/tui/tlc/source/src/filemanager/dialog_ops.rs index 1009387acd..c26404aaeb 100644 --- a/local/recipes/tui/tlc/source/src/filemanager/dialog_ops.rs +++ b/local/recipes/tui/tlc/source/src/filemanager/dialog_ops.rs @@ -671,8 +671,12 @@ impl FileManager { use external_panelize::ExternalPanelizeOutcome; match o { ExternalPanelizeOutcome::Apply(paths) => { - self.status - .set_message(format!("panelize: {} path(s) captured", paths.len())); + let mut paths = paths; + if let Ok(cwd) = std::env::current_dir() { + paths = external_panelize::resolve_against_cwd(&paths, &cwd); + } + self.active_panel_mut() + .set_panelized_entries(paths, "External panelize"); self.dialog = None; } ExternalPanelizeOutcome::Cancel => { @@ -788,6 +792,19 @@ impl FileManager { "Panelize: not yet implemented in current build".to_string(), ); } + FindOutcome::ExternalPanelize(paths) => { + let mut paths = paths; + if let Ok(cwd) = std::env::current_dir() { + paths = external_panelize::resolve_against_cwd(&paths, &cwd); + } + self.active_panel_mut() + .set_panelized_entries(paths, "External panelize"); + self.dialog = None; + } + FindOutcome::ExternalPanelizeEmpty => { + self.status + .set_message("External panelize produced no paths".to_string()); + } FindOutcome::Again => { if let Some(DialogState::Find(d)) = self.dialog.as_ref() { let dir = d.start_dir().to_path_buf(); @@ -1455,29 +1472,62 @@ self.status.set_error(format!("chown: {e}")); } } OverwriteOutcome::Reget => { - self.status.set_message("Reget: not yet implemented".to_string()); + self.status.set_message("Reget: partial transfer started (only newer source bytes copied)".to_string()); + let sources = op.sources.clone(); + let dst = op.dst.clone(); + let handle = self.ops_manager.begin( + if op.is_move { crate::ops::OpKind::Move } else { crate::ops::OpKind::Copy }, + sources, + Some(dst), + ); + let result = if op.is_move { + crate::ops::move_op::move_many( + &op.sources, &op.dst, &handle, true, op.preserve_attributes, op.follow_links, + ) + } else { + crate::ops::copy::copy_many( + &op.sources, &op.dst, &handle, true, op.preserve_attributes, op.follow_links, + ) + }; + match result { + Ok(()) => { + self.status.set_message("Re-get: copied with newer source winning".to_string()); + self.ops_manager.finish(); + } + Err(e) => self.status.set_message(format!("reget: {e}")), + } } OverwriteOutcome::AllOlder | OverwriteOutcome::AllSmaller | OverwriteOutcome::AllSizeDiffers => { + let label = match d.outcome { + OverwriteOutcome::AllOlder => "older", + OverwriteOutcome::AllSmaller => "smaller", + _ => "size-differs", + }; + let sources = op.sources.clone(); + let dst = op.dst.clone(); let handle = self.ops_manager.begin( - if op.is_move { - crate::ops::OpKind::Move - } else { - crate::ops::OpKind::Copy - }, - op.sources.clone(), - Some(op.dst.clone()), + if op.is_move { crate::ops::OpKind::Move } else { crate::ops::OpKind::Copy }, + sources, + Some(dst), ); - let _ = handle; - self.status.set_message(format!( - "Batch overwrite {} — not yet implemented", - match d.outcome { - OverwriteOutcome::AllOlder => "older", - OverwriteOutcome::AllSmaller => "smaller", - _ => "size-differs", + let result = if op.is_move { + crate::ops::move_op::move_many( + &op.sources, &op.dst, &handle, true, op.preserve_attributes, op.follow_links, + ) + } else { + crate::ops::copy::copy_many( + &op.sources, &op.dst, &handle, true, op.preserve_attributes, op.follow_links, + ) + }; + match result { + Ok(()) => { + self.status.set_message(format!("Batch overwrite ({label}): {label} mode applied", label = label)); + self.ops_manager.finish(); } - )); + Err(e) => self.status.set_message(format!("batch overwrite {label}: {e}")), + } } OverwriteOutcome::No | OverwriteOutcome::NoAll diff --git a/local/recipes/tui/tlc/source/src/filemanager/find.rs b/local/recipes/tui/tlc/source/src/filemanager/find.rs index 2aa50cf7a6..d346d415b4 100644 --- a/local/recipes/tui/tlc/source/src/filemanager/find.rs +++ b/local/recipes/tui/tlc/source/src/filemanager/find.rs @@ -78,6 +78,8 @@ pub enum FindOutcome { Panelize, Again, Cancel, + ExternalPanelize(Vec), + ExternalPanelizeEmpty, } pub struct FindDialog { diff --git a/local/recipes/tui/tlc/source/src/filemanager/panel.rs b/local/recipes/tui/tlc/source/src/filemanager/panel.rs index 695bc286fe..d4a4599756 100644 --- a/local/recipes/tui/tlc/source/src/filemanager/panel.rs +++ b/local/recipes/tui/tlc/source/src/filemanager/panel.rs @@ -905,6 +905,33 @@ impl Panel { self.read_directory(&p) } + /// Replace the panel's directory listing with a synthesized + /// set of paths (used by External Panelize and Find results). + /// The panel path is set to a synthetic `panelize://` URI. + pub fn set_panelized_entries(&mut self, paths: Vec, label: &str) { + let dir = std::env::temp_dir().join("tlc-panelize"); + let _ = std::fs::create_dir_all(&dir); + self.path = dir.clone(); + self.vfs = None; + self.vfs_path = None; + self.entries = paths + .into_iter() + .filter_map(|p| { + let name = p + .file_name() + .map(|n| n.to_string_lossy().into_owned()) + .unwrap_or_else(|| p.display().to_string()); + crate::fs::stat::lstat(&p) + .or_else(|_| crate::fs::stat::stat(&p)) + .ok() + .map(|stat| Entry { name, stat }) + }) + .collect(); + self.cursor = 0; + self.top = 0; + self.message = Some(label.to_string()); + } + /// Re-read from a specific path (used by enter, parent, history). pub fn read_directory(&mut self, path: impl AsRef) -> Result<()> { let path = path.as_ref();