From a2560db6bac7f7b47e2588cb4c14505a705568fc Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 20 Mar 2024 10:27:35 -0600 Subject: [PATCH] Parse pastes --- src/app.rs | 21 ++++++++++---- src/clipboard.rs | 73 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/src/app.rs b/src/app.rs index aa42678..593bc67 100644 --- a/src/app.rs +++ b/src/app.rs @@ -33,7 +33,7 @@ use std::{ }; use crate::{ - clipboard::{ClipboardContents, ClipboardKind}, + clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste}, config::{AppTheme, Config, IconSizes, TabConfig, CONFIG_VERSION}, fl, home_dir, key_bind::key_binds, @@ -155,6 +155,7 @@ pub enum Message { OpenTerminal(Option), OpenWith(PathBuf, mime_app::MimeApp), Paste(Option), + PasteContents(Option, ClipboardPaste), PendingComplete(u64), PendingError(u64, String), PendingProgress(u64, f32), @@ -808,12 +809,12 @@ impl Application for App { } Message::Copy(entity_opt) => { let paths = self.selected_paths(entity_opt); - let contents = ClipboardContents::new(ClipboardKind::Copy, &paths); + let contents = ClipboardCopy::new(ClipboardKind::Copy, &paths); return clipboard::write_data(contents); } Message::Cut(entity_opt) => { let paths = self.selected_paths(entity_opt); - let contents = ClipboardContents::new(ClipboardKind::Cut, &paths); + let contents = ClipboardCopy::new(ClipboardKind::Cut, &paths); return clipboard::write_data(contents); } Message::DialogCancel => { @@ -990,8 +991,18 @@ impl Application for App { // Close Open With context view self.core.window.show_context = false; } - Message::Paste(_entity_opt) => { - log::warn!("TODO: PASTE"); + Message::Paste(entity_opt) => { + return clipboard::read_data::(move |contents_opt| { + match contents_opt { + Some(contents) => { + message::app(Message::PasteContents(entity_opt, contents)) + } + None => message::none(), + } + }); + } + Message::PasteContents(entity_opt, contents) => { + println!("{:?}", contents); } Message::PendingComplete(id) => { if let Some((op, _)) = self.pending_operations.remove(&id) { diff --git a/src/clipboard.rs b/src/clipboard.rs index 43844a6..f1f7e0f 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -1,23 +1,29 @@ // Copyright 2024 System76 // SPDX-License-Identifier: GPL-3.0-only -use cosmic::iced::clipboard::mime::AsMimeTypes; -use std::{borrow::Cow, path::Path}; +use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes}; +use std::{ + borrow::Cow, + error::Error, + path::{Path, PathBuf}, + str, +}; use url::Url; +#[derive(Clone, Copy, Debug)] pub enum ClipboardKind { Copy, Cut, } -pub struct ClipboardContents { +pub struct ClipboardCopy { pub available: Cow<'static, [String]>, pub text_plain: Cow<'static, [u8]>, pub text_uri_list: Cow<'static, [u8]>, pub x_special_gnome_copied_files: Cow<'static, [u8]>, } -impl ClipboardContents { +impl ClipboardCopy { pub fn new>(kind: ClipboardKind, paths: &[P]) -> Self { let available = vec![ "text/plain".to_string(), @@ -80,7 +86,7 @@ impl ClipboardContents { } } -impl AsMimeTypes for ClipboardContents { +impl AsMimeTypes for ClipboardCopy { fn available(&self) -> Cow<'static, [String]> { self.available.clone() } @@ -94,3 +100,60 @@ impl AsMimeTypes for ClipboardContents { } } } + +#[derive(Clone, Debug)] +pub struct ClipboardPaste { + pub kind: ClipboardKind, + pub paths: Vec, +} + +impl AllowedMimeTypes for ClipboardPaste { + fn allowed() -> Cow<'static, [String]> { + Cow::from(vec![ + "x-special/gnome-copied-files".to_string(), + "text/uri-list".to_string(), + ]) + } +} + +impl TryFrom<(Vec, String)> for ClipboardPaste { + type Error = Box; + fn try_from(value: (Vec, String)) -> Result { + let (data, mime) = value; + // Assume the kind is Copy if not provided by the mime type + let mut kind = ClipboardKind::Copy; + let mut paths = Vec::new(); + match mime.as_str() { + "text/uri-list" => { + let text = str::from_utf8(&data)?; + for line in text.lines() { + let url = Url::parse(line)?; + match url.to_file_path() { + Ok(path) => paths.push(path), + Err(()) => Err(format!("invalid file URL {:?}", url))?, + } + } + } + "x-special/gnome-copied-files" => { + let text = str::from_utf8(&data)?; + for (i, line) in text.lines().enumerate() { + if i == 0 { + kind = match line { + "copy" => ClipboardKind::Copy, + "cut" => ClipboardKind::Cut, + _ => Err(format!("unsupported clipboard operation {:?}", line))?, + }; + } else { + let url = Url::parse(line)?; + match url.to_file_path() { + Ok(path) => paths.push(path), + Err(()) => Err(format!("invalid file URL {:?}", url))?, + } + } + } + } + _ => Err(format!("unsupported mime type {:?}", mime))?, + } + Ok(Self { kind, paths }) + } +}