/*
 * hotkey.c
 *
 *  Copyright (c) 1991,  Microsoft Corporation
 *
 *  DESCRIPTION
 *
 *        This file is for support of program manager under NT Windows.
 *        This file is/was ported from hotkey.c (program manager).
 *
 *  MODIFICATION HISTORY
 *      Initial Version: x/x/90    Author Unknown, since he didn't feel
 *                                like commenting the code...
 *
 *      NT 32b Version: 1/18/91    Jeff Pack
 *                                Intitial port to begin.
 *
 *
 */

#include "progman.h"

#define HK_SHIFT    0x0100
#define HK_CONTROL  0x0200
#define HK_ALT        0x0400
#define HK_EXT        0x0800

#define F_EXT        0x01000000L

TCHAR szHotKey[] = TEXT("pmhotkey");

/*** SetHotKey --
 *
 *
 * void APIENTRY SetHotKey(HWND hwnd, WORD hk)
 *
 * ENTRY -     HWND    hWnd
 *            WORD    hk
 *
 * EXIT  -    void
 *
 * SYNOPSIS -  ???
 *
 * WARNINGS -
 * EFFECTS  -
 *
 */

void APIENTRY SetHotKey(HWND hwnd, WPARAM hk)
{

    /* don't invalidate if it's the same
     */
    if((LONG)hk == GetWindowLong(hwnd, 0)){
        return;
    }
    SetWindowLong(hwnd, 0, hk);

    InvalidateRect(hwnd,NULL,TRUE);
}

/*** GetKeyName --
 *
 *
 * void APIENTRY GetKeyName(WORD vk, PSTR psz, BOOL fExt)
 *
 * ENTRY -     WORD    hk
 *            PSTR    psz
 *            BOOL    fExt
 *
 * EXIT  -    void
 *
 * SYNOPSIS -  ???
 *
 * WARNINGS -
 * EFFECTS  -
 *
 */

void APIENTRY GetKeyName(UINT vk, LPTSTR psz, BOOL fExt)
{
    LONG scan;
    scan = (LONG)MapVirtualKey(vk,0) << 16;
    if (fExt){
        scan |= F_EXT;
    }

    GetKeyNameText(scan,psz,50);
}

/*** PaintHotKey --
 *
 *
 * void APIENTRY PaintHotKey(register HWND hwnd)
 *
 * ENTRY -     HWND    hWnd
 *
 * EXIT  -    void
 *
 * SYNOPSIS -  ???
 *
 * WARNINGS -
 * EFFECTS  -
 *
 */

void APIENTRY PaintHotKey(register HWND hwnd)
{
    TCHAR sz[128];
    TCHAR szPlus[10];
    WORD cch;
    WORD hk;
    register HDC hdc;
#ifndef ORGCODE
    SIZE size;
#endif
    PAINTSTRUCT ps;
    int x, y;
    HANDLE hFont;
    DWORD dwColor;

    LoadString(hAppInstance, IDS_PLUS, szPlus, CharSizeOf(szPlus));

    if(hk = (WORD)GetWindowLong(hwnd, 0)){
        sz[0] = 0;
        cch = 0;
        if (hk & HK_CONTROL){
            GetKeyName(VK_CONTROL,sz,FALSE);
            lstrcat(sz,szPlus);
          }
        if (hk & HK_SHIFT){
            GetKeyName(VK_SHIFT, sz + lstrlen(sz), FALSE);
            lstrcat(sz,szPlus);
        }
        if (hk & HK_ALT){
            GetKeyName(VK_MENU, sz + lstrlen(sz), FALSE);
            lstrcat(sz,szPlus);
        }
        GetKeyName((UINT)LOBYTE(hk), sz + lstrlen(sz), hk & HK_EXT);
    }
    else{
        LoadString(hAppInstance,IDS_NONE,sz,100);
    }

    cch = (WORD)lstrlen(sz);
    HideCaret(hwnd);

    hdc = BeginPaint(hwnd,&ps);

    SetBkMode(hdc, TRANSPARENT);

    hFont = SelectObject(hdc,(HANDLE)GetWindowLong(hwnd,4));
    x = GetSystemMetrics(SM_CXBORDER);
    y = GetSystemMetrics(SM_CYBORDER);

    if (IsWindowEnabled(hwnd)){
	    dwColor = GetSysColor(COLOR_WINDOWTEXT);
    	dwColor = SetTextColor(hdc,dwColor);
        TextOut(hdc,x,y,sz,cch);
    }
    else if (dwColor = GetSysColor(COLOR_GRAYTEXT)){
        dwColor = SetTextColor(hdc,dwColor);
        TextOut(hdc,x,y,sz,cch);
        SetTextColor(hdc,dwColor);
    }
    else{
        GrayString(hdc,NULL,NULL,(DWORD)(LPTSTR)sz,cch,x,y,0,0);
    }

#ifdef ORGCODE
    x = (WORD)GetTextExtentPoint(hdc,sz,cch);
#else
    /*Used to return x/y in DWORD, now returns cx,cy in size*/
    GetTextExtentPoint(hdc, sz, cch, &size);
    x = size.cx;
#endif
    if (GetFocus() == hwnd)
    SetCaretPos(x+GetSystemMetrics(SM_CXBORDER), GetSystemMetrics(SM_CYBORDER));
    ShowCaret(hwnd);

    if (hFont){
        SelectObject(hdc,hFont);
    }

    SetBkMode(hdc, OPAQUE);

    EndPaint(hwnd,&ps);
}

