/*-----------------------------------------------------------------------
**
** Hotkey.c
**
** Hotkey edit control.
**
**-----------------------------------------------------------------------*/
//
// Win32 REVIEW:
//  See all the Get/SetWindowInt().
//
#include "ctlspriv.h"

#define F_EXT       0x01000000L

#define GWU_VIRTKEY  0
#define GWU_MODS     1*sizeof(ULONG_PTR)
#define GWU_INVALID  2*sizeof(ULONG_PTR)
#define GWU_DEFAULT  3*sizeof(ULONG_PTR)
#define GWU_HFONT    4*sizeof(ULONG_PTR)
#define GWU_YFONT    5*sizeof(ULONG_PTR)
#define NUM_WND_EXTRA (GWU_YFONT+sizeof(ULONG_PTR))

LRESULT CALLBACK HotKeyWndProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam);

#ifndef WIN32
#pragma code_seg(CODESEG_INIT)
#endif

BOOL FAR PASCAL InitHotKeyClass(HINSTANCE hInstance)
{
    WNDCLASS wc;

    if (!GetClassInfo(hInstance, HOTKEY_CLASS, &wc))
    {
#ifndef WIN32
        extern LRESULT CALLBACK _HotKeyWndProc(HWND, UINT, WPARAM, LPARAM);
        wc.lpfnWndProc   = _HotKeyWndProc;
#else
        wc.lpfnWndProc   = HotKeyWndProc;
#endif
        wc.lpszClassName = s_szHOTKEY_CLASS;
        wc.style         = CS_GLOBALCLASS;
        wc.hInstance     = hInstance;
        wc.hIcon         = NULL;
        wc.hCursor       = NULL;
        wc.hbrBackground = NULL;
        wc.lpszMenuName  = NULL;
        wc.cbClsExtra    = 0;
        wc.cbWndExtra    = NUM_WND_EXTRA;

        if (!RegisterClass(&wc))
            return FALSE;
    }
    return TRUE;
}

#ifndef WIN32
#pragma code_seg()
#endif


#ifndef WINNT
#pragma data_seg(DATASEG_READONLY)
#endif
const UINT s_Combos[8] = {
                    HKCOMB_NONE,
                    HKCOMB_S,
                    HKCOMB_C,
                    HKCOMB_SC,
                    HKCOMB_A,
                    HKCOMB_SA,
                    HKCOMB_CA,
                    HKCOMB_SCA};
#ifndef WINNT
#pragma data_seg()
#endif

void NEAR PASCAL SetHotKey(HWND hwnd, WORD wVirtKey, WORD wMods, BOOL fSendNotify)
{
    /* don't invalidate if it's the same
     */
    if (wVirtKey == GetWindowInt(hwnd, GWU_VIRTKEY) &&
        wMods == GetWindowInt(hwnd, GWU_MODS))
        return;

    SetWindowInt(hwnd, GWU_VIRTKEY ,wVirtKey);
    SetWindowInt(hwnd, GWU_MODS ,wMods);
    InvalidateRect(hwnd,NULL,TRUE);

    if (fSendNotify) {
        FORWARD_WM_COMMAND(GetParent(hwnd), GetDlgCtrlID(hwnd), hwnd, EN_CHANGE, SendMessage);
    }

    MyNotifyWinEvent(EVENT_OBJECT_VALUECHANGE, hwnd, OBJID_CLIENT, 0);
}

void NEAR PASCAL GetKeyName(UINT vk, LPTSTR lpsz, BOOL fExt)
{
    LONG scan;

    scan = (LONG)MapVirtualKey(vk,0) << 16;
    if (fExt)
        scan |= F_EXT;

    GetKeyNameText(scan,lpsz,50);
}

