ultimatepp/uppsrc/CtrlCore/Win32Proc.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

609 lines
No EOL
17 KiB
C++

#include "CtrlCore.h"
#ifdef GUI_WIN
#include <winnls.h>
//#include "imm.h"
namespace Upp {
#define LLOG(x) // DLOG(x)
dword Ctrl::KEYtoK(dword chr) {
if(chr == VK_TAB)
chr = K_TAB;
else
if(chr == VK_SPACE)
chr = K_SPACE;
else
if(chr == VK_RETURN)
chr = K_RETURN;
else
chr = chr + K_DELTA;
if(chr == K_ALT_KEY || chr == K_CTRL_KEY || chr == K_SHIFT_KEY)
return chr;
if(GetCtrl()) chr |= K_CTRL;
if(GetAlt()) chr |= K_ALT;
if(GetShift()) chr |= K_SHIFT;
return chr;
}
class NilDrawFull : public NilDraw {
virtual bool IsPaintingOp(const Rect& r) const { return true; }
};
void AvoidPaintingCheck__()
{
Ctrl::painting = false;
}
dword GetKeyStateSafe(dword what) {
bool h = Ctrl::painting;
Ctrl::painting = false;
dword r = GetKeyState(what);
Ctrl::painting = h;
return r;
}
static bool pendown=false;
bool GetShift() { return !!(GetKeyStateSafe(VK_SHIFT) & 0x8000); }
bool GetCtrl() { return !!(GetKeyStateSafe(VK_CONTROL) & 0x8000); }
bool GetAlt() { return !!(GetKeyStateSafe(VK_MENU) & 0x8000); }
bool GetCapsLock() { return !!(GetKeyStateSafe(VK_CAPITAL) & 1); }
bool GetMouseLeft() { return pendown || !!(GetKeyStateSafe(VK_LBUTTON) & 0x8000); }
bool GetMouseRight() { return Ctrl::GetPenInfo().barrel || !!(GetKeyStateSafe(VK_RBUTTON) & 0x8000); }
bool GetMouseMiddle() { return !!(GetKeyStateSafe(VK_MBUTTON) & 0x8000); }
Point Ctrl::CurrentMousePos;
Point GetMousePos() {
return Ctrl::CurrentMousePos;
}
bool PassWindowsKey(int wParam);
LRESULT Ctrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) {
GuiLock __;
eventid++;
// LLOG("Ctrl::WindowProc(" << message << ") in " << ::Name(this) << ", focus " << (void *)::GetFocus());
Ptr<Ctrl> _this = this;
HWND hwnd = GetHWND();
is_pen_event = (GetMessageExtraInfo() & 0xFFFFFF00) == 0xFF515700;
POINT p;
if(::GetCursorPos(&p))
CurrentMousePos = p;
auto MousePos = [&] {
Point p = Point((dword)lParam);
CurrentMousePos = p;
::ClientToScreen(hwnd, CurrentMousePos);
return p;
};
switch(message) {
case WM_POINTERDOWN:
case WM_POINTERUPDATE:
case WM_POINTERUP: {
POINT p = Point((LONG)lParam);
CurrentMousePos = p;
ScreenToClient(hwnd, &p);
pen.action = 0;
pen.pressure = pen.rotation = Null;
pen.tilt = Null;
pen.eraser = pen.barrel = pen.inverted = pen.history = false;
static BOOL (WINAPI *GetPointerType)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType);
static BOOL (WINAPI *GetPointerInfo)(UINT32 pointerId, POINTER_INFO *pointerInfo);
static BOOL (WINAPI *GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo);
static BOOL (WINAPI *GetPointerPenInfoHistory)(UINT32 pointerId, UINT32 *entriesCount, POINTER_PEN_INFO *penInfo);
ONCELOCK {
DllFn(GetPointerType, "User32.dll", "GetPointerType");
DllFn(GetPointerInfo, "User32.dll", "GetPointerInfo");
DllFn(GetPointerPenInfo, "User32.dll", "GetPointerPenInfo");
DllFn(GetPointerPenInfoHistory, "User32.dll", "GetPointerPenInfoHistory");
};
if(!(GetPointerType && GetPointerInfo && GetPointerPenInfo && GetPointerPenInfoHistory))
break;
POINTER_INPUT_TYPE pointerType;
auto ProcessPenInfo = [&](POINTER_PEN_INFO& ppi) {
if(ppi.penFlags & PEN_FLAG_BARREL)
pen.barrel = true;
if(ppi.penFlags & PEN_FLAG_INVERTED)
pen.inverted = true;
if(ppi.penFlags & PEN_FLAG_ERASER)
pen.eraser = true;
if(ppi.penMask & PEN_MASK_PRESSURE)
pen.pressure = ppi.pressure / 1024.0;
if(ppi.penMask & PEN_MASK_ROTATION)
pen.rotation = ppi.rotation * M_2PI / 360;
if(ppi.penMask & PEN_MASK_TILT_X)
pen.tilt.x = ppi.tiltX * M_2PI / 360;
if(ppi.penMask & PEN_MASK_TILT_Y)
pen.tilt.y = ppi.tiltY * M_2PI / 360;
};
auto DoPen = [&](Point p) {
GuiLock __;
eventCtrl = this;
Ctrl *q = this;
if(captureCtrl){
q = captureCtrl;
p += GetScreenRect().TopLeft()-captureCtrl->GetScreenRect().TopLeft();
}
else
for(Ctrl *t = q; t; t=q->ChildFromPoint(p)) q = t;
q->Pen(p, pen, GetMouseFlags());
SyncCaret();
Image m = CursorOverride();
if(IsNull(m)) SetMouseCursor(q->CursorImage(p,GetMouseFlags()));
else SetMouseCursor(m);
};
UINT32 pointerId = GET_POINTERID_WPARAM(wParam);
if(GetPointerType(pointerId, &pointerType) && pointerType == PT_PEN) {
UINT32 hc = 256;
Buffer<POINTER_PEN_INFO> ppit(hc);
if(message == WM_POINTERUPDATE && GetPointerPenInfoHistory(pointerId, &hc, ppit)) {
for(int i = hc - 1; i >= 0; i--) {
static Point lastp=Null;
ProcessPenInfo(ppit[i]);
pen.history = (bool)i;
POINT hp = ppit[i].pointerInfo.ptPixelLocation;
if(!pen.history || hp!=lastp){
lastp = CurrentMousePos = hp;
ScreenToClient(hwnd, &hp);
DoPen(hp);
}
}
break;
}
POINTER_PEN_INFO ppi;
if(GetPointerPenInfo(pointerId, &ppi))
ProcessPenInfo(ppi);
switch(message) {
case WM_POINTERDOWN:
pendown=true;
pen.action = PEN_DOWN;
ClickActivateWnd();
break;
case WM_POINTERUP:
pendown=false;
pen.action = PEN_UP;
break;
}
DoPen(p);
break;
}
}
break;
case WM_PALETTECHANGED:
if((HWND)wParam == hwnd)
break;
case WM_QUERYNEWPALETTE:
if(!SystemDraw::AutoPalette()) break;
{
HDC hDC = GetDC(hwnd);
HPALETTE hOldPal = SelectPalette(hDC, GetQlibPalette(), FALSE);
int i = RealizePalette(hDC);
SelectPalette(hDC, hOldPal, TRUE);
RealizePalette(hDC);
ReleaseDC(hwnd, hDC);
LLOG("Realized " << i << " colors");
if(i) InvalidateRect(hwnd, NULL, TRUE);
return i;
}
case WM_PAINT:
ASSERT_(!painting || IsPanicMode(), "WM_PAINT invoked for " + Name() + " while in Paint routine");
ASSERT(hwnd);
if(hwnd) {
PAINTSTRUCT ps;
if(IsVisible())
SyncScroll();
HDC dc = BeginPaint(hwnd, &ps);
fullrefresh = false;
if(IsVisible()) {
SystemDraw draw(dc);
HPALETTE hOldPal;
if(draw.PaletteMode() && SystemDraw::AutoPalette()) {
hOldPal = SelectPalette(dc, GetQlibPalette(), TRUE);
int n = RealizePalette(dc);
LLOG("In paint realized " << n << " colors");
}
painting = true;
UpdateArea(draw, Rect(ps.rcPaint));
painting = false;
if(draw.PaletteMode() && SystemDraw::AutoPalette())
SelectPalette(dc, hOldPal, TRUE);
}
EndPaint(hwnd, &ps);
UpdateDHCtrls(); // so that they are displayed withing the same WM_PAINT - looks better
}
return 0L;
case WM_NCHITTEST:
CheckMouseCtrl();
if(ignoremouse) return HTTRANSPARENT;
break;
case WM_LBUTTONDOWN:
ClickActivateWnd();
if(ignoreclick) return 0L;
DoMouse(LEFTDOWN, MousePos(), 0);
if(_this) PostInput();
return 0L;
case WM_LBUTTONUP:
if(ignoreclick)
EndIgnore();
else
DoMouse(LEFTUP, MousePos(), 0);
if(_this) PostInput();
return 0L;
case WM_LBUTTONDBLCLK:
ClickActivateWnd();
if(ignoreclick) return 0L;
DoMouse(LEFTDOUBLE, MousePos(), 0);
if(_this) PostInput();
return 0L;
case WM_RBUTTONDOWN:
ClickActivateWnd();
if(ignoreclick) return 0L;
DoMouse(RIGHTDOWN, MousePos());
if(_this) PostInput();
return 0L;
case WM_RBUTTONUP:
if(ignoreclick)
EndIgnore();
else
DoMouse(RIGHTUP, MousePos());
if(_this) PostInput();
return 0L;
case WM_RBUTTONDBLCLK:
ClickActivateWnd();
if(ignoreclick) return 0L;
DoMouse(RIGHTDOUBLE, MousePos());
if(_this) PostInput();
return 0L;
case WM_MBUTTONDOWN:
ClickActivateWnd();
if(ignoreclick) return 0L;
DoMouse(MIDDLEDOWN, MousePos());
if(_this) PostInput();
return 0L;
case WM_MBUTTONUP:
if(ignoreclick)
EndIgnore();
else
DoMouse(MIDDLEUP, MousePos());
if(_this) PostInput();
return 0L;
case WM_MBUTTONDBLCLK:
ClickActivateWnd();
if(ignoreclick) return 0L;
DoMouse(MIDDLEDOUBLE, MousePos());
if(_this) PostInput();
return 0L;
case WM_NCLBUTTONDOWN:
case WM_NCRBUTTONDOWN:
case WM_NCMBUTTONDOWN:
ClickActivateWnd();
IgnoreMouseUp();
break;
case WM_MOUSEMOVE:
LLOG("WM_MOUSEMOVE: ignoreclick = " << ignoreclick);
if(ignoreclick)
EndIgnore();
else {
if(_this) DoMouse(MOUSEMOVE, MousePos());
DoCursorShape();
}
return 0L;
case 0x20a: // WM_MOUSEWHEEL:
if(ignoreclick) {
EndIgnore();
return 0L;
}
if(_this) {
Point p(0, 0);
::ClientToScreen(hwnd, p);
DoMouse(MOUSEWHEEL, Point((dword)lParam) - p, (short)HIWORD(wParam));
CurrentMousePos = Point((dword)lParam);
}
if(_this) PostInput();
return 0L;
case WM_SETCURSOR:
if((HWND)wParam == hwnd && LOWORD((dword)lParam) == HTCLIENT) {
if(hCursor) SetCursor(hCursor);
return TRUE;
}
break;
// case WM_MENUCHAR:
// return MAKELONG(0, MNC_SELECT);
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_CHAR:
ignorekeyup = false;
case WM_KEYUP:
case WM_SYSKEYUP:
{
#if 0
String msgdump;
switch(message)
{
case WM_KEYDOWN: msgdump << "WM_KEYDOWN"; break;
case WM_KEYUP: msgdump << "WM_KEYUP"; break;
case WM_SYSKEYDOWN: msgdump << "WM_SYSKEYDOWN"; break;
case WM_SYSKEYUP: msgdump << "WM_SYSKEYUP"; break;
case WM_CHAR: msgdump << "WM_CHAR"; break;
}
msgdump << " wParam = 0x" << FormatIntHex(wParam, 8)
<< ", lParam = 0x" << FormatIntHex(lParam, 8)
<< ", ignorekeyup = " << (ignorekeyup ? "true" : "false");
LLOG(msgdump);
#endif
dword keycode = 0;
if(message == WM_KEYDOWN) {
keycode = KEYtoK((dword)wParam);
if(keycode == K_SPACE)
keycode = 0;
}
else
if(message == WM_KEYUP)
keycode = KEYtoK((dword)wParam) | K_KEYUP;
else
if(message == WM_SYSKEYDOWN /*&& ((lParam & 0x20000000) || wParam == VK_F10)*/)
keycode = KEYtoK((dword)wParam);
else
if(message == WM_SYSKEYUP /*&& ((lParam & 0x20000000) || wParam == VK_F10)*/)
keycode = KEYtoK((dword)wParam) | K_KEYUP;
else
if(message == WM_CHAR && wParam != 127 && wParam > 32 || wParam == 32 && KEYtoK(VK_SPACE) == K_SPACE) {
if(IsWindowUnicode(hwnd)) { // TRC 04/10/17: ActiveX Unicode patch
static WCHAR surr[2];
keycode = (dword)wParam;
if((keycode & 0XFC00) == 0xD800) { // covert UTF16 surrogate pair to UTF32 codepoint
surr[0] = (WCHAR)keycode;
return 0L;
}
if((keycode & 0xFC00) == 0xDC00) {
surr[1] = (WCHAR)keycode;
keycode = ReadSurrogatePair(surr, surr + 2);
surr[0] = 0;
if(!keycode)
return 0L;
}
else
surr[0] = 0;
}
else {
char b[20];
::GetLocaleInfo(MAKELCID(LOWORD(GetKeyboardLayout(0)), SORT_DEFAULT),
LOCALE_IDEFAULTANSICODEPAGE, b, 20);
int codepage = atoi(b);
if(codepage >= 1250 && codepage <= 1258)
keycode = ToUnicode((dword)wParam, codepage - 1250 + CHARSET_WIN1250);
else
keycode = (dword)wParam;
}
}
bool b = false;
if(keycode) {
b = DispatchKey(keycode, LOWORD(lParam));
SyncCaret();
if(_this) PostInput();
}
// LOG("key processed = " << b);
if(b || (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
&& wParam != VK_F4 && !PassWindowsKey((dword)wParam)) // 17.11.2003 Mirek -> invoke system menu
return 0L;
break;
}
break;
// case WM_GETDLGCODE:
// return wantfocus ? 0 : DLGC_STATIC;
case WM_XBUTTONDOWN: {
UINT button = GET_XBUTTON_WPARAM(wParam);
if(button == XBUTTON2)
DispatchKey(K_MOUSE_FORWARD, 1);
if(button == XBUTTON1)
DispatchKey(K_MOUSE_BACKWARD, 1);
return 0L;
}
case WM_XBUTTONUP: {
UINT button = GET_XBUTTON_WPARAM(wParam);
if(button == XBUTTON2)
DispatchKey(K_MOUSE_FORWARD|K_KEYUP, 1);
if(button == XBUTTON1)
DispatchKey(K_MOUSE_BACKWARD|K_KEYUP, 1);
return 0L;
}
case WM_ERASEBKGND:
return 1L;
case WM_DESTROY:
PreDestroy();
break;
case WM_NCDESTROY:
if(!hwnd) break;
if(HasChildDeep(mouseCtrl) || this == ~mouseCtrl) mouseCtrl = NULL;
if(HasChildDeep(focusCtrl) || this == ~focusCtrl) focusCtrl = NULL;
if(HasChildDeep(focusCtrlWnd) || this == ~focusCtrlWnd) {
LLOG("WM_NCDESTROY: clearing focusCtrlWnd = " << UPP::Name(focusCtrlWnd));
focusCtrlWnd = NULL;
focusCtrl = NULL;
}
if(::GetFocus() == NULL) {
Ctrl *owner = GetOwner();
if(owner && (owner->IsForeground() || IsForeground()) && !owner->SetWantFocus())
IterateFocusForward(owner, owner);
}
if(IsWindowUnicode(hwnd)) // TRC 04/10/17: ActiveX unicode patch
DefWindowProcW(hwnd, message, wParam, lParam);
else
DefWindowProc(hwnd, message, wParam, lParam);
hwnd = NULL;
return 0L;
case WM_CANCELMODE:
if(this == ~captureCtrl || HasChildDeep(captureCtrl))
ReleaseCtrlCapture();
break;
case WM_SHOWWINDOW:
visible = (BOOL) wParam;
StateH(SHOW);
break;
case WM_MOUSEACTIVATE:
LLOG("WM_MOUSEACTIVATE " << Name() << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd) << ", raw = " << (void *)::GetFocus());
if(!IsEnabled()) {
if(lastActiveWnd && lastActiveWnd->IsEnabled()) {
if(focusCtrl) { // this closes popup
LLOG("WM_MOUSEACTIVATE -> ClickActivateWnd for " << UPP::Name(lastActiveWnd));
lastActiveWnd->ClickActivateWnd();
}
else { // this makes child dialog active when clicked on disabled parent
LLOG("WM_MOUSEACTIVATE -> ::SetFocus for " << UPP::Name(lastActiveWnd));
::SetFocus(lastActiveWnd->GetHWND());
}
}
else
MessageBeep(MB_OK);
return MA_NOACTIVATEANDEAT;
}
if(IsPopUp()) return MA_NOACTIVATE;
break;
case WM_SIZE:
case WM_MOVE:
if(hwnd) {
Rect rect;
if(activex) {
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWINFO);
::GetWindowPlacement(hwnd, &wp);
rect = wp.rcNormalPosition;
}
else
rect = GetScreenClient(hwnd);
LLOG("WM_MOVE / WM_SIZE: screen client = " << rect);
if(GetRect() != rect)
SetWndRect(rect);
#if WINCARET
WndDestroyCaret();
caretCtrl = NULL;
SyncCaret();
#endif
}
return 0L;
case WM_HELP:
return TRUE;
case WM_ACTIVATE:
LLOG("WM_ACTIVATE " << Name() << ", wParam = " << (int)wParam << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd) << ", raw = " << (void *)::GetFocus());
ignorekeyup = true;
break;
case WM_SETFOCUS:
LLOG("WM_SETFOCUS " << Name() << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd) << ", raw = " << (void *)::GetFocus());
if(this != focusCtrlWnd || focusCtrl && focusCtrlWnd != focusCtrl->GetTopCtrl()) { // second condition fixes popup issue when clicking dialog parent
if(IsEnabled()) {
LLOG("WM_SETFOCUS -> ActivateWnd: this != focusCtrlWnd, this = "
<< Name() << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd));
ActivateWnd();
}
else {
if(focusCtrlWnd && focusCtrlWnd->IsEnabled()) {
if(!IsEnabled())
MessageBeep(MB_OK);
LLOG("WM_SETFOCUS -> ::SetFocus for " << UPP::Name(focusCtrlWnd) << ", this: " << UPP::Name(this));
::SetFocus(focusCtrlWnd->GetHWND());
}
else
if(lastActiveWnd && lastActiveWnd->IsEnabled()) {
LLOG("WM_SETFOCUS -> ::SetFocus for " << UPP::Name(lastActiveWnd));
::SetFocus(lastActiveWnd->GetHWND());
}
else {
LLOG("WM_SETFOCUS - ::SetFocus(NULL)");
::SetFocus(NULL);
}
}
}
LLOG("//WM_SETFOCUS " << (void *)hwnd << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd) << ", raw = " << (void *)::GetFocus());
return 0L;
case WM_KILLFOCUS:
LLOG("WM_KILLFOCUS " << (void *)(HWND)wParam << ", this = " << UPP::Name(this) << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd) << ", raw = " << (void *)::GetFocus());
LLOG("Kill " << UPP::Name(CtrlFromHWND((HWND)wParam)));
if(!CtrlFromHWND((HWND)wParam)) {
LLOG("WM_KILLFOCUS -> KillFocusWnd: " << UPP::Name(this));
KillFocusWnd();
}
LLOG("//WM_KILLFOCUS " << (void *)(HWND)wParam << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd) << ", raw = " << (void *)::GetFocus());
return 0L;
case WM_ENABLE:
if(!!wParam != enabled) {
enabled = !!wParam;
RefreshFrame();
StateH(ENABLE);
}
return 0L;
#ifndef PLATFORM_WINCE
case WM_GETMINMAXINFO:
{
MINMAXINFO *mmi = (MINMAXINFO *)lParam;
Rect frmrc = Size(200, 200);
::AdjustWindowRect(frmrc, WS_OVERLAPPEDWINDOW, FALSE);
// Size msz = Ctrl::GetWorkArea().Deflated(-frmrc.left, -frmrc.top,
// frmrc.right - 200, frmrc.bottom - 200).GetSize();
// Rect minr(Point(50, 50), min(msz, GetMinSize()));
// Rect maxr(Point(50, 50), min(msz, GetMaxSize())); // Removed cxl&nixnixnix 2012-6-12
Rect minr(Point(50, 50), GetMinSize());
Rect maxr(Point(50, 50), GetMaxSize());
dword style = ::GetWindowLong(hwnd, GWL_STYLE);
dword exstyle = ::GetWindowLong(hwnd, GWL_EXSTYLE);
AdjustWindowRectEx(minr, style, FALSE, exstyle);
AdjustWindowRectEx(maxr, style, FALSE, exstyle);
mmi->ptMinTrackSize = Point(minr.Size());
mmi->ptMaxTrackSize = Point(maxr.Size());
LLOG("WM_GETMINMAXINFO: MinTrackSize = " << Point(mmi->ptMinTrackSize) << ", MaxTrackSize = " << Point(mmi->ptMaxTrackSize));
LLOG("ptMaxSize = " << Point(mmi->ptMaxSize) << ", ptMaxPosition = " << Point(mmi->ptMaxPosition));
}
return 0L;
#endif
/* case WM_SETTINGCHANGE:
case 0x031A: // WM_THEMECHANGED
ReSkin();
RefreshLayoutDeep();
RefreshFrame();
break;
*/
/*
case WM_IME_COMPOSITION:
HIMC himc = ImmGetContext(hwnd);
if(!himc) break;
CANDIDATEFORM cf;
Rect r = GetScreenRect();
cf.dwIndex = 0;
cf.dwStyle = CFS_CANDIDATEPOS;
cf.ptCurrentPos.x = r.left + caretx;
cf.ptCurrentPos.y = r.top + carety + caretcy;
ImmSetCandidateWindow (himc, &cf);
break;
*/
}
if(hwnd) {
if(IsWindowUnicode(hwnd)) // TRC 04/10/17: ActiveX unicode patch
return DefWindowProcW(hwnd, message, wParam, lParam);
else
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0L;
}
void Ctrl::PreDestroy() {}
}
#endif