ultimatepp/uppsrc/CtrlCore/Win32Clip.cpp
Mirek Fidler 34ff691308 sizeof(wchar) is changed to 4 (32 bits) to support non BMP unicode characters
This might bring some incompatibilities in the code that expects wchar to be 16 bit, which
  escpecially involves dealing with Win32 (and to lesser extend MacOS) APIs, so if your application
  is doing that, please check all instances of WCHAR (UniChar on MacOS) or even wchar
  especially type casts.

  To support host APIs, char16 is introduced (but there is no 16-bit String varian).

  Use ToSystemCharsetW, FromSystemCharsetW to convert texts to Win32 API.

- Support of drawing non-BMP characters in GUI
- Vastly improved character font replacement code (when drawing characters missing with requested font, replacement font is used)
- Last instances of Win32 ANSI calls (those ending with A) are removed
- UTF handling routines are refactored and their's naming is unified
- RTF is now being able to handle non-BMP characters (RTF is used as clipboard format for RichText)

Other minor changes:

- fixed TryRealloc issue
- improved MemoryCheck
- Removed MemoryAlloc48/MemoryFree48
- In theide Background parsing should less often cause delays in the main thread
2021-12-02 12:03:19 +01:00

546 lines
11 KiB
C++

#include "CtrlCore.h"
#include <plugin/bmp/bmp.h>
#ifdef GUI_WIN
namespace Upp {
#define LLOG(x) // LOG(x)
VectorMap<int, ClipData>& sClipMap()
{
static VectorMap<int, ClipData> x;
return x;
}
extern HWND utilityHWND;
int GetClipboardFormatCode(const char *format_id)
{
GuiLock ___;
int x = (int)(intptr_t)format_id;
if(x >= 0 && x < 65535)
return x;
String fmt = format_id;
if(fmt == "text")
return CF_TEXT;
if(fmt == "wtext")
return CF_UNICODETEXT;
if(fmt == "dib")
return CF_DIB;
if(fmt == "files")
return CF_HDROP;
static StaticMutex m;
Mutex::Lock __(m);
static VectorMap<String, int> format_map;
int f = format_map.Find(format_id);
if(f < 0) {
f = format_map.GetCount();
format_map.Add(format_id,
#ifdef PLATFORM_WINCE
::RegisterClipboardFormat(ToSystemCharset(format_id))
#else
::RegisterClipboardFormat(format_id)
#endif
);
}
return format_map[f];
}
bool DebugClipboard()
{
static bool b = GetIniKey("DebugClipboard") == "1";
return b;
}
void ClipboardLog(const char *txt)
{
if(!DebugClipboard())
return;
FileAppend f(GetExeDirFile("clip.log"));
f << GetSysTime() << ": " << txt << "\n";
}
void ClipboardError(const char *txt)
{
if(!DebugClipboard())
return;
String s = txt;
s << "\n" << GetLastErrorMessage();
MessageBox(::GetActiveWindow(), s, "Clipboard error", MB_ICONSTOP | MB_OK | MB_APPLMODAL);
ClipboardLog(String().Cat() << s << " ERROR");
}
String FromWin32CF(int cf);
void ClipboardError(const char *txt, int format)
{
if(!DebugClipboard())
return;
ClipboardError(String().Cat() << txt << ' ' << FromWin32CF(format));
}
bool ClipboardOpen()
{
// Win32 has serious race condition problem with clipboard; system or other apps open it
// right after we close it thus blocking us to send more formats
// So the solution is to wait and retry... (mirek, 2011-01-09)
for(int i = 0; i < 200; i++) {
if(OpenClipboard(utilityHWND)) {
ClipboardLog("----- ClipboardOpen OK");
return true;
}
Sleep(10);
}
ClipboardError("ClipboardOpen has failed!");
return false;
}
void ClearClipboard()
{
GuiLock __;
sClipMap().Clear();
ClipboardLog("* ClearClipboard");
if(ClipboardOpen()) {
if(!EmptyClipboard())
ClipboardError("EmptyClipboard ERROR");
if(!CloseClipboard())
ClipboardError("CloseClipboard ERROR");
}
}
void SetClipboardRaw(int format, const byte *data, int length)
{
GuiLock __;
HANDLE handle = NULL;
ClipboardLog("* SetClipboardRaw");
if(data) {
handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, length + 2);
byte *ptr;
if(!handle) {
ClipboardError("GlobalAlloc ERROR");
return;
}
if(!(ptr = (byte *)GlobalLock(handle))) {
ClipboardError("GlobalLock ERROR");
GlobalFree(handle);
return;
}
memcpy(ptr, data, length);
ptr[length] = 0;
ptr[length + 1] = 0;
GlobalUnlock(handle);
}
if(SetClipboardData(format, handle) != handle) {
ClipboardError("SetCliboardData", format);
LLOG("SetClipboardData error: " << GetLastErrorMessage());
GlobalFree(handle);
}
}
void AppendClipboard(int format, const byte *data, int length)
{
GuiLock __;
ClipboardLog("* AppendClipboard");
if(ClipboardOpen()) {
SetClipboardRaw(format, data, length);
if(!CloseClipboard())
ClipboardError("CloseClipboard", format);
}
}
void AppendClipboard(const char *format, const byte *data, int length)
{
GuiLock __;
Vector<String> f = Split(format, ';');
for(int i = 0; i < f.GetCount(); i++)
AppendClipboard(GetClipboardFormatCode(f[i]), data, length);
}
void AppendClipboard(const char *format, const String& data)
{
GuiLock __;
AppendClipboard(format, data, data.GetLength());
}
void AppendClipboard(const char *format, const Value& data, String (*render)(const Value&))
{
GuiLock __;
Vector<String> f = Split(format, ';');
for(int i = 0; i < f.GetCount(); i++) {
int c = GetClipboardFormatCode(f[i]);
sClipMap().GetAdd(c) = ClipData(data, render);
AppendClipboard(c, NULL, 0);
}
}
void Ctrl::RenderFormat(int format)
{
GuiLock __;
int q = sClipMap().Find(format);
if(q >= 0) {
String s = sClipMap()[q].Render();
SetClipboardRaw(format, s, s.GetLength());
}
}
void Ctrl::RenderAllFormats()
{
GuiLock __;
if(sClipMap().GetCount() && OpenClipboard(utilityHWND)) {
for(int i = 0; i < sClipMap().GetCount(); i++)
RenderFormat(sClipMap().GetKey(i));
CloseClipboard();
}
}
void Ctrl::DestroyClipboard()
{
GuiLock __;
sClipMap().Clear();
}
String ReadClipboard(const char *format)
{
GuiLock __;
if(!ClipboardOpen())
return Null;
HGLOBAL hmem = GetClipboardData(GetClipboardFormatCode(format));
if(hmem == 0) {
ClipboardError("GetClipboardData failed");
CloseClipboard();
return Null;
}
const byte *src = (const byte *)GlobalLock(hmem);
ASSERT(src);
int length = (int)GlobalSize(hmem);
if(length < 0) {
ClipboardError("ReadCliboard length < 0");
CloseClipboard();
return Null;
}
String out(src, length);
GlobalUnlock(hmem);
CloseClipboard();
return out;
}
void AppendClipboardText(const String& s)
{
AppendClipboardUnicodeText(s.ToWString());
}
void AppendClipboardUnicodeText(const WString& s)
{
Vector<char16> ws = ToUtf16(s);
AppendClipboard("wtext", (const byte *)ws.begin(), 2 * ws.GetCount());
}
const char *ClipFmtsText()
{
return "wtext;text";
}
String GetString(PasteClip& clip)
{
GuiLock __;
if(clip.Accept("wtext")) {
String s = ~clip;
return ToUtf8((const char16 *)~s, strlen16((const char16 *)~s));
}
if(clip.Accept("text"))
return ~clip;
return Null;
}
WString GetWString(PasteClip& clip)
{
GuiLock __;
if(clip.Accept("wtext")) {
String s = ~clip;
return ToUtf32((const char16 *)~s, strlen16((const char16 *)~s));
}
if(clip.Accept("text"))
return (~clip).ToWString();
return Null;
}
bool AcceptText(PasteClip& clip)
{
return clip.Accept(ClipFmtsText());
}
static String sText(const Value& data)
{
return data;
}
static String sWText(const Value& data)
{
return Unicode__(WString(data));
}
void Append(VectorMap<String, ClipData>& data, const String& text)
{
data.GetAdd("text", ClipData(text, sText));
data.GetAdd("wtext", ClipData(text, sWText));
}
void Append(VectorMap<String, ClipData>& data, const WString& text)
{
data.GetAdd("text", ClipData(text, sText));
data.GetAdd("wtext", ClipData(text, sWText));
}
String GetTextClip(const WString& text, const String& fmt)
{
if(fmt == "text")
return text.ToString();
if(fmt == "wtext")
return Unicode__(text);
return Null;
}
String GetTextClip(const String& text, const String& fmt)
{
if(fmt == "text")
return text;
if(fmt == "wtext")
return Unicode__(text.ToWString());
return Null;
}
String ReadClipboardText()
{
#ifdef PLATFORM_WINCE
return ReadClipboardUnicodeText().ToString();
#else
String s = ReadClipboardUnicodeText().ToString();
if(s.GetCount())
return s;
s = ReadClipboard((const char *)CF_TEXT);
return String(s, (int)strlen(~s));
#endif
}
WString ReadClipboardUnicodeText()
{
String s = ReadClipboard((const char *)CF_UNICODETEXT);
return ToUtf32((const char16 *)~s, strlen16((const char16 *)~s));
}
bool IsClipboardAvailable(const char *id)
{
return ::IsClipboardFormatAvailable(GetClipboardFormatCode(id));
}
bool IsClipboardAvailableText()
{
return IsClipboardAvailable((const char *)CF_TEXT);
}
const char *ClipFmtsImage()
{
static const char *q;
ONCELOCK {
static String s = "dib;" + ClipFmt<Image>();
q = s;
}
return q;
}
bool AcceptImage(PasteClip& clip)
{
GuiLock __;
return clip.Accept(ClipFmtsImage());
}
Image GetImage(PasteClip& clip)
{
GuiLock __;
Image m;
if(Accept<Image>(clip)) {
LoadFromString(m, ~clip);
if(!m.IsEmpty())
return m;
}
if(clip.Accept("dib")) {
String data = ~clip;
if((unsigned)data.GetCount() < sizeof(BITMAPINFO)) return Null;
BITMAPINFO *lpBI = (BITMAPINFO *)~data;
BITMAPINFOHEADER& hdr = lpBI->bmiHeader;
byte *bits = (byte *)lpBI + hdr.biSize;
if(hdr.biBitCount <= 8)
bits += (hdr.biClrUsed ? hdr.biClrUsed : 1 << hdr.biBitCount) * sizeof(RGBQUAD);
if(hdr.biBitCount >= 16 || hdr.biBitCount == 32) {
if(hdr.biCompression == 3)
bits += 12;
if(hdr.biClrUsed != 0)
bits += hdr.biClrUsed * sizeof(RGBQUAD);
}
int h = abs((int)hdr.biHeight);
ImageDraw iw(hdr.biWidth, h);
::StretchDIBits(iw.GetHandle(),
0, 0, hdr.biWidth, h,
0, 0, hdr.biWidth, h,
bits, lpBI, DIB_RGB_COLORS, SRCCOPY);
return iw;
}
return Null;
}
Image ReadClipboardImage()
{
GuiLock __;
PasteClip d = Ctrl::Clipboard();
return GetImage(d);
}
String sDib(const Value& image)
{
Image img = image;
BITMAPINFOHEADER header;
Zero(header);
header.biSize = sizeof(header);
header.biWidth = img.GetWidth();
header.biHeight = -img.GetHeight();
header.biBitCount = 32;
header.biPlanes = 1;
header.biCompression = BI_RGB;
StringBuffer b(sizeof(header) + 4 * img.GetLength());
byte *p = (byte *)~b;
memcpy(p, &header, sizeof(header));
memcpy(p + sizeof(header), ~img, 4 * img.GetLength());
return String(b);
}
String sImage(const Value& image)
{
Image img = image;
return StoreAsString(const_cast<Image&>(img));
}
String GetImageClip(const Image& img, const String& fmt)
{
GuiLock __;
if(img.IsEmpty()) return Null;
if(fmt == "dib")
return sDib(img);
if(fmt == ClipFmt<Image>())
return sImage(img);
return Null;
}
void AppendClipboardImage(const Image& img)
{
GuiLock __;
if(img.IsEmpty()) return;
AppendClipboard(ClipFmt<Image>(), img, sImage);
AppendClipboard("dib", img, sDib);
}
void Append(VectorMap<String, ClipData>& data, const Image& img)
{
data.Add(ClipFmt<Image>(), ClipData(img, sImage));
data.Add("dib", ClipData(img, sDib));
}
bool AcceptFiles(PasteClip& clip)
{
if(clip.Accept("files")) {
clip.SetAction(DND_COPY);
return true;
}
return false;
}
bool IsAvailableFiles(PasteClip& clip)
{
return clip.IsAvailable("files");
}
struct sDROPFILES {
DWORD offset;
POINT pt;
BOOL nc;
BOOL unicode;
};
Vector<String> GetClipFiles(const String& data)
{
GuiLock __;
Vector<String> f;
if((unsigned)data.GetCount() < sizeof(sDROPFILES) + 2)
return f;
const sDROPFILES *df = (const sDROPFILES *)~data;
const char *s = ((const char *)df + df->offset);
if(df->unicode) {
const char16 *ws = (char16 *)s;
while(*ws) {
WString fn;
while(*ws)
fn.Cat(*ws++);
f.Add(fn.ToString());
ws++;
}
}
else
while(*s) {
String fn;
while(*s)
fn.Cat(*s++);
f.Add(fn.ToString());
s++;
}
return f;
}
Vector<String> GetFiles(PasteClip& clip)
{
GuiLock __;
Vector<String> f;
return GetClipFiles(clip.Get("files"));
}
void AppendFiles(VectorMap<String, ClipData>& clip, const Vector<String>& files)
{
WString wfiles;
for(int i = 0; i < files.GetCount(); i++)
wfiles << files[i].ToWString() << (wchar)0;
sDROPFILES h;
h.unicode = true;
h.offset = sizeof(h);
GetCursorPos(&h.pt);
h.nc = TRUE;
String data;
data.Cat((byte *)&h, sizeof(h));
data.Cat((byte *)~wfiles, 2 * (wfiles.GetCount() + 1));
clip.GetAdd("files") = ClipData(data);
}
bool Has(UDropTarget *dt, const char *fmt);
String Get(UDropTarget *dt, const char *fmt);
bool PasteClip::IsAvailable(const char *fmt) const
{
if(this == &Ctrl::Selection())
return false;
return dt ? UPP::Has(dt, fmt) : IsClipboardAvailable(fmt);
}
String PasteClip::Get(const char *fmt) const
{
if(this == &Ctrl::Selection())
return Null;
return dt ? UPP::Get(dt, fmt) : ReadClipboard(fmt);
}
void PasteClip::GuiPlatformConstruct()
{
dt = NULL;
}
}
#endif