
/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples. 
*       Copyright (C) 1993 Microsoft Corporation.
*       All rights reserved. 
*       This source code is only intended as a supplement to 
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the 
*       Microsoft samples programs.
\******************************************************************************/

/****************************** Module Header *******************************
* Module Name: ctrlproc.c
*
* Contains the window procedures for controls in the dialog being edited.
*
* Functions:
*
*    DialogCtrlWndProc()
*    CtrlWndProc()
*    ChildWndProc()
*    DrawOwnerDrawButton()
*
* Comments:
*
****************************************************************************/

#include "dlgedit.h"
#include "dlgfuncs.h"
#include "dlgextrn.h"



/************************************************************************
* DialogCtrlWndProc
*
* This is the window procedure that subclasses the dialog being
* edited.  It handles a few messages that have to be special-cased
* for the dialog.  Most messages, however, are passed on to the
* generic control subclass procedure (CtrlWndProc).
*
* Arguments:
*    HWND - Handle to the dialog window
*    UINT  - window message
*    WPARAM - message parameter
*    LPARAM - message parameter
*
* Returns:
*    The default window procedure
*
************************************************************************/

WINDOWPROC DialogCtrlWndProc(
    HWND hwnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam)
{
    POINT pt;
    BOOL fTracking;

    switch (msg) {
        case WM_NCPAINT:
        case WM_PAINT:
            if (gfTrackRectShown) {
                fTracking = TRUE;
                HideTrackRect();
            }
            else {
                fTracking = FALSE;
            }

            /*
             * Allow the dialog to paint first.
             */
            CallWindowProc((WNDPROC)CtrlWndProc, hwnd, msg, wParam, lParam);

            /*
             * Draw the handles if the dialog is selected.
             */
            if (gfDlgSelected) {
                HDC hDC;

                hDC = GetWindowDC(hwnd);
                DrawHandles(hwnd, hDC, TRUE);
                ReleaseDC(hwnd, hDC);
            }

            if (fTracking)
                ShowTrackRect();

            break;

        case WM_LBUTTONDOWN:
            /*
             * Discard all mouse messages during certain operations.
             */
            if (gfDisabled)
                break;

            /*
             * Also, be sure any outstanding changes get applied
             * without errors.
             */
            if (!StatusApplyChanges())
                break;

            /*
             * Check to see if we are in a normal mode.  If we are
             * in some other mode, like dragging a new control,
             * we want to ignore this mouse down and wait for the
             * mouse up.  For instance, this can happen when the
             * Duplicate command is selected from the Edit menu.
             */
            if (gState == STATE_NORMAL) {
                ((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y);
                MapDlgClientPoint(&pt, TRUE);

                /*
                 * Is the dialog selected and was one of its handles hit?
                 * If so, call CtrlButtonDown as if we are a drag window.
                 */
                if (gfDlgSelected &&
                        HandleHitTest(hwnd, pt.x, pt.y) != DRAG_CENTER) {
                    CtrlButtonDown(hwnd, pt.x, pt.y, TRUE);
                }
                else {
                    /*
                     * If the click was within the client area and
                     * there is not a tool selected, start an outline
                     * selection operation.  Otherwise call CtrlButtonDown
                     * which will either begin dragging the dialog or
                     * dragging the new control.
                     */
                    if (gCurTool == W_NOTHING &&
                            PtInRect(&grcDlgClient, pt))
                        OutlineSelectBegin(pt.x, pt.y);
                    else
                        CtrlButtonDown(hwnd, pt.x, pt.y, FALSE);
                }
            }

            break;

        case WM_MOUSEMOVE:
            ((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y);

            /*
             * If we are not dragging a new control, then this
             * message is for the dialog, and we map it from
             * the dialog client to the dialog window.
             */
            if (gState != STATE_DRAGGINGNEW)
                MapDlgClientPoint(&pt, TRUE);

            /*
             * Now we process the mouse move message.  If the dialog is
             * selected, and if we are not dragging a new control, we
             * pass in a TRUE for fDragWindow.  This is because the
             * dialog itself does not have a separate drag window like
             * controls, and if it is selected this message needs to
             * be processed as if a drag window was hit.
             */
            CtrlMouseMove(hwnd,
                    (gfDlgSelected && gState != STATE_DRAGGINGNEW) ?
                    TRUE : FALSE, pt.x, pt.y);

            break;

        case WM_LBUTTONUP:
            ((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y);

            /*
             * If we are not dragging a new control, then this
             * message is for the dialog, and we map it from
             * the dialog client to the dialog window.
             */
            if (gState != STATE_DRAGGINGNEW)
                MapDlgClientPoint(&pt, TRUE);

            CtrlButtonUp(pt.x, pt.y);

            break;

        case WM_DRAWITEM:
            return DrawOwnerDrawButton((LPDRAWITEMSTRUCT)lParam);

        default:
            return CallWindowProc(
                    (WNDPROC)CtrlWndProc, hwnd, msg, wParam, lParam);
    }

    return 0L;
}



/************************************************************************
* CtrlWndProc
*
* This is the window procedure that subclasses all of the controls.
* The dialog being edited will also pass messages that it does not
* handle through this procedure.
*
* Arguments:
*    HWND - Handle to the control window
*    UINT  - window message
*    WPARAM - message parameter
*    LPARAM - message parameter
*
* Returns:
*    The default window procedure
*
************************************************************************/

WINDOWPROC CtrlWndProc(
    HWND hwnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam)
{
    POINT pt;

    switch (msg) {
        case WM_NCPAINT:
        case WM_PAINT:
            {
                BOOL fTracking;

                if (gfTrackRectShown) {
                    fTracking = TRUE;
                    HideTrackRect();
                }
                else {
                    fTracking = FALSE;
                }

                /*
                 * Allow the control to paint first.
                 */
                CallWindowProc((WNDPROC)PCFROMHWND(hwnd)->pwcd->pfnOldWndProc,
                        hwnd, msg, wParam, lParam);

                if (fTracking)
                    ShowTrackRect();
            }

            break;

        case WM_SETCURSOR:
            /*
             * Defeat the system changing cursors on us.  We do it based
             * on our own hit testing.
             */
            return TRUE;

        case WM_TIMER:
            PreDragTimeout(hwnd, TRUE);
            break;

        case WM_LBUTTONDOWN:
            ((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y);
            CtrlButtonDown(hwnd, pt.x, pt.y, FALSE);
            break;

        case WM_MOUSEMOVE:
            ((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y);
            CtrlMouseMove(hwnd, FALSE, pt.x, pt.y);
            break;

        case WM_LBUTTONUP:
            ((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y);
            CtrlButtonUp(pt.x, pt.y);
            break;

        case WM_LBUTTONDBLCLK:
            if (gfDisabled)
                break;

            /*
             * Also, be sure any outstanding changes get applied
             * without errors.
             */
            if (!StatusApplyChanges())
                break;

            StylesDialog();

            break;

        case WM_NCHITTEST:
            return HTCLIENT;

        case WM_RBUTTONDOWN:
        case WM_MBUTTONDOWN:
        case WM_RBUTTONDBLCLK:
        case WM_MBUTTONDBLCLK:
            /*
             * Helps prevent anything from happening when
             * the middle or right mouse buttons are pressed
             * (or doubleclicked).
             */
            break;

        case WM_MOUSEACTIVATE:
            /*
             * Defeat this message so that mouse clicks do not activate
             * the control.
             */
            return MA_NOACTIVATE;

        case WM_DESTROY:
            /*
             * Unsubclass the control.
             */
            SetWindowLong(hwnd, GWL_WNDPROC,
                    (DWORD)(WNDPROC)(PCFROMHWND(hwnd)->pwcd->pfnOldWndProc));

            UNSETPCINTOHWND(hwnd);

            break;

        default:
            return CallWindowProc(
                    (WNDPROC)PCFROMHWND(hwnd)->pwcd->pfnOldWndProc,
                    hwnd, msg, wParam, lParam);
    }

    return 0L;
}



/************************************************************************
* ChildWndProc
*
* This is the window procedure that subclasses all of the children
* of controls that have them.  Currently this is only comboboxes.
*
* Arguments:
*    HWND - Handle to the child control window
*    UINT  - window message
*    WPARAM - message parameter
*    LPARAM - message parameter
*
* Returns:
*    The default window procedure
*
************************************************************************/

WINDOWPROC ChildWndProc(
    HWND hwnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam)
{
    /*
     * Is this a mouse message?
     */
    if (msg >= WM_MOUSEFIRST && msg <= WM_MOUSELAST) {
        POINT pt;

        /*
         * Yes, convert the coordinates and send it to the parent.
         */
        ((pt).x = (SHORT)LOWORD(lParam), (pt).y = (SHORT)HIWORD(lParam));
        ClientToScreen(hwnd, &pt);
        ScreenToClient(GetParent(hwnd), &pt);
        POINT2LONG(pt, lParam);
        SendMessage(GetParent(hwnd), msg, wParam, lParam);
        return FALSE;
    }
    else if (msg == WM_SETCURSOR) {
        /*
         * Defeat the system changing cursors on us.  We do it based
         * on our own hit testing.
         */
        return TRUE;
    }
    else if (msg == WM_NCDESTROY) {
        /*
         * Unsubclass the child.
         */
        SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)(WNDPROC)GETCHILDPROC(hwnd));

        /*
         * When destroying the child window, we must be sure and
         * remove the properties associated with it.
         */
        UNSETCHILDPROC(hwnd);

        return 0;
    }
    else {
        /*
         * A benign message, call the class proc.
         */
        return CallWindowProc(GETCHILDPROC(hwnd), hwnd, msg, wParam, lParam);
    }
}



/************************************************************************
* DrawOwnerDrawButton
*
* Draws the owner-draw buttons.
*
* Arguments:
*    LPDRAWITEMSTRUCT -  owner-draw item structure
*
* Returns:
*    TRUE if the button was drawn; FALSE if the button could not be drawn.
*
************************************************************************/

BOOL DrawOwnerDrawButton(
    LPDRAWITEMSTRUCT lpdis)
{
    TCHAR szText[CCHTEXTMAX];

    if (lpdis->CtlType != ODT_BUTTON || lpdis->itemAction != ODA_DRAWENTIRE)
        return FALSE;

    RoundRect(lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top,
            lpdis->rcItem.right, lpdis->rcItem.bottom, 4, 4);

    GetWindowText(lpdis->hwndItem, szText, CCHTEXTMAX);
    SetBkMode(lpdis->hDC, TRANSPARENT);

    if (gcd.hFont)
        SelectObject(lpdis->hDC, gcd.hFont);

    DrawText(lpdis->hDC, szText, -1, &lpdis->rcItem,
            DT_CENTER | DT_NOCLIP | DT_VCENTER | DT_SINGLELINE);

    return TRUE;
}