/*** HotKeyWndProc --
 *
 *
 * LONG APIENTRY HotKeyWndProc(register HWND hwnd, UINT wMsg,
 *                                register WPARAM wParam, LONG lParam)
 *
 * ENTRY -     HWND    hWnd
 *            WORD    wMsg
 *            WPARAM    wParam
 *            LONG    lParam
 * EXIT  -    LONG    xxx - returns info, or zero, for nothing to return
 *
 * SYNOPSIS -  ???
 *
 * WARNINGS -
 * EFFECTS  -
 *
 */

LONG APIENTRY HotKeyWndProc(register HWND hwnd, UINT wMsg,
                             register WPARAM wParam, LONG lParam)
{
    HDC hdc;
    WORD wT;
#ifndef ORGCODE
    SIZE size;
#endif
    switch (wMsg){
    case WM_CREATE:
        SetHotKey(hwnd,0);
        SendMessage(hwnd,WM_SETFONT,(WPARAM)GetStockObject(SYSTEM_FONT),0L);
        break;

    case WM_SETFOCUS:
        InvalidateRect(hwnd,NULL,TRUE);
        CreateCaret(hwnd,NULL,0,GetWindowLong(hwnd,8));
        ShowCaret(hwnd);
        break;

    case WM_KILLFOCUS:
        if (!LOBYTE(GetWindowLong(hwnd,0))){
            SetHotKey(hwnd,0);
        }
        DestroyCaret();
        break;

    case WM_GETDLGCODE:
        return DLGC_WANTCHARS | DLGC_WANTARROWS;

    case WM_SETTEXT:
        SetHotKey(hwnd,LOWORD(lParam));
        break;

    case WM_GETTEXT:
        *(LPINT)lParam = GetWindowLong(hwnd,0);
        break;

    case WM_SETHOTKEY:
        SetHotKey(hwnd,(WPARAM) wParam);
        break;

    case WM_GETHOTKEY:
        return GetWindowLong(hwnd,0);

    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:
            SetHotKey(hwnd,0);
            return DefWindowProc(hwnd,wMsg,wParam,lParam);
        case VK_MENU:
        case VK_SHIFT:
        case VK_CONTROL:
            wParam = 0;
            /*** fall thru ***/

        default:
            if (GetKeyState(VK_CONTROL) < 0)
                wParam |= HK_CONTROL;
            if (GetKeyState(VK_SHIFT) < 0)
                wParam |= HK_SHIFT;
            if (GetKeyState(VK_MENU) < 0)
                wParam |= HK_ALT;
            if (lParam & F_EXT)
                wParam |= HK_EXT;

            // more than one shift key must be specified.  That is,
            // CONTROL+ALT, CONTROL+SHIFT, SHIFT+ALT
            // get the bitmask of shift keys and then determine whether
            // it has at least two bits set via the bitcount trick

    	    // if not enough control things are present we add them
	        // in.  so the user gets the idea.

            if ((wParam & HK_ALT) && !(wParam & HK_CONTROL)
                                  && !(wParam & HK_SHIFT)) {
                break;
            }
            else {
	            wT = (WORD)(wParam & (HK_CONTROL|HK_SHIFT|HK_ALT));
	            if (!wT || !(wT & (wT - 1)))
        		    wParam |= HK_CONTROL | HK_ALT;

	            SetHotKey(hwnd,wParam);
	            break;
            }
        }
        break;

    case WM_SYSKEYUP:
    case WM_CHAR:
    case WM_SYSCHAR:
    case WM_KEYUP:
    if (!LOBYTE((WORD)GetWindowLong(hwnd,0)))
        SetHotKey(hwnd,0);
    break;


    case WM_GETFONT:
        return GetWindowLong(hwnd,4);

    case WM_SETFONT:
        lParam = GetWindowLong(hwnd,4);
        SetWindowLong(hwnd,4,wParam);
        hdc = GetDC(hwnd);
        wParam = (WPARAM) SelectObject(hdc,(HANDLE)wParam);
        GetTextExtentPoint(hdc, TEXT("C"), 1, &size);
        SetWindowLong(hwnd, 8, size.cy);
        if (wParam){
            SelectObject(hdc,(HANDLE)wParam);
        }
        ReleaseDC(hwnd,hdc);
        InvalidateRect(hwnd,NULL,TRUE);
        return lParam;

    case WM_PAINT:
        PaintHotKey(hwnd);
        break;

    case WM_ERASEBKGND:
        HideCaret(hwnd);
        lParam = DefWindowProc(hwnd,wMsg,wParam,lParam);
        ShowCaret(hwnd);
        return lParam;

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

/*** RegisterHotKeyClass --
 *
 *
 * BOOL APIENTRY RegisterHotKeyClass(HANDLE hInstance)
 *
 * ENTRY - HWND    HANDLE hInstance
 * EXIT  - BOOL    xxx - returns return code from RegisterClass
 *
 * SYNOPSIS -  ???
 *
 * WARNINGS -  This (under 16)  took hInstance.  Under win32, hInstance is
 *                NULL, cause there is only one instance.
 * EFFECTS  -
 *
 */

BOOL APIENTRY RegisterHotKeyClass(HANDLE hInstance)
{
    WNDCLASS wc;

    wc.style = 0;
    wc.lpfnWndProc = HotKeyWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 12;
    wc.hInstance = hInstance;
    wc.hIcon = NULL;
    wc.hCursor = NULL;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = szHotKey;

    return RegisterClass(&wc);
}