Skip to content

Commit a25b7c7

Browse files
committed
feat: implement copy, cut, and paste
1 parent 706bb50 commit a25b7c7

File tree

3 files changed

+103
-9
lines changed

3 files changed

+103
-9
lines changed

src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ fn build_fm(app: &Application) {
4242
let path_bar = pathbar::build_pathbar(&mut fmstate.borrow_mut());
4343

4444
// right click menus
45-
let empty_area_menu = popup_menu::get_empty_right_click(&content_area, fmstate.clone());
45+
let empty_area_menu = popup_menu::get_empty_right_click(&content_area, fmstate.clone(), &files_list);
4646
let file_area_menu =
47-
popup_menu::get_file_right_click(&content_area, fmstate.clone(), &files_list);
47+
popup_menu::get_file_right_click(&content_area, fmstate.clone(), &files_list, &list_view);
4848

4949
// implement all actions for the headerbar
5050
headerbar::implement_actions(&window, &app, fmstate.clone(), &files_list);

src/popup_menu.rs

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
use crate::state::FmState;
21
use crate::files_panel;
2+
use crate::state::FmState;
33
use gtk4::{
44
Box as GtkBox, Label, ListView, Popover, SignalListItemFactory, SingleSelection, StringList,
55
gio, glib, prelude::*,
66
};
7+
use std::path::PathBuf;
78
use std::{cell::RefCell, env, path::Path, process::Command, rc::Rc};
89

910
struct MenuItem<'a> {
@@ -13,7 +14,7 @@ struct MenuItem<'a> {
1314
show_if_dir: bool,
1415
}
1516

