295 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			295 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  *	Data Exchanger Implementation
 | |
|  *	Copyright(C) 2003-2017 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 <nana/paint/pixel_buffer.hpp>
 | |
| #include <vector>
 | |
| #include <cassert>
 | |
| #include <cstring>
 | |
| 
 | |
| #if defined(NANA_WINDOWS)
 | |
| #	include <windows.h>
 | |
| #elif defined(NANA_X11)
 | |
| #	include "../detail/platform_spec_selector.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 std::string& text, native_window_type owner)
 | |
| 		{
 | |
| #ifdef NANA_WINDOWS
 | |
| 			std::wstring wstr = ::nana::charset(text, nana::unicode::utf8);
 | |
| 			_m_set(format::text, wstr.c_str(), (wstr.length() + 1) * sizeof(wchar_t), owner);
 | |
| #elif defined(NANA_X11)
 | |
| 			_m_set(format::text, text.c_str(), text.length() + 1, owner);
 | |
| #endif
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		void dataexch::set(const std::wstring& text, native_window_type owner)
 | |
| 		{
 | |
| #ifdef NANA_WINDOWS
 | |
| 			_m_set(format::text, text.c_str(), (text.length() + 1) * sizeof(wchar_t), owner);
 | |
| #else
 | |
| 			std::string str = to_utf8(text);
 | |
| 			_m_set(format::text, str.c_str(), str.size() + 1, owner);
 | |
| #endif
 | |
| 		}
 | |
| 
 | |
| 		bool dataexch::set(const nana::paint::graphics& g, native_window_type owner)
 | |
| 		{
 | |
| #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);
 | |
| 				::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)
 | |
| 			{
 | |
| 				char* p = (char*)gmem;
 | |
| 				// Fix BITMAPINFOHEADER obtained from GetDIBits WinAPI
 | |
| 				bmi.bmiHeader.biCompression = BI_RGB;
 | |
| 				bmi.bmiHeader.biHeight = ::abs(bmi.bmiHeader.biHeight);
 | |
| 				std::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)
 | |
| 					if (::OpenClipboard(reinterpret_cast<HWND>(owner)))
 | |
| 					{
 | |
| 						if (::EmptyClipboard())
 | |
| 							if (::SetClipboardData(CF_DIB, h_gmem))
 | |
| 								if (::CloseClipboard())
 | |
| 									return true;
 | |
| 						::CloseClipboard();
 | |
| 					}
 | |
| 			}
 | |
| 			assert(false);
 | |
| 			::GlobalFree(h_gmem);
 | |
| 			return false;
 | |
| 
 | |
| //#elif defined(NANA_X11)
 | |
| #else
 | |
| 			static_cast<void>(g); //eliminate unused parameter compiler warning.
 | |
| 			static_cast<void>(owner);
 | |
| 			throw std::logic_error("dataexch::set(const paint::graphics&, native_window_type owner) not implemented yet.");
 | |
| 
 | |
| 			return false;
 | |
| #endif
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		void dataexch::get(std::string& text_utf8)
 | |
