/**************************************************************************
 *
 *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
 *  PURPOSE.
 *
 *  Copyright (C) 1992 - 1996 Microsoft Corporation.  All Rights Reserved.
 *
 **************************************************************************/
/****************************************************************************
 *
 *   arrow.c: Arrow control window
 *
 *   Vidcap32 Source code
 *
 ***************************************************************************/


#include <windows.h>
#include <windowsx.h>

#include <stdlib.h>

#include "arrow.h"


// a few porting macros
#ifdef _WIN32
#define SENDSCROLL(hwnd, msg, a, b, h)           \
        SendMessage(hwnd, msg, (UINT)MAKELONG(a,b), (LONG)(h))

#define EXPORT

#else
#define SENDSCROLL(hwnd, msg, a, b, h)
        SendMessage(hwnd, msg, a, MAKELONG(b,h))   // handle is in HIWORD

#endif


#ifndef LONG2POINT
    #define LONG2POINT(l, pt)               ((pt).x = (SHORT)LOWORD(l), (pt).y = (SHORT)HIWORD(l))
#endif
#define GWID(hwnd)      (GetDlgCtrlID(hwnd))


#define SHIFT_TO_DOUBLE 1
#define DOUBLECLICK     0
#define POINTSPERARROW  3
#define ARROWXAXIS      15
#define ARROWYAXIS      15

POINT ArrowUp[POINTSPERARROW] = {7,1, 3,5, 11,5};
POINT ArrowDown[POINTSPERARROW] = {7,13, 3,9, 11,9};

static    BOOL      bRight;
static    RECT      rUp, rDown;
static    LPRECT    lpUpDown;
static    FARPROC   lpArrowProc;
static    HANDLE    hParent;
BOOL      fInTimer;


#define TEMP_BUFF_SIZE    32

#define SCROLLMSG(hwndTo, msg, code, hwndId)                                     \
                          SENDSCROLL(hwndTo, msg, code, GWID(hwndId), hwndId)

/*
 * @doc EXTERNAL WINCOM
 *
 * @api LONG | ArrowEditChange | This function is helps process the WM_VSCROLL
 * message when using the Arrow controlled edit box.
 * It will increment/decrement the value in the given edit box and return
 * the new value.  Increment/decrement bounds are checked and Beep 0 is produced if
 * the user attempts to go beyond the bounds.
 *
 * @parm        HWND | hwndEdit | Specifies a handle to the edit box window.
 *
 * @parm        UINT | wParam | Specifies the <p wParam> passed to the WM_VSCROLL message.
 *
 * @parm        LONG | lMin | Specifies the minimum value bound for decrements.
 *
 * @parm        LONG | lMax | Specifies the maximum value bound for increments.
 *
 * @rdesc       Returns the updated value of the edit box.
 *
 */
LONG FAR PASCAL ArrowEditChange( HWND hwndEdit, UINT wParam,
                                 LONG lMin, LONG lMax )
{
    char achTemp[TEMP_BUFF_SIZE];
    LONG l;

    GetWindowText( hwndEdit, achTemp, TEMP_BUFF_SIZE );
    l = atol(achTemp);
    if( wParam == SB_LINEUP ) {
        /* size kluge for now */
        if( l < lMax ) {
            l++;
            wsprintf( achTemp, "%ld", l );
            SetWindowText( hwndEdit, achTemp );
        } else {
        MessageBeep( 0 );
        }
    } else if( wParam == SB_LINEDOWN ) {
        if( l > lMin ) {
            l--;
            wsprintf( achTemp, "%ld", l );
            SetWindowText( hwndEdit, achTemp );
        } else {
            MessageBeep( 0 );
        }
    }
    return( l );

}



UINT NEAR PASCAL UpOrDown()
{
    LONG pos;
    UINT retval;
    POINT pt;

    pos = GetMessagePos();
    LONG2POINT(pos,pt);
    if (PtInRect((LPRECT)&rUp, pt))
        retval = SB_LINEUP;
    else if (PtInRect((LPRECT)&rDown, pt))
        retval = SB_LINEDOWN;
    else
        retval = (UINT)(-1);      /* -1, because SB_LINEUP == 0 */

    return(retval);
}



