1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//! Ctrl+X, Ctrl+C and Ctrl+V on a file manager's rows: the keys a desktop file explorer cuts,
//! copies and pastes entries with, claimed on the manager's node so they reach it only while its
//! rows have focus and no text is selected with the mouse.
use crate::widget::{ClipboardKey, NodeMut};
use super::state::ROOT;
use super::{FileManager, FileManagerMsg, FileView, parent_key};
impl<Msg: Clone + 'static> FileManager<'_, Msg> {
/// Claims the clipboard keys on `node`, the node holding the rows, for what can be done now.
///
/// A key with nothing to act on is left unclaimed, so it does what it does without the
/// manager: Ctrl+C and Ctrl+X with no entry selected, Ctrl+V with nothing waiting.
pub(super) fn claim_clipboard<'v>(&self, node: NodeMut<'v, Msg>) -> NodeMut<'v, Msg> {
if self.disabled {
return node;
}
let mut node = node;
if let Some(key) = self.clipboard_target() {
node = node
.on_clipboard(ClipboardKey::Cut, (self.wrap)(FileManagerMsg::Cut(key.clone())))
.on_clipboard(ClipboardKey::Copy, (self.wrap)(FileManagerMsg::Copy(key)));
}
if !self.state.pending().is_empty() {
let into = self.paste_folder();
node = node.on_clipboard(ClipboardKey::Paste, (self.wrap)(FileManagerMsg::Paste(into)));
}
node
}
/// The key a cut or a copy is sent for, which [`FileManagerState::targets`] widens to the
/// whole selection: one of the selected entries, or the entry under the cursor when nothing
/// is selected. The root, and in a flat view the row of the shown folder itself, are where
/// the person is rather than entries to carry away, so they are never it.
///
/// [`FileManagerState::targets`]: super::FileManagerState::targets
fn clipboard_target(&self) -> Option<String> {
let state = self.state;
let shown = (self.view != FileView::Tree).then(|| state.folder());
let movable = |key: &&str| *key != ROOT && Some(*key) != shown;
let chosen = state.chosen().iter().map(String::as_str).find(movable);
let key = if state.chosen().is_empty() { state.selected().filter(movable) } else { chosen };
key.map(str::to_owned)
}
/// Where Ctrl+V pastes. A flat view pastes into the folder it shows, as a desktop explorer's
/// window does. The tree shows every folder at once, so it pastes where the cursor is: into
/// the folder the cursor is on, into the folder holding the file the cursor is on, and into
/// the root while the cursor is nowhere.
fn paste_folder(&self) -> String {
let state = self.state;
if self.view != FileView::Tree {
return state.folder().to_owned();
}
match state.selected() {
Some(key) if key == ROOT || state.is_folder(key) => key.to_owned(),
Some(key) => parent_key(key).to_owned(),
None => ROOT.to_owned(),
}
}
}