use super::base_helper::to_utf16;
use crate::controls::ControlHandle;
use winapi::um::winnt::HANDLE;
use winapi::um::winuser::{CF_BITMAP, CF_TEXT, CF_UNICODETEXT};
#[derive(Copy, Clone)]
pub enum ClipboardFormat {
Text,
UnicodeText,
Bitmap,
Global(&'static str),
}
impl ClipboardFormat {
fn into_raw(&self) -> u32 {
use ClipboardFormat::*;
use winapi::um::winuser::RegisterClipboardFormatW;
match self {
Text => CF_TEXT,
UnicodeText => CF_UNICODETEXT,
Bitmap => CF_BITMAP,
Global(v) => unsafe {
let v = to_utf16(v);
RegisterClipboardFormatW(v.as_ptr())
},
}
}
}
pub struct ClipboardData(HANDLE);
impl ClipboardData {
pub unsafe fn cast<D: Copy>(&self) -> *const D {
self.0 as *const D
}
pub fn release(self) {
}
}
impl Drop for ClipboardData {
fn drop(&mut self) {
unsafe {
::winapi::um::winbase::GlobalUnlock(self.0);
}
}
}
pub struct Clipboard;
impl Clipboard {
pub fn set_data_text<'a, C: Into<ControlHandle>>(handle: C, text: &'a str) {
use core::{mem, ptr};
use winapi::shared::basetsd::SIZE_T;
use winapi::um::stringapiset::MultiByteToWideChar;
use winapi::um::winbase::{
GMEM_MOVEABLE, GlobalAlloc, GlobalFree, GlobalLock, GlobalUnlock,
};
use winapi::um::winnls::CP_UTF8;
use winapi::um::winuser::SetClipboardData;
let size = unsafe {
MultiByteToWideChar(
CP_UTF8,
0,
text.as_ptr() as *const _,
text.len() as _,
ptr::null_mut(),
0,
)
};
if size == 0 {
return;
}
let alloc_size = (mem::size_of::<u16>() * (size as usize + 1)) as SIZE_T;
let alloc = unsafe { GlobalAlloc(GMEM_MOVEABLE, alloc_size) };
unsafe {
let locked_ptr = GlobalLock(alloc) as *mut u16;
assert!(!locked_ptr.is_null());
MultiByteToWideChar(
CP_UTF8,
0,
text.as_ptr() as *const _,
text.len() as _,
locked_ptr,
size,
);
ptr::write(locked_ptr.offset(size as isize), 0);
GlobalUnlock(alloc);
}
Clipboard::open(handle);
Clipboard::empty();
unsafe {
if SetClipboardData(CF_UNICODETEXT, alloc as _).is_null() {
GlobalFree(alloc);
}
}
Clipboard::close();
}
pub fn data_text<C: Into<ControlHandle>>(handle: C) -> Option<String> {
use ClipboardFormat::*;
let mut data = None;
Clipboard::open(handle);
unsafe {
if Clipboard::has_format(UnicodeText) {
let handle = Clipboard::data_handle(UnicodeText).unwrap();
data = from_wide_ptr(handle.cast());
handle.release();
} else if Clipboard::has_format(Text) {
let handle = Clipboard::data_handle(Text).unwrap();
data = from_ptr(handle.cast());
handle.release();
}
}
Clipboard::close();
data
}
pub fn clear<C: Into<ControlHandle>>() {
use std::ptr;
use winapi::um::winuser::{CloseClipboard, EmptyClipboard, OpenClipboard};
unsafe {
OpenClipboard(ptr::null_mut());
EmptyClipboard();
CloseClipboard();
}
}
pub fn open<C: Into<ControlHandle>>(handle: C) {
use winapi::um::winuser::OpenClipboard;
let handle = handle.into().hwnd().expect("Control should be a window");
unsafe {
OpenClipboard(handle);
}
}
pub fn set_data<D: Copy>(fmt: ClipboardFormat, data: *const D, count: usize) {
use std::{mem, ptr};
use winapi::shared::basetsd::SIZE_T;
use winapi::um::winbase::{
GMEM_MOVEABLE, GlobalAlloc, GlobalFree, GlobalLock, GlobalUnlock,
};
use winapi::um::winuser::SetClipboardData;
let fmt = fmt.into_raw();
let alloc_size = (mem::size_of::<D>() * count) as SIZE_T;
let alloc = unsafe { GlobalAlloc(GMEM_MOVEABLE, alloc_size) };
unsafe {
ptr::copy_nonoverlapping(data, GlobalLock(alloc) as *mut D, count);
}
unsafe {
GlobalUnlock(alloc);
}
unsafe {
if SetClipboardData(fmt, alloc as HANDLE).is_null() {
GlobalFree(alloc);
}
}
}
pub fn has_format(fmt: ClipboardFormat) -> bool {
use winapi::um::winuser::IsClipboardFormatAvailable;
let selected_format = fmt.into_raw();
unsafe { IsClipboardFormatAvailable(selected_format) != 0 }
}
pub fn data<D: Copy>(fmt: ClipboardFormat) -> Option<D> {
use std::{mem, ptr};
use winapi::um::winbase::{GlobalLock, GlobalUnlock};
use winapi::um::winuser::GetClipboardData;
let fmt = fmt.into_raw();
let handle = unsafe { GetClipboardData(fmt) };
if handle.is_null() {
return None;
}
let mut data = unsafe { mem::zeroed() };
unsafe {
ptr::copy_nonoverlapping(GlobalLock(handle) as *const D, &mut data, 1);
}
unsafe {
GlobalUnlock(handle);
}
Some(data)
}
pub fn data_handle(fmt: ClipboardFormat) -> Option<ClipboardData> {
use winapi::um::winbase::GlobalLock;
use winapi::um::winuser::GetClipboardData;
let fmt = fmt.into_raw();
let handle = unsafe { GetClipboardData(fmt) };
unsafe {
match handle.is_null() {
true => None,
false => Some(ClipboardData(GlobalLock(handle))),
}
}
}
pub fn count_clipboard_formats() -> u32 {
use winapi::um::winuser::CountClipboardFormats;
unsafe { CountClipboardFormats() as u32 }
}
pub fn empty() {
use winapi::um::winuser::EmptyClipboard;
unsafe {
EmptyClipboard();
}
}
pub fn close() {
use winapi::um::winuser::CloseClipboard;
unsafe {
CloseClipboard();
}
}
pub fn ownder() -> ControlHandle {
let handle = unsafe { ::winapi::um::winuser::GetClipboardOwner() };
ControlHandle::Hwnd(handle)
}
}
fn from_wide_ptr(ptr: *const u16) -> Option<String> {
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::slice::from_raw_parts;
let mut length: isize = 0;
unsafe {
while *&*ptr.offset(length) != 0 {
length += 1;
}
}
let array: &[u16] = unsafe { from_raw_parts(ptr, length as usize) };
OsString::from_wide(&array).into_string().ok()
}
fn from_ptr(ptr: *const u8) -> Option<String> {
use std::slice::from_raw_parts;
use std::str;
let mut length: isize = 0;
unsafe {
while *&*ptr.offset(length) != 0 {
length += 1;
}
}
let array: &[u8] = unsafe { from_raw_parts(ptr, length as usize) };
str::from_utf8(array).map(|s| s.into()).ok()
}