UINT FAR PASCAL ArrowTimerProc(hWnd, wMsg, nID, dwTime)
HANDLE hWnd;
UINT wMsg;
short nID;
DWORD dwTime;
{
    UINT wScroll;

    if ((wScroll = UpOrDown()) != -1)
    {
        if (bRight == WM_RBUTTONDOWN)
            wScroll += SB_PAGEUP - SB_LINEUP;
        SCROLLMSG( hParent, WM_VSCROLL, wScroll, hWnd);
    }
/* Don't need to call KillTimer(), because SetTimer will reset the right one */
    SetTimer(hWnd, nID, 50, (TIMERPROC)lpArrowProc);
    return(0);
}


void InvertArrow(HANDLE hArrow, UINT wScroll)
{
    HDC hDC;

    lpUpDown = (wScroll == SB_LINEUP) ? &rUp : &rDown;
    hDC = GetDC(hArrow);
    ScreenToClient(hArrow, (LPPOINT)&(lpUpDown->left));
    ScreenToClient(hArrow, (LPPOINT)&(lpUpDown->right));
    InvertRect(hDC, lpUpDown);
    ClientToScreen(hArrow, (LPPOINT)&(lpUpDown->left));
    ClientToScreen(hArrow, (LPPOINT)&(lpUpDown->right));
    ReleaseDC(hArrow, hDC);
    ValidateRect(hArrow, lpUpDown);
    return;
}


