mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
932 lines
No EOL
27 KiB
C++
932 lines
No EOL
27 KiB
C++
#include "CtrlCore.h"
|
|
|
|
#ifdef GUI_WIN
|
|
|
|
#include <winnls.h>
|
|
|
|
#include <imm.h>
|
|
|
|
namespace Upp {
|
|
|
|
void XpClear(); // Clear cached windows theme handles
|
|
|
|
#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);
|
|
|
|
void Ctrl::DoCancelPreedit()
|
|
{
|
|
if(!focusCtrlWnd)
|
|
return;
|
|
Top *top = focusCtrl->GetTop();
|
|
if(top)
|
|
focusCtrl->HidePreedit();
|
|
if(top && top->hwnd) {
|
|
HIMC himc = ImmGetContext(top->hwnd);
|
|
if(himc && ImmGetOpenStatus(himc)) {
|
|
ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
|
|
ImmReleaseContext(top->hwnd, himc);
|
|
}
|
|
}
|
|
}
|
|
|
|
static UINT getDpiForWindow(HWND hwnd)
|
|
{
|
|
static UINT (WINAPI *GetDpiForWindow)(HWND hwnd);
|
|
DllFn(GetDpiForWindow, "user32.dll", "GetDpiForWindow");
|
|
return GetDpiForWindow ? GetDpiForWindow(hwnd) : 96;
|
|
}
|
|
|
|
static int getSystemMetricsForDpi(int nIndex, UINT dpi)
|
|
{
|
|
static int (*GetSystemMetricsForDpi)(int nIndex, UINT dpi);
|
|
DllFn(GetSystemMetricsForDpi, "user32.dll", "GetSystemMetricsForDpi");
|
|
return GetSystemMetricsForDpi ? GetSystemMetricsForDpi(nIndex, dpi) : 0;
|
|
}
|
|
|
|
static bool IsMaximized(HWND hwnd)
|
|
{
|
|
WINDOWPLACEMENT placement = {0};
|
|
placement.length = sizeof(WINDOWPLACEMENT);
|
|
return GetWindowPlacement(hwnd, &placement) && placement.showCmd == SW_SHOWMAXIMIZED;
|
|
}
|
|
|
|
static int win32_dpi_scale(int value, UINT dpi)
|
|
{
|
|
return (int)((double)value * dpi / 96);
|
|
}
|
|
|
|
static int getTitlebarButtonWidth(int dpi)
|
|
{
|
|
return win32_dpi_scale(47, dpi);
|
|
}
|
|
|
|
int GetWin32TitleBarHeight(HWND hwnd); // implemented in CtrlLib/ChWin32.cpp
|
|
|
|
/* UHD
|
|
|
|
r = [0, 0] - [2854, 60] : (2854, 60)
|
|
button_width = 94
|
|
|
|
HD
|
|
|
|
r = [0, 0] - [1424, 31] : (1424, 31)
|
|
button_width = 47
|
|
|
|
Light
|
|
|
|
Bar 238 244 249
|
|
Min/max 230,234,239
|
|
|
|
Dark
|
|
|
|
bar 26,34,39
|
|
min/max 39,47,52
|
|
close 232,17,35
|
|
|
|
*/
|
|
|
|
static Rect getTitleBarRect(HWND hwnd) // TODO (image, cy)
|
|
{ // Adopted from: https://github.com/oberth/custom-chrome/blob/master/source/gui/window_helper.hpp#L52-L64
|
|
const int top_and_bottom_borders = 2;
|
|
|
|
int height = win32_dpi_scale(GetWin32TitleBarHeight(hwnd), GetDpiForWindow(hwnd)) + top_and_bottom_borders;
|
|
|
|
RECT rect;
|
|
GetClientRect(hwnd, &rect);
|
|
rect.bottom = rect.top + height;
|
|
return rect;
|
|
}
|
|
|
|
void Ctrl::PaintWinBar(SystemDraw& w, const Rect& clip)
|
|
{
|
|
bool custom_titlebar = false;
|
|
auto topwin = dynamic_cast<TopWindow *>(this);
|
|
HWND hwnd = GetHWND();
|
|
if(topwin && topwin->custom_titlebar && hwnd) {
|
|
Rect r = getTitleBarRect(hwnd);
|
|
DDUMP(r);
|
|
// draw.DrawRect(r, Yellow());
|
|
int button_width = getTitlebarButtonWidth(GetDpiForWindow(hwnd));
|
|
DDUMP(button_width);
|
|
|
|
int x = r.right;
|
|
int n = 0;
|
|
auto Drw = [&](const Image& m) {
|
|
x -= button_width;
|
|
Color ink = topwin->HasFocusDeep() ? SBlack() : Gray();
|
|
if(topwin->active_titlebar_button == n) {
|
|
w.DrawRect(x, r.top, button_width, r.GetHeight(), n ? SWhiteGray() : Color(196, 43, 28));
|
|
if(n == 0)
|
|
ink = SWhite();
|
|
}
|
|
w.DrawImage(x + (button_width - m.GetWidth()) / 2, r.top + (r.GetHeight() - m.GetHeight()) / 2, m, ink);
|
|
n++;
|
|
};
|
|
Drw(CtrlCoreImg::WinClose());
|
|
if(topwin->IsZoomable()) {
|
|
Drw(IsMaximized(hwnd) ? CtrlCoreImg::WinMaximized() : CtrlCoreImg::WinMaximize());
|
|
Drw(CtrlCoreImg::WinMinimize());
|
|
}
|
|
|
|
// TODO: Fake shadow rect?
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
POINT p;
|
|
if(::GetCursorPos(&p))
|
|
CurrentMousePos = p;
|
|
|
|
bool custom_titlebar = false;
|
|
auto topwin = dynamic_cast<TopWindow *>(this);
|
|
if(topwin)
|
|
custom_titlebar = topwin->custom_titlebar;
|
|
|
|
Rect titlebar_rect = Null;
|
|
int titlebar_button_width = 0;
|
|
int active_titlebar_button = -1;
|
|
bool active_titlebar_active = false;
|
|
|
|
if(custom_titlebar) {
|
|
titlebar_rect = getTitleBarRect(hwnd);
|
|
titlebar_button_width = getTitlebarButtonWidth(GetDpiForWindow(hwnd));
|
|
|
|
Point p = CurrentMousePos;
|
|
ScreenToClient(hwnd, p);
|
|
if(p.y >= titlebar_rect.top && p.y < titlebar_rect.bottom) {
|
|
int q = (titlebar_rect.right - p.x) / titlebar_button_width;
|
|
active_titlebar_button = q >= 0 && q < (topwin->IsZoomable() ? 3 : 1) ? q : -1;
|
|
}
|
|
|
|
active_titlebar_active = topwin->HasFocusDeep();
|
|
|
|
if(findarg(message, WM_PAINT, WM_NCPAINT) < 0 &&
|
|
active_titlebar_button != topwin->active_titlebar_button ||
|
|
active_titlebar_active != topwin->active_titlebar_active) {
|
|
topwin->active_titlebar_button = active_titlebar_button;
|
|
topwin->active_titlebar_active = active_titlebar_active;
|
|
InvalidateRect(hwnd, titlebar_rect, FALSE);
|
|
}
|
|
}
|
|
|
|
cancel_preedit = DoCancelPreedit; // We really need this just once, but whatever..
|
|
|
|
is_pen_event = (GetMessageExtraInfo() & 0xFFFFFF80) == 0xFF515700; // https://learn.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages?redirectedfrom=MSDN
|
|
|
|
auto MousePos = [&] {
|
|
Point p = Point((dword)lParam);
|
|
CurrentMousePos = p;
|
|
::ClientToScreen(hwnd, CurrentMousePos);
|
|
return p;
|
|
};
|
|
|
|
bool has_preedit = HasFocusDeep() && focusCtrl && !IsNull(focusCtrl->GetPreedit());
|
|
|
|
auto StopPreedit = [&] {
|
|
HidePreedit();
|
|
if(HasFocusDeep())
|
|
CancelPreedit();
|
|
};
|
|
|
|
auto ClickActivate = [&] {
|
|
ClickActivateWnd();
|
|
StopPreedit();
|
|
};
|
|
|
|
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;
|
|
ClickActivate();
|
|
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_ERASEBKGND:
|
|
if(erasebg) {
|
|
HDC hdc = (HDC)(wParam);
|
|
RECT rc; GetClientRect(hwnd, &rc);
|
|
Color c = SColorFace();
|
|
HBRUSH brush = CreateSolidBrush(RGB(c.GetR(), c.GetG(), c.GetB()));
|
|
FillRect(hdc, &rc, brush);
|
|
DeleteObject(brush);
|
|
erasebg = false;
|
|
}
|
|
return 1L;
|
|
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));
|
|
if(draw.PaletteMode() && SystemDraw::AutoPalette())
|
|
SelectPalette(dc, hOldPal, TRUE);
|
|
painting = false;
|
|
}
|
|
EndPaint(hwnd, &ps);
|
|
|
|
UpdateDHCtrls(); // so that they are displayed withing the same WM_PAINT - looks better
|
|
}
|
|
return 0L;
|
|
case WM_NCCALCSIZE:
|
|
if(custom_titlebar && wParam) { // custom titlebar based on https://github.com/grassator/win32-window-custom-titlebar/blob/main/main.c
|
|
UINT dpi = GetDpiForWindow(hwnd);
|
|
|
|
int frame_x = GetSystemMetricsForDpi(SM_CXFRAME, dpi);
|
|
int frame_y = GetSystemMetricsForDpi(SM_CYFRAME, dpi);
|
|
int padding = GetSystemMetricsForDpi(92 /*SM_CXPADDEDBORDER*/, dpi);
|
|
|
|
NCCALCSIZE_PARAMS *params = (NCCALCSIZE_PARAMS *)lParam;
|
|
RECT *requested_client_rect = params->rgrc;
|
|
|
|
requested_client_rect->right -= frame_x + padding;
|
|
requested_client_rect->left += frame_x + padding;
|
|
requested_client_rect->bottom -= frame_y + padding;
|
|
|
|
if(IsMaximized(hwnd))
|
|
requested_client_rect->top += padding;
|
|
|
|
return 0;
|
|
}
|
|
break;
|
|
case WM_NCHITTEST:
|
|
CheckMouseCtrl();
|
|
if(ignoremouse) return HTTRANSPARENT;
|
|
if(custom_titlebar) {
|
|
LRESULT hit = DefWindowProc(hwnd, message, wParam, lParam);
|
|
if(findarg(hit, HTNOWHERE, HTRIGHT, HTLEFT, HTTOPLEFT, HTTOP,
|
|
HTTOPRIGHT, HTBOTTOMRIGHT, HTBOTTOM, HTBOTTOMLEFT) >= 0)
|
|
return hit;
|
|
if(active_titlebar_button == 1)
|
|
return HTMAXBUTTON;
|
|
// Looks like adjustment happening in NCCALCSIZE is messing with the detection
|
|
// of the top hit area so manually fixing that.
|
|
UINT dpi = GetDpiForWindow(hwnd);
|
|
int frame_y = GetSystemMetricsForDpi(SM_CYFRAME, dpi);
|
|
int padding = GetSystemMetricsForDpi(92 /*SM_CXPADDEDBORDER*/, dpi);
|
|
Point cursor_point((LONG)lParam);
|
|
ScreenToClient(hwnd, cursor_point);
|
|
// We should not return HTTOP when hit-testing a maximized window
|
|
if(!IsMaximized(hwnd) && cursor_point.y > 0 && cursor_point.y < frame_y + padding && topwin->IsSizeable())
|
|
return HTTOP;
|
|
|
|
// Since we are drawing our own caption, this needs to be a custom test
|
|
if(cursor_point.y < getTitleBarRect(hwnd).bottom && topwin->IsCustomTitleBarDragArea(cursor_point))
|
|
return HTCAPTION;
|
|
|
|
return HTCLIENT;
|
|
}
|
|
break;
|
|
case WM_NCLBUTTONUP: // Map button clicks to the right messages for the window
|
|
switch(active_titlebar_button) {
|
|
case 0:
|
|
PostMessageW(hwnd, WM_CLOSE, 0, 0);
|
|
return 0;
|
|
case 1:
|
|
ShowWindow(hwnd, IsMaximized(hwnd) ? SW_NORMAL : SW_MAXIMIZE);
|
|
return 0;
|
|
case 2:
|
|
ShowWindow(hwnd, SW_MINIMIZE);
|
|
return 0;
|
|
}
|
|
break;
|
|
case WM_NCRBUTTONUP:
|
|
if(wParam == HTCAPTION) {
|
|
BOOL isMaximized = IsMaximized(hwnd);
|
|
MENUITEMINFO menu_item_info;
|
|
menu_item_info.cbSize = sizeof(menu_item_info);
|
|
menu_item_info.fMask = MIIM_STATE;
|
|
HMENU sys_menu = GetSystemMenu(hwnd, false);
|
|
|
|
auto Menu = [&](UINT item, bool enabled) {
|
|
menu_item_info.fState = enabled ? MF_ENABLED : MF_DISABLED;
|
|
SetMenuItemInfo(sys_menu, item, false, &menu_item_info);
|
|
};
|
|
|
|
Menu(SC_RESTORE, isMaximized);
|
|
Menu(SC_MOVE, !isMaximized);
|
|
Menu(SC_SIZE, !isMaximized);
|
|
Menu(SC_MINIMIZE, true);
|
|
Menu(SC_MAXIMIZE, !isMaximized);
|
|
Menu(SC_CLOSE, true);
|
|
|
|
Point p((LONG)lParam);
|
|
BOOL result = TrackPopupMenu(sys_menu, TPM_RETURNCMD, p.x, p.y, 0, hwnd, NULL);
|
|
if(result)
|
|
PostMessage(hwnd, WM_SYSCOMMAND, result, 0);
|
|
}
|
|
break;
|
|
case WM_LBUTTONDOWN:
|
|
ClickActivate();
|
|
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:
|
|
ClickActivate();
|
|
if(ignoreclick) return 0L;
|
|
DoMouse(LEFTDOUBLE, MousePos(), 0);
|
|
if(_this) PostInput();
|
|
return 0L;
|
|
case WM_RBUTTONDOWN:
|
|
ClickActivate();
|
|
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:
|
|
ClickActivate();
|
|
if(ignoreclick) return 0L;
|
|
DoMouse(RIGHTDOUBLE, MousePos());
|
|
if(_this) PostInput();
|
|
return 0L;
|
|
case WM_MBUTTONDOWN:
|
|
ClickActivate();
|
|
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:
|
|
ClickActivate();
|
|
if(ignoreclick) return 0L;
|
|
DoMouse(MIDDLEDOUBLE, MousePos());
|
|
if(_this) PostInput();
|
|
return 0L;
|
|
case WM_NCLBUTTONDOWN:
|
|
ClickActivate();
|
|
IgnoreMouseUp();
|
|
if(active_titlebar_button >= 0)
|
|
return 0;
|
|
break;
|
|
case WM_NCRBUTTONDOWN:
|
|
case WM_NCMBUTTONDOWN:
|
|
ClickActivate();
|
|
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:
|
|
case 0x20e: // WM_MOUSEHWHEEL:
|
|
if(ignoreclick) {
|
|
EndIgnore();
|
|
return 0L;
|
|
}
|
|
if(_this) {
|
|
Point p(0, 0);
|
|
::ClientToScreen(hwnd, p);
|
|
DoMouse(message == 0x20e ? MOUSEHWHEEL : 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_IME_STARTCOMPOSITION:
|
|
case WM_IME_COMPOSITION:
|
|
if(has_preedit) {
|
|
HIMC himc = ImmGetContext(GetHWND());
|
|
if(!himc)
|
|
break;
|
|
Rect pr = GetPreeditScreenRect();
|
|
Point p = pr.TopLeft() - GetScreenRect().TopLeft();
|
|
CANDIDATEFORM cf;
|
|
cf.dwIndex = 0;
|
|
cf.dwStyle = CFS_EXCLUDE;
|
|
cf.ptCurrentPos.x = p.x;
|
|
cf.ptCurrentPos.y = p.y;
|
|
cf.rcArea.left = p.x;
|
|
cf.rcArea.top = p.y;
|
|
cf.rcArea.right = p.x + DPI(20); // DPI(20) is sort of hack, but candidate windows are above or bellow anyway...
|
|
cf.rcArea.bottom = p.y + pr.GetHeight();
|
|
ImmSetCandidateWindow(himc, &cf);
|
|
/* // todo: SetCaretPos too
|
|
Rect r;
|
|
::CreateCaret(hwnd, NULL, 1, pr.Height());
|
|
::ShowCaret(hwnd);
|
|
::SetCaretPos(p.x, p.y);
|
|
*/
|
|
auto ReadString = [&](int type) -> WString {
|
|
int len = ImmGetCompositionStringW (himc, type, NULL, 0);
|
|
if(len > 0) {
|
|
Buffer<char16> sw(len / 2);
|
|
ImmGetCompositionStringW(himc, type, sw, len);
|
|
return ToUtf32(sw, len / 2);
|
|
}
|
|
return Null;
|
|
};
|
|
if(lParam & GCS_COMPSTR) {
|
|
ShowPreedit(ReadString(GCS_COMPSTR), ImmGetCompositionString(himc, GCS_CURSORPOS, 0, 0));
|
|
}
|
|
if(lParam & GCS_RESULTSTR) {
|
|
WString h = ReadString(GCS_RESULTSTR);
|
|
for(wchar c : h)
|
|
DispatchKey(c, 1);
|
|
HidePreedit();
|
|
SyncCaret();
|
|
}
|
|
ImmReleaseContext(GetHWND(), himc);
|
|
return 0L;
|
|
}
|
|
break;
|
|
case WM_IME_ENDCOMPOSITION:
|
|
if(has_preedit) {
|
|
HidePreedit();
|
|
return 0L;
|
|
}
|
|
break;
|
|
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_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))
|
|
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 -> ClickActivate for " << UPP::Name(lastActiveWnd));
|
|
StopPreedit();
|
|
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
|
|
SyncPreedit();
|
|
}
|
|
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;
|
|
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());
|
|
StopPreedit();
|
|
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
|
|
PostReSkin();
|
|
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) {
|
|
LRESULT r;
|
|
int level = LeaveGuiMutexAll();
|
|
if(IsWindowUnicode(hwnd))
|
|
r = DefWindowProcW(hwnd, message, wParam, lParam);
|
|
else
|
|
r = DefWindowProc(hwnd, message, wParam, lParam);
|
|
EnterGuiMutex(level);
|
|
return r;
|
|
}
|
|
return 0L;
|
|
}
|
|
|
|
void Ctrl::PreDestroy() {}
|
|
|
|
}
|
|
|
|
#endif |