void NEAR PASCAL PaintHotKey(register HWND hwnd)
{
    TCHAR sz[128];
    TCHAR szPlus[10];
    int cch;
    register HDC hdc;
    UINT wMods;
    UINT wVirtKey;
    PAINTSTRUCT ps;
    int x, y;
    HANDLE hFont;
    // DWORD dwColor;
    // DWORD dwBkColor;

    LocalizedLoadString(IDS_PLUS, szPlus, ARRAYSIZE(szPlus));

    wVirtKey = (UINT) GetWindowInt(hwnd, GWU_VIRTKEY);
    wMods = (UINT) GetWindowInt(hwnd, GWU_MODS);
    if (wVirtKey || wMods)
    {
        sz[0] = 0;
        cch = 0;
        if (wMods & HOTKEYF_CONTROL)
        {
            GetKeyName(VK_CONTROL, sz, FALSE);
            lstrcat(sz,(LPTSTR)szPlus);
        }
        if (wMods & HOTKEYF_SHIFT)
        {
            GetKeyName(VK_SHIFT, sz+lstrlen(sz), FALSE);
            lstrcat(sz,szPlus);
        }
        if (wMods & HOTKEYF_ALT)
        {
            GetKeyName(VK_MENU, sz+lstrlen(sz), FALSE);
            lstrcat(sz,szPlus);
        }

        GetKeyName(wVirtKey, sz+lstrlen(sz), wMods & HOTKEYF_EXT);
    }
    else
        LocalizedLoadString(IDS_NONE,sz,100);

    cch = lstrlen(sz);

    HideCaret(hwnd);

    InvalidateRect(hwnd, NULL, TRUE);
    hdc = BeginPaint(hwnd,&ps);


    hFont = SelectObject(hdc, (HFONT)GetWindowInt(hwnd,GWU_HFONT));

    x = g_cxBorder;
    y = g_cyBorder;

    if (IsWindowEnabled(hwnd))
    {
        SetBkColor(hdc, g_clrWindow);
        SetTextColor(hdc, g_clrWindowText);
        TextOut(hdc,x,y,sz,cch);
    }
    else
    {
        // set the background color to Grayed like edit controls
        SetBkColor(hdc, g_clrBtnFace);
        if (g_clrGrayText)
        {
            SetTextColor(hdc,g_clrGrayText);
            TextOut(hdc,x,y,sz,cch);
        }
        else
        {
            GrayString(hdc,NULL,NULL,(ULONG_PTR)(LPTSTR)sz,cch,x,y,0,0);
        }
    }

    MGetTextExtent(hdc, sz, cch, &x, NULL);
     if (GetFocus() == hwnd)
         SetCaretPos(x+g_cxBorder,
                    g_cyBorder);
    ShowCaret(hwnd);

#if 0
    if (hFont)
        SelectObject(hdc,hFont);
#endif

    EndPaint(hwnd,&ps);
}

void NEAR PASCAL HKMSetRules(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    SetWindowInt(hwnd, GWU_INVALID, wParam);
    SetWindowInt(hwnd, GWU_DEFAULT, lParam);
}

HFONT NEAR PASCAL HKMSetFont(HWND hwnd, HFONT wParam)
{
    HFONT lParam;
    HDC hdc;
    INT cy;

    lParam = (HFONT)GetWindowInt(hwnd,GWU_HFONT);
    SetWindowInt(hwnd,GWU_HFONT,(LONG_PTR)wParam);
    hdc = GetDC(hwnd);
    if (wParam)
        wParam = SelectObject(hdc, wParam);
    MGetTextExtent(hdc, TEXT("C"), 1, NULL, &cy);
    SetWindowInt(hwnd,GWU_YFONT,cy);
    if (wParam)
        SelectObject(hdc, wParam);
    ReleaseDC(hwnd,hdc);
    InvalidateRect(hwnd,NULL,TRUE);
    return lParam;
}

