#include "uipriv_windows.hpp"
#include "area.hpp"
static uiModifiers getModifiers(void)
{
uiModifiers m = 0;
if ((GetKeyState(VK_CONTROL) & 0x80) != 0)
m |= uiModifierCtrl;
if ((GetKeyState(VK_MENU) & 0x80) != 0)
m |= uiModifierAlt;
if ((GetKeyState(VK_SHIFT) & 0x80) != 0)
m |= uiModifierShift;
if ((GetKeyState(VK_LWIN) & 0x80) != 0)
m |= uiModifierSuper;
if ((GetKeyState(VK_RWIN) & 0x80) != 0)
m |= uiModifierSuper;
return m;
}
static void track(uiArea *a, BOOL tracking)
{
TRACKMOUSEEVENT tm;
if (a->tracking && tracking)
return;
if (!a->tracking && !tracking)
return;
a->tracking = tracking;
ZeroMemory(&tm, sizeof (TRACKMOUSEEVENT));
tm.cbSize = sizeof (TRACKMOUSEEVENT);
tm.dwFlags = TME_LEAVE;
if (!a->tracking)
tm.dwFlags |= TME_CANCEL;
tm.hwndTrack = a->hwnd;
if (_TrackMouseEvent(&tm) == 0)
logLastError(L"error setting up mouse tracking");
}
static void capture(uiArea *a, BOOL capturing)
{
if (a->capturing && capturing)
return;
if (!a->capturing && !capturing)
return;
a->capturing = capturing;
if (a->capturing) {
track(a, FALSE);
SetCapture(a->hwnd);
} else
if (ReleaseCapture() == 0)
logLastError(L"error releasing capture on drag");
}
static void areaMouseEvent(uiArea *a, int down, int up, WPARAM wParam, LPARAM lParam)
{
uiAreaMouseEvent me;
int button;
POINT clientpt;
RECT client;
BOOL inClient;
double xpix, ypix;
if (a->capturing) {
clientpt.x = GET_X_LPARAM(lParam);
clientpt.y = GET_Y_LPARAM(lParam);
uiWindowsEnsureGetClientRect(a->hwnd, &client);
inClient = PtInRect(&client, clientpt);
if (inClient && !a->inside) {
a->inside = TRUE;
(*(a->ah->MouseCrossed))(a->ah, a, 0);
uiprivClickCounterReset(&(a->cc));
} else if (!inClient && a->inside) {
a->inside = FALSE;
(*(a->ah->MouseCrossed))(a->ah, a, 1);
uiprivClickCounterReset(&(a->cc));
}
}
xpix = (double) GET_X_LPARAM(lParam);
ypix = (double) GET_Y_LPARAM(lParam);
pixelsToDIP(a, &xpix, &ypix);
me.X = xpix;
me.Y = ypix;
if (a->scrolling) {
me.X += a->hscrollpos;
me.Y += a->vscrollpos;
}
loadAreaSize(a, NULL, &(me.AreaWidth), &(me.AreaHeight));
me.Down = down;
me.Up = up;
me.Count = 0;
if (me.Down != 0)
me.Count = uiprivClickCounterClick(&(a->cc), me.Down,
me.X, me.Y,
GetMessageTime(), GetDoubleClickTime(),
GetSystemMetrics(SM_CXDOUBLECLK) / 2,
GetSystemMetrics(SM_CYDOUBLECLK) / 2);
me.Modifiers = getModifiers();
button = me.Down;
if (button == 0)
button = me.Up;
me.Held1To64 = 0;
if (button != 1 && (wParam & MK_LBUTTON) != 0)
me.Held1To64 |= 1 << 0;
if (button != 2 && (wParam & MK_MBUTTON) != 0)
me.Held1To64 |= 1 << 1;
if (button != 3 && (wParam & MK_RBUTTON) != 0)
me.Held1To64 |= 1 << 2;
if (button != 4 && (wParam & MK_XBUTTON1) != 0)
me.Held1To64 |= 1 << 3;
if (button != 5 && (wParam & MK_XBUTTON2) != 0)
me.Held1To64 |= 1 << 4;
if (me.Down != 0)
capture(a, TRUE);
if (me.Up != 0 && me.Held1To64 == 0)
capture(a, FALSE);
(*(a->ah->MouseEvent))(a->ah, a, &me);
}
static void onMouseEntered(uiArea *a)
{
if (a->inside)
return;
if (a->capturing) return;
track(a, TRUE);
(*(a->ah->MouseCrossed))(a->ah, a, 0);
uiprivClickCounterReset(&(a->cc));
}
static void onMouseLeft(uiArea *a)
{
a->tracking = FALSE;
a->inside = FALSE;
(*(a->ah->MouseCrossed))(a->ah, a, 1);
uiprivClickCounterReset(&(a->cc));
}
struct extkeymap {
WPARAM vk;
uiExtKey extkey;
};
static const struct extkeymap numpadExtKeys[] = {
{ VK_HOME, uiExtKeyN7 },
{ VK_UP, uiExtKeyN8 },
{ VK_PRIOR, uiExtKeyN9 },
{ VK_LEFT, uiExtKeyN4 },
{ VK_CLEAR, uiExtKeyN5 },
{ VK_RIGHT, uiExtKeyN6 },
{ VK_END, uiExtKeyN1 },
{ VK_DOWN, uiExtKeyN2 },
{ VK_NEXT, uiExtKeyN3 },
{ VK_INSERT, uiExtKeyN0 },
{ VK_DELETE, uiExtKeyNDot },
{ VK_SNAPSHOT, 0 },
};
static const struct extkeymap extKeys[] = {
{ VK_ESCAPE, uiExtKeyEscape },
{ VK_INSERT, uiExtKeyInsert },
{ VK_DELETE, uiExtKeyDelete },
{ VK_HOME, uiExtKeyHome },
{ VK_END, uiExtKeyEnd },
{ VK_PRIOR, uiExtKeyPageUp },
{ VK_NEXT, uiExtKeyPageDown },
{ VK_UP, uiExtKeyUp },
{ VK_DOWN, uiExtKeyDown },
{ VK_LEFT, uiExtKeyLeft },
{ VK_RIGHT, uiExtKeyRight },
{ VK_F1, uiExtKeyF1 },
{ VK_F2, uiExtKeyF2 },
{ VK_F3, uiExtKeyF3 },
{ VK_F4, uiExtKeyF4 },
{ VK_F5, uiExtKeyF5 },
{ VK_F6, uiExtKeyF6 },
{ VK_F7, uiExtKeyF7 },
{ VK_F8, uiExtKeyF8 },
{ VK_F9, uiExtKeyF9 },
{ VK_F10, uiExtKeyF10 },
{ VK_F11, uiExtKeyF11 },
{ VK_F12, uiExtKeyF12 },
{ VK_ADD, uiExtKeyNAdd },
{ VK_SUBTRACT, uiExtKeyNSubtract },
{ VK_MULTIPLY, uiExtKeyNMultiply },
{ VK_DIVIDE, uiExtKeyNDivide },
{ VK_SNAPSHOT, 0 },
};
static const struct {
WPARAM vk;
uiModifiers mod;
} modKeys[] = {
{ VK_CONTROL, uiModifierCtrl },
{ VK_LCONTROL, uiModifierCtrl },
{ VK_RCONTROL, uiModifierCtrl },
{ VK_MENU, uiModifierAlt },
{ VK_LMENU, uiModifierAlt },
{ VK_RMENU, uiModifierAlt },
{ VK_SHIFT, uiModifierShift },
{ VK_LSHIFT, uiModifierShift },
{ VK_RSHIFT, uiModifierShift },
{ VK_LWIN, uiModifierSuper },
{ VK_RWIN, uiModifierSuper },
{ VK_SNAPSHOT, 0 },
};
static int areaKeyEvent(uiArea *a, int up, WPARAM wParam, LPARAM lParam)
{
uiAreaKeyEvent ke;
int righthand;
int i;
ke.Key = 0;
ke.ExtKey = 0;
ke.Modifier = 0;
ke.Modifiers = getModifiers();
ke.Up = up;
righthand = (lParam & 0x01000000) != 0;
if (righthand) {
if (wParam == VK_RETURN) {
ke.ExtKey = uiExtKeyNEnter;
goto keyFound;
}
} else
for (i = 0; numpadExtKeys[i].vk != VK_SNAPSHOT; i++)
if (numpadExtKeys[i].vk == wParam) {
ke.ExtKey = numpadExtKeys[i].extkey;
goto keyFound;
}
for (i = 0; extKeys[i].vk != VK_SNAPSHOT; i++)
if (extKeys[i].vk == wParam) {
ke.ExtKey = extKeys[i].extkey;
goto keyFound;
}
for (i = 0; modKeys[i].vk != VK_SNAPSHOT; i++)
if (modKeys[i].vk == wParam) {
ke.Modifier = modKeys[i].mod;
ke.Modifiers &= ~ke.Modifier;
goto keyFound;
}
if (uiprivFromScancode((lParam >> 16) & 0xFF, &ke))
goto keyFound;
return 0;
keyFound:
return (*(a->ah->KeyEvent))(a->ah, a, &ke);
}
enum {
msgAreaKeyDown = WM_USER + 0x40,
msgAreaKeyUp,
};
BOOL areaDoEvents(uiArea *a, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
{
switch (uMsg) {
case WM_ACTIVATE:
uiprivClickCounterReset(&(a->cc));
*lResult = 0;
return TRUE;
case WM_MOUSEMOVE:
onMouseEntered(a);
areaMouseEvent(a, 0, 0, wParam, lParam);
*lResult = 0;
return TRUE;
case WM_MOUSELEAVE:
onMouseLeft(a);
*lResult = 0;
return TRUE;
case WM_LBUTTONDOWN:
SetFocus(a->hwnd);
areaMouseEvent(a, 1, 0, wParam, lParam);
*lResult = 0;
return TRUE;
case WM_LBUTTONUP:
areaMouseEvent(a, 0, 1, wParam, lParam);
*lResult = 0;
return TRUE;
case WM_MBUTTONDOWN:
SetFocus(a->hwnd);
areaMouseEvent(a, 2, 0, wParam, lParam);
*lResult = 0;
return TRUE;
case WM_MBUTTONUP:
areaMouseEvent(a, 0, 2, wParam, lParam);
*lResult = 0;
return TRUE;
case WM_RBUTTONDOWN:
SetFocus(a->hwnd);
areaMouseEvent(a, 3, 0, wParam, lParam);
*lResult = 0;
return TRUE;
case WM_RBUTTONUP:
areaMouseEvent(a, 0, 3, wParam, lParam);
*lResult = 0;
return TRUE;
case WM_XBUTTONDOWN:
SetFocus(a->hwnd);
areaMouseEvent(a,
GET_XBUTTON_WPARAM(wParam) + 3, 0,
GET_KEYSTATE_WPARAM(wParam), lParam);
*lResult = TRUE; return TRUE;
case WM_XBUTTONUP:
areaMouseEvent(a,
0, GET_XBUTTON_WPARAM(wParam) + 3,
GET_KEYSTATE_WPARAM(wParam), lParam);
*lResult = TRUE; return TRUE;
case WM_CAPTURECHANGED:
if (a->capturing) {
a->capturing = FALSE;
(*(a->ah->DragBroken))(a->ah, a);
}
*lResult = 0;
return TRUE;
case msgAreaKeyDown:
*lResult = (LRESULT) areaKeyEvent(a, 0, wParam, lParam);
return TRUE;
case msgAreaKeyUp:
*lResult = (LRESULT) areaKeyEvent(a, 1, wParam, lParam);
return TRUE;
}
return FALSE;
}
BOOL areaFilter(MSG *msg)
{
LRESULT handled;
if (msg->hwnd == NULL) return FALSE;
if (windowClassOf(msg->hwnd, areaClass, NULL) != 0)
return FALSE;
handled = 0;
switch (msg->message) {
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
handled = SendMessageW(msg->hwnd, msgAreaKeyDown, msg->wParam, msg->lParam);
break;
case WM_KEYUP:
case WM_SYSKEYUP:
handled = SendMessageW(msg->hwnd, msgAreaKeyUp, msg->wParam, msg->lParam);
break;
}
return (BOOL) handled;
}