| 		{
 | |
| 			std::size_t size;
 | |
| 			auto res = _m_get(format::text, size);
 | |
| 			if (res)
 | |
| 			{
 | |
| #if defined(NANA_WINDOWS)
 | |
| 				size /= sizeof(wchar_t);
 | |
| 				std::wstring wstr;
 | |
| 				wstr.reserve(size);
 | |
| 				wstr.append(reinterpret_cast<wchar_t*>(res), reinterpret_cast<wchar_t*>(res) + size);
 | |
| 
 | |
| 				auto pos = wstr.find_last_not_of(L'\0');
 | |
| 				if (pos != wstr.npos)
 | |
| 					wstr.erase(pos + 1);
 | |
| 
 | |
| 				text_utf8 = to_utf8(wstr);
 | |
| #else
 | |
| 				text_utf8.reserve(size);
 | |
| 				text_utf8.append(reinterpret_cast<char*>(res), reinterpret_cast<char*>(res) + size);
 | |
| 
 | |
| 				auto pos = text_utf8.find_last_not_of('\0');
 | |
| 				if (pos != text_utf8.npos)
 | |
| 					text_utf8.erase(pos + 1);
 | |
| 				::XFree(res);
 | |
| #endif
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		void dataexch::get(std::wstring& str)
 | |
| 		{
 | |
| 			std::size_t size;
 | |
| 			auto res = _m_get(format::text, size);
 | |
| 			if(res)
 | |
| 			{
 | |
| #if defined(NANA_WINDOWS)
 | |
| 				str.clear();
 | |
| 
 | |
| 				size /= sizeof(wchar_t);
 | |
| 
 | |
| 				str.reserve(size);
 | |
| 				str.append(reinterpret_cast<wchar_t*>(res), reinterpret_cast<wchar_t*>(res) + size);
 | |
| 
 | |
| 				auto pos = str.find_last_not_of(L'\0');
 | |
| 				if (pos != str.npos)
 | |
| 					str.erase(pos + 1);
 | |
| #else
 | |
| 				std::string text_utf8;
 | |
| 				text_utf8.reserve(size);
 | |
| 				text_utf8.append(reinterpret_cast<char*>(res), reinterpret_cast<char*>(res) + size);
 | |
| 
 | |
| 				auto pos = text_utf8.find_last_not_of('\0');
 | |
| 				if (pos != text_utf8.npos)
 | |
| 					text_utf8.erase(pos + 1);
 | |
| 				::XFree(res);
 | |
| 
 | |
| 				str = to_wstring(text_utf8);
 | |
| #endif
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		std::wstring dataexch::wget()
 | |
| 		{
 | |
| 			std::wstring str;
 | |
| 			this->get(str);
 | |
| 			return str;
 | |
| 		}
 | |
| 
 | |
| 	//private:
 | |
| 		bool dataexch::_m_set(format fmt, const void* buf, std::size_t size, native_window_type owner)
 | |
| 		{
 | |
| 			bool res = false;
 | |
| 
 | |
| #if defined(NANA_WINDOWS)
 | |
| 			if(::OpenClipboard(reinterpret_cast<HWND>(owner)))
 | |
| 			{
 | |
| 				if(::EmptyClipboard())
 | |
| 				{
 | |
| 					HGLOBAL g = ::GlobalAlloc(GHND | GMEM_SHARE, size);
 | |
| 					void * addr = ::GlobalLock(g);
 | |
| 
 | |
| 					memcpy(addr, buf, size);
 | |
| 					::GlobalUnlock(g);
 | |
| 
 | |
| 					unsigned data_format;
 | |
| 					switch(fmt)
 | |
| 					{
 | |
| 					case format::text:		data_format = CF_UNICODETEXT;	break;
 | |
| 					case format::pixmap:	data_format = CF_BITMAP;		break;
 | |
| 					}
 | |
| 					
 | |
| 					res = (nullptr != ::SetClipboardData(data_format, g));
 | |
| 				}
 | |
| 				::CloseClipboard();
 | |
| 			}
 | |
| #elif defined(NANA_X11)
 | |
| 			auto & spec = ::nana::detail::platform_spec::instance();
 | |
| 			
 | |
| 			Atom atom_type;
 | |
| 			switch(fmt)
 | |
| 			{
 | |
| 			case format::text:	atom_type = spec.atombase().utf8_string;	break;
 | |
| 			default:
 | |
| 				return false;
 | |
| 			}
 | |
| 
 | |
| 			spec.write_selection(owner, atom_type, buf, size);
 | |
| 			return true;
 | |
| 			
 | |
| #endif
 | |
| 			return res;
 | |
| 		}
 | |
| 
 | |
| 		void* dataexch::_m_get(format fmt, size_t& size)
 | |
| 		{
 | |
| 			size = 0;
 | |
| 			void* res = nullptr;
 | |
| #if defined(NANA_WINDOWS)
 | |
| 			if(::OpenClipboard(::GetFocus()))
 | |
| 			{
 | |
| 				unsigned data_format;
 | |
| 				switch(fmt)
 | |
| 				{
 | |
| 				case format::text:		data_format = CF_UNICODETEXT;	break;
 | |
| 				case format::pixmap:	data_format = CF_BITMAP;	break;
 | |
| 				}
 | |
| 				HANDLE handle = ::GetClipboardData(data_format);
 | |
| 				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(fmt)
 | |
| 				{
 | |
| 				case format::text:	atom = spec.atombase().utf8_string;	break;
 | |
| 				default:
 | |
| 					return nullptr;
 | |
| 				}
 | |
| 				res = spec.request_selection(requester, atom, size);
 | |
| 			}
 | |
| #endif
 | |
| 			return res;
 | |
| 		}
 | |
| 	//end class dataexch
 | |
| 
 | |
| }//end namespace system
 | |
| }//end namespace nana
 | |
| 
 | 