LRESULT CALLBACK HotKeyWndProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
    WORD wVirtKey;
    WORD wMods;
    RECT rc;
    HDC hdc;

    switch (wMsg)
    {
    case WM_NCCREATE:
        SetWindowBits(hwnd, GWL_EXSTYLE, WS_EX_CLIENTEDGE, WS_EX_CLIENTEDGE);
        CCCreateWindow();
        InitGlobalColors();
        return TRUE;

    case WM_DESTROY:
        CCDestroyWindow();
        break;

    case WM_CREATE:
        SetHotKey(hwnd, 0, 0, FALSE);
        HKMSetRules(hwnd, 0, 0);
        HKMSetFont(hwnd, g_hfontSystem);
        break;

    case WM_SETFOCUS:
        InvalidateRect(hwnd,NULL,TRUE);
        CreateCaret(hwnd,NULL,0,(int)GetWindowInt(hwnd,GWU_YFONT));
        ShowCaret(hwnd);
        break;

    case WM_KILLFOCUS:
        if (!GetWindowInt(hwnd, GWU_VIRTKEY))
        SetHotKey(hwnd, 0, 0, TRUE);
        DestroyCaret();
        break;

    case WM_GETDLGCODE:
        return DLGC_WANTCHARS | DLGC_WANTARROWS; // | DLGC_WANTALLKEYS;

    case HKM_SETHOTKEY:
        SetHotKey(hwnd, LOBYTE(wParam), HIBYTE(wParam), FALSE);
        break;

    case HKM_GETHOTKEY:
        return (256*(BYTE)GetWindowInt(hwnd, GWU_MODS)) +
                ((BYTE)GetWindowInt(hwnd, GWU_VIRTKEY));
        break;

    case HKM_SETRULES:
        HKMSetRules(hwnd, wParam, LOWORD(lParam));
        break;

    case WM_LBUTTONDOWN:
        SetFocus(hwnd);
        break;

    case WM_SYSKEYDOWN:
    case WM_KEYDOWN:
        switch (wParam)
        {
        case VK_RETURN:
        case VK_TAB:
        case VK_SPACE:
        case VK_DELETE:
        case VK_ESCAPE:
        case VK_BACK:
        case VK_LWIN:
        case VK_RWIN:
        case VK_APPS:
            SetHotKey(hwnd, 0, 0, TRUE);
            return DefWindowProc(hwnd,wMsg,wParam,lParam);

        case VK_MENU:
        case VK_SHIFT:
        case VK_CONTROL:
            wVirtKey = 0;
            goto SetNewHotKey;

        default:
            wVirtKey = (WORD) wParam;
SetNewHotKey:
            wMods = 0;
            if (GetKeyState(VK_CONTROL) < 0)
                wMods |= HOTKEYF_CONTROL;
            if (GetKeyState(VK_SHIFT) < 0)
                wMods |= HOTKEYF_SHIFT;
            if (GetKeyState(VK_MENU) < 0)
                wMods |= HOTKEYF_ALT;

            #define IsFUNKEY(vk) ((vk) >= VK_F1 && (vk) <= VK_F24)
            #define IsNUMKEY(vk) ((vk) >= VK_NUMPAD0 && (vk) <= VK_DIVIDE)

            //
            //  dont enforce any rules on the Function keys or
            //  on the number pad keys.
            //
            // if this combination is invalid, use the default
            if (!IsFUNKEY(wVirtKey) &&
                !IsNUMKEY(wVirtKey) &&
                (s_Combos[wMods] & GetWindowInt(hwnd, GWU_INVALID)))
            {
                wMods = (WORD)GetWindowInt(hwnd, GWU_DEFAULT);
            }

            if (lParam & F_EXT)
                wMods |= HOTKEYF_EXT;

            SetHotKey(hwnd, wVirtKey, wMods, TRUE);
            break;
        }
        break;

    case WM_SYSKEYUP:
    case WM_CHAR:
    case WM_SYSCHAR:
    case WM_KEYUP:
        if (!GetWindowInt(hwnd, GWU_VIRTKEY))
            SetHotKey(hwnd, 0, 0, TRUE);
        break;

    case WM_GETFONT:
        return GetWindowInt(hwnd,GWU_HFONT);

    case WM_SETFONT:
        return (LRESULT)(UINT_PTR)HKMSetFont(hwnd, (HFONT)wParam);

    case WM_PAINT:
        PaintHotKey(hwnd);
        break;

    case WM_ERASEBKGND:
        HideCaret(hwnd);
        hdc = GetDC(hwnd);
        GetClientRect(hwnd, &rc);
        if (IsWindowEnabled(hwnd)) {
            FillRect(hdc, &rc, g_hbrWindow);
        } else {
            FillRect(hdc, &rc, g_hbrBtnFace);
        }
        ReleaseDC(hwnd, hdc);
        // lParam = DefWindowProc(hwnd,wMsg,wParam,lParam);
        ShowCaret(hwnd);
        return TRUE;

    case WM_GETOBJECT:
        if( lParam == OBJID_QUERYCLASSNAMEIDX )
            return MSAA_CLASSNAMEIDX_HOTKEY;
        goto DoDefault;

    case WM_ENABLE:
        InvalidateRect(hwnd, NULL, TRUE);
        goto DoDefault;

    default:
DoDefault:
        return DefWindowProc(hwnd,wMsg,wParam,lParam);
    }
    return 0L;
}