nana/source/system/dataexch.cpp

258 lines
7.0 KiB
C++

/*
* Data Exchanger Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/system/dataexch.cpp
* @description: An implementation of a data exchange mechanism through Windows Clipboard, X11 Selection.
*/
#include <nana/system/dataexch.hpp>
#include <nana/traits.hpp>
#include <nana/paint/graphics.hpp>
#include <vector>
#include <cassert>
#if defined(NANA_WINDOWS)
#include <windows.h>
#elif defined(NANA_X11)
#include PLATFORM_SPEC_HPP
#include <nana/gui/detail/bedrock.hpp>
#include <nana/gui/detail/basic_window.hpp>
#endif
namespace nana{ namespace system{
//class dataexch
void dataexch::set(const nana::char_t* text)
{
_m_set(std::is_same<char, nana::char_t>::value ? format::text : format::unicode, text, (nana::strlen(text) + 1) * sizeof(nana::char_t));
}
void dataexch::set(const nana::string& text)
{
_m_set(std::is_same<char, nana::char_t>::value ? format::text : format::unicode, text.c_str(), (text.length() + 1) * sizeof(nana::char_t));
}
bool dataexch::set(const nana::paint::graphics& g)
{
#if defined(NANA_WINDOWS)
size sz = g.size();
paint::pixel_buffer pbuffer;
rectangle r;
r.x = 0;
r.y = 0;
r.width = sz.width;
r.height = sz.height;
pbuffer.attach(g.handle(), r);
size_t bytes_per_line = pbuffer.bytes_per_line();
size_t bitmap_bytes = bytes_per_line * r.height;
struct {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[256];
} bmi = {0};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
HDC hDC = ::GetDC(NULL);
if (::GetDIBits(hDC, (HBITMAP)g.pixmap(), 0, 1, NULL, (BITMAPINFO *)&bmi, DIB_RGB_COLORS) == 0) {
assert(false);
int err = ::GetLastError();
::ReleaseDC(NULL, hDC);
return false;
}
if (!::ReleaseDC(NULL, hDC)) {
return false;
}
size_t header_size = sizeof(bmi.bmiHeader);
// Bitmaps are huge, so to avoid unnegligible extra copy, this routine does not use private _m_set method.
HGLOBAL h_gmem = ::GlobalAlloc(GHND | GMEM_SHARE, header_size + bitmap_bytes);
void * gmem = ::GlobalLock(h_gmem);
if (!gmem) {
assert(false);
goto Label_GlobalFree;
}
char* p = (char*)gmem;
// Fix BITMAPINFOHEADER obtained from GetDIBits WinAPI
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biHeight = ::abs(bmi.bmiHeader.biHeight);
memcpy(p, &bmi, header_size);
p += header_size;
// many programs do not support bottom-up DIB, so reversing row order is needed.
for (int y=0; y<bmi.bmiHeader.biHeight; ++y) {
memcpy(p, pbuffer.raw_ptr(bmi.bmiHeader.biHeight - 1 - y), bytes_per_line);
p += bytes_per_line;
}
if (!::GlobalUnlock(h_gmem) && GetLastError() != NO_ERROR) {
assert(false);
goto Label_GlobalFree;
}
if (!::OpenClipboard(::GetFocus())) {
goto Label_GlobalFree;
}
if (!::EmptyClipboard()) {
goto Label_GlobalFree;
}
if (!::SetClipboardData(CF_DIB, h_gmem)) {
goto Label_GlobalFree;
}
if (!::CloseClipboard()) {
// really?
return false;
}
return true;
Label_GlobalFree:
::GlobalFree(h_gmem);
return false;
//#elif defined(NANA_X11)
#else
throw "not implemented yet.";
return false;
#endif
}
void dataexch::get(nana::string& str)
{
std::size_t size;
void* res = _m_get(std::is_same<char, nana::char_t>::value ? format::text : format::unicode, size);
if(res)
{
#if defined(NANA_X11) && defined(NANA_UNICODE)
nana::detail::charset_conv conv("UTF-32", "UTF-8");
const std::string & utf32str = conv.charset(reinterpret_cast<char*>(res), size);
const nana::char_t * utf32ptr = reinterpret_cast<const nana::char_t*>(utf32str.c_str());
str.append(utf32ptr + 1, utf32ptr + utf32str.size() / sizeof(nana::char_t));
#else
str.reserve(size / sizeof(nana::char_t));
str.append(reinterpret_cast<nana::char_t*>(res), reinterpret_cast<nana::char_t*>(res) + size / sizeof(nana::char_t));
nana::string::size_type pos = str.find_last_not_of(nana::char_t(0));
if(pos != str.npos)
str.erase(pos + 1);
#endif
#if defined(NANA_X11)
::XFree(res);
#endif
}
}
//private:
bool dataexch::_m_set(unsigned type, const void* buf, std::size_t size)
{
bool res = false;
#if defined(NANA_WINDOWS)
if(type < format::end && ::OpenClipboard(::GetFocus()))
{
if(::EmptyClipboard())
{
HGLOBAL g = ::GlobalAlloc(GHND | GMEM_SHARE, size);
void * addr = ::GlobalLock(g);
memcpy(addr, buf, size);
::GlobalUnlock(g);
switch(type)
{
case format::text: type = CF_TEXT; break;
case format::unicode: type = CF_UNICODETEXT; break;
case format::pixmap: type = CF_BITMAP; break;
}
HANDLE h = ::SetClipboardData(type, g);
res = (h != NULL);
}
::CloseClipboard();
}
#elif defined(NANA_X11)
auto & spec = ::nana::detail::platform_spec::instance();
native_window_type owner = nullptr;
{
internal_scope_guard lock;
auto wd = detail::bedrock::instance().focus();
if(wd) owner = wd->root;
}
if(owner)
{
Atom atom_type;
switch(type)
{
case format::text: atom_type = XA_STRING; break;
case format::unicode: atom_type = spec.atombase().utf8_string; break;
default:
return false;
}
#if defined(NANA_UNICODE)
//The internal clipboard stores UTF8_STRING, the parameter string should be converted from utf32 to utf8.
nana::detail::charset_conv conv("UTF-8", "UTF-32");
std::string utf8str = conv.charset(reinterpret_cast<const char*>(buf), size);
buf = utf8str.c_str();
size = utf8str.size();
#endif
spec.write_selection(owner, atom_type, buf, size);
return true;
}
#endif
return res;
}
void* dataexch::_m_get(unsigned type, size_t& size)
{
void* res = 0;
#if defined(NANA_WINDOWS)
if(type < format::end && ::OpenClipboard(::GetFocus()))
{
switch(type)
{
case format::text: type = CF_TEXT; break;
case format::unicode: type = CF_UNICODETEXT; break;
case format::pixmap: type = CF_BITMAP; break;
}
HANDLE handle = ::GetClipboardData(type);
if(handle)
{
res = reinterpret_cast<HGLOBAL>(::GlobalLock(handle));
if(res)
size = ::GlobalSize(handle);
}
::CloseClipboard();
}
#elif defined(NANA_X11)
nana::detail::platform_spec & spec = nana::detail::platform_spec::instance();
native_window_type requester = nullptr;
spec.lock_xlib();
{
internal_scope_guard isg;
detail::bedrock::core_window_t * wd = detail::bedrock::instance().focus();
if(wd) requester = wd->root;
}
spec.unlock_xlib();
if(requester)
{
Atom atom;
switch(type)
{
case format::text: atom = XA_STRING; break;
case format::unicode: atom = spec.atombase().utf8_string; break;
default:
return 0;
}
res = spec.request_selection(requester, atom, size);
}
#endif
return res;
}
//end class dataexch
}//end namespace system
}//end namespace nana