16-
pub fn get_empty_right_click(content_area: &GtkBox, fmstate: Rc<RefCell<FmState>>) -> Popover {
17+
pub fn get_empty_right_click(content_area: &GtkBox, fmstate: Rc<RefCell<FmState>>, files_list: &gtk4::StringList) -> Popover {
1718
let popover = Popover::new();
1819
popover.set_parent(content_area);
1920

@@ -24,6 +25,12 @@ pub fn get_empty_right_click(content_area: &GtkBox, fmstate: Rc<RefCell<FmState>
2425
show_if_file: true,
2526
show_if_dir: true,
2627
}),
28+
Rc::new(MenuItem {
29+
label: "Paste",
30+
icon_name: "edit-paste-symbolic",
31+
show_if_file: true,
32+
show_if_dir: true,
33+
}),
2734
Rc::new(MenuItem {
2835
label: "Open Terminal Here",
2936
icon_name: "utilities-terminal-symbolic",
@@ -75,6 +82,8 @@ pub fn get_empty_right_click(content_area: &GtkBox, fmstate: Rc<RefCell<FmState>
7582
selection_model.connect_selected_notify(glib::clone!(
7683
#[weak]
7784
popover,
85+
#[weak]
86+
files_list,
7887
#[strong]
7988
fmstate,
8089
move |sel| {
@@ -84,6 +93,9 @@ pub fn get_empty_right_click(content_area: &GtkBox, fmstate: Rc<RefCell<FmState>
8493

8594
match text.as_str() {
8695
"New Folder" => println!("New Folder clicked"),
96+
"Paste" => {
97+
paste_function(fmstate.clone(), &files_list)
98+
}
8799
"Open Terminal Here" => {
88100
let terminal_cmd =
89101
env::var("TERMINAL").unwrap_or_else(|_| "xterm".to_string());
@@ -121,6 +133,7 @@ pub fn get_file_right_click(
121133
content_area: &GtkBox,
122134
fmstate: Rc<RefCell<FmState>>,
123135
files_list: &gtk4::StringList,
136+
filespanel_list_view: &gtk4::ListView,
124137
) -> Popover {
125138
let popover = Popover::new();
126139
popover.set_parent(content_area);
@@ -130,6 +143,8 @@ pub fn get_file_right_click(
130143
fmstate,
131144
#[weak]
132145
files_list,
146+
#[weak]
147+
filespanel_list_view,
133148
move |popover| {
134149
let fmstate_ref = fmstate.borrow();
135150
if let Some(path) = &fmstate_ref.popup_focused_file {
@@ -231,6 +246,8 @@ pub fn get_file_right_click(
231246
#[weak]
232247
files_list,
233248
#[weak]
249+
filespanel_list_view,
250+
#[weak]
234251
popover,
235252
move |sel| {
236253
if let Some(item) = sel.selected_item() {
@@ -246,9 +263,27 @@ pub fn get_file_right_click(
246263
}
247264
}
248265
}
249-
"Cut" => {}
250-
"Copy" => {}
251-
"Paste" => {}
266+
"Cut" => {
267+
let mut fmstate_mut = fmstate.borrow_mut();
268+
if let Some(path_str) = &fmstate_mut.popup_focused_file {
269+
let path = PathBuf::from(path_str);
270+
fmstate_mut.clipboard.clear();
271+
fmstate_mut.clipboard.push(path);
272+
fmstate_mut.clipboard_is_cut = true;
273+
}
274+
}
275+
"Copy" => {
276+
let mut fmstate_mut = fmstate.borrow_mut();
277+
if let Some(path_str) = &fmstate_mut.popup_focused_file {
278+
let path = PathBuf::from(path_str);
279+
fmstate_mut.clipboard.clear();
280+
fmstate_mut.clipboard.push(path);
281+
fmstate_mut.clipboard_is_cut = false;
282+
}
283+
}
284+
"Paste" => {
285+
paste_function(fmstate.clone(), &files_list)
286+
}
252287
"Open in Terminal" => {
253288
let terminal_cmd = env::var("TERMINAL")
254289
.unwrap_or_else(|_| "xterm".to_string());
@@ -298,7 +333,7 @@ pub fn get_file_right_click(
298333
&file,
299334
&files_list,
300335
&current_path,
301-
show_hidden
336+
show_hidden,
302337
);
303338
}
304339
}
@@ -326,7 +361,7 @@ fn rename_file_dialog(
326361
file_path: &gio::File,
327362
files_list: &gtk4::StringList,
328363
current_path: &gio::File,
329-
show_hidden: bool
364+
show_hidden: bool,
330365
) {
331366
let file_name: String = file_path
332367
.basename()
@@ -381,3 +416,57 @@ fn rename_file_dialog(
381416

382417
dialog.show();
383418
}
419+
420+
fn get_selected_index(list_view: &gtk4::ListView) -> Option<u32> {
421+
list_view
422+
.model()
423+
.and_then(|m| m.downcast::<gtk4::SingleSelection>().ok())
424+
.and_then(|sel| Some(sel.selected()))
425+
}
426+
427+
fn paste_function(fmstate: Rc<RefCell<FmState>>, files_list: &gtk4::StringList) {
428+
let mut state = fmstate.borrow_mut();
429+
for src_path in &state.clipboard {
430+
let src_file = gio::File::for_path(src_path);
431+
let dest_file = state
432+
.current_path
433+
.child(src_path.file_name().unwrap().to_str().unwrap());
434+
435+
let flags = gio::FileCopyFlags::OVERWRITE;
436+
437+
let result = if state.clipboard_is_cut {
438+
src_file.move_(
439+
&dest_file,
440+
flags,
441+
None::<&gio::Cancellable>,
442+
None,
443+
)
444+
} else {
445+
src_file.copy(
446+
&dest_file,
447+
flags,
448+
None::<&gio::Cancellable>,
449+
None,
450+
)
451+
};
452+
453+
if let Err(e) = result {
454+
eprintln!(
455+
"Failed to paste {}: {}",
456+
src_path.display(),
457+
e
458+
);
459+
}
460+
}
461+
462+
if state.clipboard_is_cut {
463+
state.clipboard.clear();
464+
state.clipboard_is_cut = false;
465+
}
466+
467+
files_panel::populate_files_list(
468+
&files_list,
469+
&state.current_path,
470+
&state.settings.show_hidden,
471+
);
472+
}

src/state.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
use crate::utils::FMSettings;
22
use gtk4::{gio, glib::GString};
3+
use std::path::PathBuf;
34

45
pub struct FmState {
56
pub current_path: gio::File,
67
pub on_path_changed: Vec<Box<dyn Fn(&gio::File)>>,
78
pub settings: FMSettings,
89
pub hovered_file: Option<GString>,
910
pub popup_focused_file: Option<GString>,
11+
pub clipboard: Vec<PathBuf>,
12+
pub clipboard_is_cut: bool,
1013
}
1114

1215
impl FmState {
@@ -17,6 +20,8 @@ impl FmState {
1720
settings: FMSettings::new(),
1821
hovered_file: None,
1922
popup_focused_file: None,
23+
clipboard: Vec::new(),
24+
clipboard_is_cut: false,
2025
}
2126
}
2227

0 commit comments

Comments
 (0)