use crate::controls::ControlHandle;
use super::base_helper::{to_utf16};
use winapi::um::winuser::{CF_BITMAP, CF_TEXT, CF_UNICODETEXT};
use winapi::um::winnt::HANDLE;
#[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 winapi::um::winuser::SetClipboardData;
use winapi::um::stringapiset::MultiByteToWideChar;
use winapi::um::winnls::CP_UTF8;
use winapi::shared::basetsd::SIZE_T;
use winapi::um::winbase::{GlobalAlloc, GlobalLock, GlobalFree, GlobalUnlock, GMEM_MOVEABLE};
use core::{mem, ptr};
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 winapi::um::winuser::{OpenClipboard, EmptyClipboard, CloseClipboard};
use std::ptr;
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 unsafe fn set_data<D: Copy>(fmt: ClipboardFormat, data: *const D, count: usize) {
use winapi::um::winuser::SetClipboardData;
use winapi::um::winbase::{GlobalAlloc, GlobalLock, GlobalFree, GlobalUnlock, GMEM_MOVEABLE};
use winapi::shared::basetsd::SIZE_T;
use std::{mem, ptr};
let fmt = fmt.into_raw();
let alloc_size = (mem::size_of::<D>() * count) as SIZE_T;
let alloc = GlobalAlloc(GMEM_MOVEABLE, alloc_size);
ptr::copy_nonoverlapping(data, GlobalLock(alloc) as *mut D, count);
GlobalUnlock(alloc);
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 unsafe fn data<D: Copy>(fmt: ClipboardFormat) -> Option<D> {
use winapi::um::winuser::GetClipboardData;
use winapi::um::winbase::{GlobalLock, GlobalUnlock};
use std::{ptr, mem};
let fmt = fmt.into_raw();
let handle = GetClipboardData(fmt);
if handle.is_null() {
return None;
}
let mut data = mem::zeroed();
ptr::copy_nonoverlapping(GlobalLock(handle) as *const D, &mut data, 1);
GlobalUnlock(handle);
Some(data)
}
pub unsafe fn data_handle(fmt: ClipboardFormat) -> Option<ClipboardData> {
use winapi::um::winuser::GetClipboardData;
use winapi::um::winbase::GlobalLock;
let fmt = fmt.into_raw();
let handle = GetClipboardData(fmt);
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)
}
}
unsafe fn from_wide_ptr(ptr: *const u16) -> Option<String> {
use std::slice::from_raw_parts;
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
let mut length: isize = 0;
while *&*ptr.offset(length) != 0 {
length += 1;
}
let array: &[u16] = from_raw_parts(ptr, length as usize);
OsString::from_wide(&array)
.into_string()
.ok()
}
unsafe fn from_ptr(ptr: *const u8) -> Option<String> {
use std::slice::from_raw_parts;
use std::str;
let mut length: isize = 0;
while *&*ptr.offset(length) != 0 {
length += 1;
}
let array: &[u8] = from_raw_parts(ptr, length as usize);
str::from_utf8(array)
.map(|s| s.into())
.ok()
}