LONG FAR PASCAL EXPORT ArrowControlProc(HWND hArrow, unsigned message,
                                         WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    RECT        rArrow;
    HBRUSH      hbr;
    short       fUpDownOut;
    UINT        wScroll;

    switch (message) {
/*
        case WM_CREATE:
            break;

        case WM_DESTROY:
            break;
*/

        case WM_MOUSEMOVE:
            if (!bRight)  /* If not captured, don't worry about it */
                break;

            if (lpUpDown == &rUp)
                fUpDownOut = SB_LINEUP;
            else if (lpUpDown == &rDown)
                fUpDownOut = SB_LINEDOWN;
            else
                fUpDownOut = -1;

            switch (wScroll = UpOrDown()) {
                case SB_LINEUP:
                    if (fUpDownOut == SB_LINEDOWN)
                        InvertArrow(hArrow, SB_LINEDOWN);

                    if (fUpDownOut != SB_LINEUP)
                        InvertArrow(hArrow, wScroll);

                    break;

                case SB_LINEDOWN:
                    if (fUpDownOut == SB_LINEUP)
                        InvertArrow(hArrow, SB_LINEUP);

                    if (fUpDownOut != SB_LINEDOWN)
                        InvertArrow(hArrow, wScroll);

                    break;

                default:
                    if (lpUpDown) {
                        InvertArrow(hArrow, fUpDownOut);
                        lpUpDown = 0;
                    }
                }

                break;

        case WM_RBUTTONDOWN:
        case WM_LBUTTONDOWN:
            if (bRight)
                break;

            bRight = message;
            SetCapture(hArrow);
            hParent = GetParent(hArrow);
            GetWindowRect(hArrow, (LPRECT) &rUp);
            CopyRect((LPRECT)&rDown, (LPRECT) &rUp);
            rUp.bottom = (rUp.top + rUp.bottom) / 2;
            rDown.top = rUp.bottom + 1;
            wScroll = UpOrDown();
            InvertArrow(hArrow, wScroll);
#if SHIFT_TO_DOUBLE
            if (wParam & MK_SHIFT) {
                if (message != WM_RBUTTONDOWN)
                    goto ShiftLClick;
                else
                    goto ShiftRClick;
            }
#endif
            if (message == WM_RBUTTONDOWN)
                wScroll += SB_PAGEUP - SB_LINEUP;

            SCROLLMSG(hParent, WM_VSCROLL, wScroll, hArrow);

            lpArrowProc = MakeProcInstance((FARPROC) ArrowTimerProc,ghInst);
            SetTimer(hArrow, GWID(hArrow), 200, (TIMERPROC)lpArrowProc);

            break;

        case WM_LBUTTONUP:
        case WM_RBUTTONUP:
            if ((bRight - WM_LBUTTONDOWN + WM_LBUTTONUP) == (int)message) {
                bRight = 0;
                ReleaseCapture();
                if (lpUpDown)
                    InvertArrow(hArrow,(UINT)(lpUpDown==&rUp)?
                                                        SB_LINEUP:SB_LINEDOWN);
                if (lpArrowProc) {
                    SCROLLMSG(hParent, WM_VSCROLL, SB_ENDSCROLL, hArrow);
                    KillTimer(hArrow, GWID(hArrow));

                    FreeProcInstance(lpArrowProc);
                    ReleaseCapture();
                    lpArrowProc = 0;
                }
            }
            break;

        case WM_LBUTTONDBLCLK:
ShiftLClick:
            wScroll = UpOrDown() + SB_TOP - SB_LINEUP;
            SCROLLMSG(hParent, WM_VSCROLL, wScroll, hArrow);
            SCROLLMSG(hParent, WM_VSCROLL, SB_ENDSCROLL, hArrow);

            break;

        case WM_RBUTTONDBLCLK:
ShiftRClick:
            wScroll = UpOrDown() + SB_THUMBPOSITION - SB_LINEUP;
            SCROLLMSG(hParent, WM_VSCROLL, wScroll, hArrow);
            SCROLLMSG(hParent, WM_VSCROLL, SB_ENDSCROLL, hArrow);
/*
            hDC = GetDC(hArrow);
            InvertRect(hDC, (LPRECT) &rArrow);
            ReleaseDC(hArrow, hDC);
            ValidateRect(hArrow, (LPRECT) &rArrow);
*/
            break;

        case WM_PAINT:
            BeginPaint(hArrow, &ps);
            GetClientRect(hArrow, (LPRECT) &rArrow);
            hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
            FillRect(ps.hdc, (LPRECT)&rArrow, hbr);
            DeleteObject(hbr);
            hbr = SelectObject(ps.hdc, GetStockObject(BLACK_BRUSH));
            SetTextColor(ps.hdc, GetSysColor(COLOR_WINDOWFRAME));
            SetMapMode(ps.hdc, MM_ANISOTROPIC);

            SetViewportOrgEx(ps.hdc, rArrow.left, rArrow.top, NULL);

            SetViewportExtEx(ps.hdc, rArrow.right - rArrow.left,
                                                    rArrow.bottom - rArrow.top, NULL);
            SetWindowOrgEx(ps.hdc, 0, 0, NULL);
            SetWindowExtEx(ps.hdc, ARROWXAXIS, ARROWYAXIS, NULL);
            MoveToEx(ps.hdc, 0, (ARROWYAXIS / 2), NULL);
            LineTo(ps.hdc, ARROWXAXIS, (ARROWYAXIS / 2));
/*
            Polygon(ps.hdc, (LPPOINT) Arrow, 10);
*/
            Polygon(ps.hdc, (LPPOINT) ArrowUp, POINTSPERARROW);
            Polygon(ps.hdc, (LPPOINT) ArrowDown, POINTSPERARROW);
            SelectObject(ps.hdc, hbr);

            EndPaint(hArrow, &ps);

            break;

        default:
            return(DefWindowProc(hArrow, message, wParam, lParam));

            break;
        }

    return(0L);
}

#ifndef _WIN32
#pragma alloc_text(_INIT, ArrowInit)
#endif


BOOL FAR PASCAL ArrowInit(HANDLE hInst)
{
    WNDCLASS wcArrow;

    wcArrow.lpszClassName = SPINARROW_CLASSNAME;
    wcArrow.hInstance     = hInst;
    wcArrow.lpfnWndProc   = ArrowControlProc;
    wcArrow.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wcArrow.hIcon         = NULL;
    wcArrow.lpszMenuName  = NULL;
    wcArrow.hbrBackground = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
    wcArrow.style         = CS_HREDRAW | CS_VREDRAW;
#if DOUBLECLICK
    wcArrow.style         |= CS_DBLCLKS;
#endif
    wcArrow.cbClsExtra    = 0;
    wcArrow.cbWndExtra    = 0;

    if (!RegisterClass(&wcArrow))
        return FALSE;

    return TRUE;
}
