//===========================================================================
// CAL.CPP... Would be CALIBRATE.CPP, but that's not 8.3 compliant :(
//
// Functions:
//
//    CalInitProc  
//    CalXYProc  	
//    CalSliderProc
//    CalPovProc
//    CalStateChange
//    CollectCalInfo
//    EnableXYWindows
//    GetOEMCtrlString
//
//===========================================================================

// This is necessary or PSH_WIZARD_LITE will not be defined!
#if (_WIN32_IE < 0x0500)
    #undef _WIN32_IE
    #define  _WIN32_IE  0x0500
#endif

// This is necessary for UnregisterDeviceNotification!
#if (WINVER < 0x0500)
    #undef WINVER
    #define WINVER 0x0500
#endif

// Uncomment if we decide to calibrate the POV!
#define WE_SUPPORT_CALIBRATING_POVS	1

#include "cplsvr1.h"
//#include <windowsx.h>

#include <mmsystem.h>
#include <malloc.h>


#include "cplsvr1.h"

#ifdef _UNICODE
    #include <winuser.h>  // For RegisterDeviceNotification stuff!
    #include <dbt.h>      // for DBT_ defines!!!
#endif // _UNICODE

// remove to remove support for calibration of deadzones!
//#define DEADZONE 1

#include "resource.h"
#include "cal.h"			// Data to be shared with other modules
#include "calocal.h"		// Local Data to this module
#include "dicputil.h"	// for OnContextMenu and OnHelp
#include "pov.h"			// for SetDegrees()

#include <prsht.h>      // includes the property sheet functionality

#include <shlwapi.h>    // for the Str... functions!

#include <regstr.h>		// for pre-defined Registry string names
#include "Gradient.h" 	// for Gradient Fill Slider!

// Local function prototypes!
static void UpdateXYLabel           (const HWND hDlg);
static BOOL UpdateProgressLabel (const HWND hDlg);
// myitoa prototype is in cplsvr1.h
static void reverse                 (LPTSTR string);
static void RawDataSelected     (const HWND hWnd, BOOL bEnable);
static void WizFinish               (const HWND hWnd);

// Calibration procedures!
LRESULT CALLBACK CalInitProc    (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK CalXYProc          (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK CalSliderProc  (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

#ifdef WE_SUPPORT_CALIBRATING_POVS
LRESULT CALLBACK CalPovProc   (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
#endif //WE_SUPPORT_CALIBRATING_POVS

VOID CALLBACK TimerProc             (const HWND hWnd, UINT uMsg, UINT  idEvent, DWORD  dwTime);


//static void EnableSliderWindows	(const HWND hWnd, BOOL bEnable);


HWND ProgWndCal;                 // Handle to Progress Control Window
//DWORD dwUsage;  				 // Usage flags for the device being calibrated!
char nCalState;                  // Flag state variable!
char nPrevCalState;
LPMYJOYRANGE pRanges;        // Ranges recieved by the Calibration!
BOOL bShowRawData;
LPWSTR lpwszTypeName;        // Set in WM_INIT, Used in GetOEMCtrlString
LPDIJOYCONFIG_DX5 pJoyConfig; // DIJC_REGHWCONFIGTYPE information about the device!


// 
extern LPMYJOYRANGE lpCurrentRanges;
extern LPDIJOYSTATE lpDIJoyState;       // Defined in TEST.CPP
extern CDIGameCntrlPropSheet_X *pdiCpl;
extern HINSTANCE  ghInst;

HFONT hTitleFont;

static LPDIRECTINPUTDEVICE2 pdiDevice2; 
static CGradientProgressCtrl *pGradient;
static BOOL bGradient;

//****************************************************************************
//
//   FUNCTION: CreateWizard(HWND hwndOwner, LPARAM lParam)
//
//   PURPOSE: Create the Wizard control. 
//
//   COMMENTS:
//	
//      This function creates the wizard property sheet.
//****************************************************************************
short CreateWizard(const HWND hwndOwner, LPARAM lParam)
{
#ifdef WE_SUPPORT_CALIBRATING_POVS
    const BYTE nTempArray[]  = {IDD_INITIAL,                IDD_XY,                     IDD_SLIDER,   IDD_POV };
    const DLGPROC pDlgProc[] = {(DLGPROC)CalInitProc,  (DLGPROC)CalXYProc,  (DLGPROC)CalSliderProc,   (DLGPROC)CalPovProc };
#else
    const BYTE nTempArray[]  = {IDD_INITIAL,                IDD_XY,                     IDD_SLIDER };
    const DLGPROC pDlgProc[] = {(DLGPROC)CalInitProc,  (DLGPROC)CalXYProc,  (DLGPROC)CalSliderProc };
#endif

    HPROPSHEETPAGE  *pPages = new (HPROPSHEETPAGE[sizeof(nTempArray)/sizeof(BYTE)]);
    if( !pPages ) {
        return 0;
    }

    // Allocate and Zero the Page header memory
    PROPSHEETHEADER *ppsh = new (PROPSHEETHEADER);
    if( !ppsh ) {
        delete[] (pPages);
        return 0;
    }

    ZeroMemory(ppsh, sizeof(PROPSHEETHEADER));

    ppsh->dwSize     = sizeof(PROPSHEETHEADER);
    ppsh->dwFlags    = PSH_WIZARD_LITE | PSH_NOAPPLYNOW | PSH_USEICONID; 
    ppsh->hwndParent = hwndOwner;
    ppsh->pszIcon     = MAKEINTRESOURCE(IDI_GCICON);
    ppsh->hInstance  = ghInst;
    ppsh->phpage      = pPages;

    ppsh->pszbmWatermark = MAKEINTRESOURCE(IDB_CALHD);

    PROPSHEETPAGE *ppsp = new (PROPSHEETPAGE);
    if( !ppsp ) {
        delete[] (pPages);
        delete (ppsh);

        return 0;
    }

    ZeroMemory(ppsp, sizeof(PROPSHEETPAGE));

    ppsp->dwSize      = sizeof(PROPSHEETPAGE);
// ppsp->pszTitle    = MAKEINTRESOURCE(nTabID);
    ppsp->hInstance   = ghInst;
    ppsp->lParam        = lParam;

    while( ppsh->nPages < (sizeof(nTempArray)/sizeof(BYTE)) ) {
        ppsp->pfnDlgProc  = pDlgProc[ppsh->nPages];
        ppsp->pszTemplate = MAKEINTRESOURCE(nTempArray[ppsh->nPages]);

        ppsh->phpage[ppsh->nPages] = CreatePropertySheetPage(ppsp);

        ppsh->nPages++;
    }

    if( ppsp )
        delete (ppsp);

    short nRet = (short)PropertySheet(ppsh);

    if( pPages )
        delete[] (pPages);

    // Clean up!
    if( ppsh )
        delete (ppsh);

    return(nRet);
}

//*******************************************************************************
//
//   FUNCTION: CalInitProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//   PURPOSE: 	Procedure for Start-up screen
//
//   COMMENTS:	This function is responsible for display of text and bitmap.
//					Since it is also the only page that is Guarenteed to be hit,
//					it is also responsible for creating, deleteing, and storing 
//					everything for the calibration wizard.
//	
//*******************************************************************************
LRESULT CALLBACK CalInitProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static HFONT hBoldFont;
    static PVOID hNotifyDevNode;     

    switch( uMsg ) {
    case WM_LBUTTONDOWN:
        // Click Drag service for PropSheets!
        PostMessage(GetParent(hWnd), WM_NCLBUTTONDOWN, (WPARAM)HTCAPTION, lParam);
        break;

#ifdef _UNICODE
    case WM_DEVICECHANGE:  
        if( (UINT)wParam == DBT_DEVICEREMOVECOMPLETE )
            ::PostMessage(GetParent(hWnd), WM_COMMAND, IDCANCEL, 0);
        break;
#endif
        // OnInit
    case WM_INITDIALOG:
        // Init to FALSE to turn off Gradient fill!
        bGradient = FALSE;

        // According to knowlege base artical Q138505, this is the prescribed method of removing 
        // the context sensitive help '?' from the title bar.
        {
            LONG style = ::GetWindowLong(GetParent(hWnd), GWL_EXSTYLE);
            style &= ~WS_EX_CONTEXTHELP;

            HWND hParent = GetParent(hWnd);

            ::SetWindowLong(hParent, GWL_EXSTYLE, style);


            // Set up the Device Notification
#ifdef _UNICODE
            RegisterForDevChange(hWnd, &hNotifyDevNode);
#endif
            HDC myDC = GetDC(hWnd);
            if( myDC ) {     // Prefix Whistler 45095
                hTitleFont = CreateFont(-MulDiv(8, GetDeviceCaps(myDC, LOGPIXELSY), 72), 0, 0, 
                                        0, FW_SEMIBOLD, FALSE, 
                                        FALSE, FALSE, DEFAULT_CHARSET, 
                                        OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, 
                                        DEFAULT_PITCH | FF_DONTCARE, TEXT("MS Shell Dlg"));

                // Do the Create font thing...
                hBoldFont = CreateFont(-MulDiv(15, GetDeviceCaps(myDC, LOGPIXELSY), 72), 0, 0, 
                                       0, FW_SEMIBOLD, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, 
                                       PROOF_QUALITY, DEFAULT_PITCH | FF_ROMAN, TEXT("MS Shell Dlg")); 

                ReleaseDC(hWnd, myDC);
            }
            
            if( hBoldFont )
                ::SendDlgItemMessage(hWnd, IDC_INIT_TITLE, WM_SETFONT, (WPARAM)hBoldFont, TRUE);

            CenterDialog(hWnd);

            ::PostMessage(hParent, PSM_SETWIZBUTTONS, 0, (LPARAM)(DWORD)PSWIZB_NEXT);

            bShowRawData = FALSE;

            // Allocate the memory for the ranges!
            pRanges = new MYJOYRANGE;
            assert(pRanges);

            // Set Everything to
            ZeroMemory(pRanges, sizeof(MYJOYRANGE));

            // Get the "best guess" ranges...
            CopyMemory(pRanges, lpCurrentRanges, sizeof(MYJOYRANGE));

            pdiCpl->GetDevice(&pdiDevice2);

            // Attempt to Set them... die if you can't!
            SetMyRanges(pdiDevice2, pRanges, pdiCpl->GetStateFlags()->nAxis);

            if( FAILED(GetLastError()) ) {
                Error(hWnd, (short)IDS_USER_MODE_TITLE, (short)IDS_USER_MODE);
                PostMessage(GetParent(hWnd), WM_SYSCOMMAND, SC_CLOSE, 0L);
            }

            pJoyConfig = new(DIJOYCONFIG_DX5);
            assert (pJoyConfig);

            pJoyConfig->dwSize = sizeof (DIJOYCONFIG_DX5);

            LPDIRECTINPUTJOYCONFIG pdiJoyConfig;
            pdiCpl->GetJoyConfig(&pdiJoyConfig);

            HRESULT hres;

            // Retrieve and store Hardware Configuration about the device!
            hres = pdiJoyConfig->GetConfig(pdiCpl->GetID(), (LPDIJOYCONFIG)pJoyConfig, DIJC_REGHWCONFIGTYPE | DIJC_GUIDINSTANCE);

            if( SUCCEEDED(hres) ) {
                bPolledPOV = (pJoyConfig->hwc.hws.dwFlags & JOY_HWS_HASPOV) && (pJoyConfig->hwc.hws.dwFlags & JOY_HWS_POVISPOLL);
                CalibratePolledPOV( &pJoyConfig->hwc );
            }

        }
        break;

        // Change the background of all Static text fields to WHITE
    case WM_CTLCOLORSTATIC:
        return(LRESULT)GetStockObject(WHITE_BRUSH);

    case WM_DESTROY:
        if( pJoyConfig )
            delete (pJoyConfig);

        if( lpwszTypeName )
            LocalFree(lpwszTypeName);

        pdiDevice2->Unacquire();
        SetCalibrationMode( FALSE );

        if( hTitleFont )
            DeleteObject((HGDIOBJ)hTitleFont);

        if( hBoldFont )
            DeleteObject((HGDIOBJ)hBoldFont);

// if you call this function you will hang up the system for 30 seconds or more!!!
#ifdef _UNICODE
        if( hNotifyDevNode )
            UnregisterDeviceNotification(hNotifyDevNode);
#endif // _UNICODE
        break;
    }               
    return(DefWindowProc(hWnd, uMsg, wParam, lParam));
}

//*******************************************************************************
//
//   FUNCTION: CalXYProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//   PURPOSE: 	Procedure for first three stages of calibration
//
//   COMMENTS:	This function is responsible for capture of X/Y and Center values!
//	
//*******************************************************************************
LRESULT CALLBACK CalXYProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch( uMsg ) {
    case WM_LBUTTONDOWN:
        // Click Drag service for PropSheets!
        PostMessage(GetParent(hWnd), WM_NCLBUTTONDOWN, (WPARAM)HTCAPTION, lParam);
        break;

        // OnInit
    case WM_INITDIALOG:
        {
            // set up the local globals
            nCalState = JCS_XY_CENTER1;
            nPrevCalState = JCS_INIT;

            // Get the JoyConfig Interface Pointer!
            LPDIRECTINPUTJOYCONFIG pdiJoyConfig;
            pdiCpl->GetJoyConfig(&pdiJoyConfig);

            if( SUCCEEDED(pdiJoyConfig->SetCooperativeLevel(hWnd, DISCL_EXCLUSIVE | DISCL_BACKGROUND)) ) {
                // Set the font for the 
                ::SendDlgItemMessage(hWnd, IDC_WIZARD_MSG_HDR, WM_SETFONT, (WPARAM)hTitleFont, TRUE);

                lpwszTypeName = StrDupW(pJoyConfig->wszType);

                // This sets up the Windows and the global ProgWndCal!
                UpdateXYLabel(hWnd);

                // Set up for first round
                CalStateChange( hWnd, (BYTE)pJoyConfig->hwc.hws.dwFlags );

                VERIFY(SUCCEEDED(SetCalibrationMode(TRUE)));
                VERIFY(FAILED(pdiDevice2->Acquire()));
            }
        }
        break;

        // Change the background of all Static text fields to WHITE
    case WM_CTLCOLORSTATIC:
        // We only want to paint the background for the items in the top white rectangle!
        switch( GetDlgCtrlID((HWND)lParam) ) {
        case IDC_WIZARD_MSG:
        case IDC_HEADERFRAME:
        case IDC_WIZARD_MSG_HDR:
            return(LRESULT)GetStockObject(WHITE_BRUSH);
        }
        return(FALSE);

        // OnNotify
    case WM_NOTIFY:
        switch( ((NMHDR FAR *) lParam)->code ) {
        case PSN_KILLACTIVE:
            KillTimer(hWnd, ID_CAL_TIMER);
            break;

        case PSN_RESET:
            // reset to the original values
            KillTimer(hWnd, ID_CAL_TIMER);
            break;

        case PSN_SETACTIVE:
            SetTimer( hWnd, ID_CAL_TIMER, CALIBRATION_INTERVAL, (TIMERPROC)TimerProc);

            // Sorry, you can't go back to the first page... 
            if( nCalState > JCS_XY_CENTER1 )
                ::PostMessage(GetParent(hWnd), PSM_SETWIZBUTTONS, 0, (LPARAM)(DWORD)PSWIZB_NEXT | PSWIZB_BACK);
            else
                ::PostMessage(GetParent(hWnd), PSM_SETWIZBUTTONS, 0, (LPARAM)(DWORD)PSWIZB_NEXT);
            break;

        case PSN_WIZBACK:
            // Determine what the next calibration stage is!
            // Look out... we're backing up!
            if( nCalState == nPrevCalState )
                nPrevCalState--;

            nCalState = nPrevCalState;

            CalStateChange(hWnd, (BYTE)pJoyConfig->hwc.hws.dwFlags);

            // No more backing up!
            if( nCalState == JCS_XY_CENTER1 )
                ::PostMessage(GetParent(hWnd), PSM_SETWIZBUTTONS, 0, (LPARAM)(DWORD)PSWIZB_NEXT);

            SetWindowLongPtr(hWnd,  DWLP_MSGRESULT, (nCalState < JCS_XY_CENTER1) ? IDD_INITIAL : -1);
            return(nCalState < JCS_XY_CENTER1) ?  IDD_INITIAL : -1;


        case PSN_WIZNEXT:
            nPrevCalState = nCalState;

            ::PostMessage(GetParent(hWnd), PSM_SETWIZBUTTONS, 0, (LPARAM)(DWORD)PSWIZB_NEXT | PSWIZB_BACK);

#if 0
            // Determine what the next calibration stage is!
    #ifndef DEADZONE
            //while ((!(pdiCpl->GetStateFlags()->nAxis & 1<<nCalState++)) && (nCalState < JCS_FINI));
            nCalState++;
    #else
            nCalState++;
    #endif // DEADZONE
#endif

            while( (!(pdiCpl->GetStateFlags()->nAxis & (1<<nCalState++) )) && (nCalState < JCS_FINI) );

            if( nCalState > JCS_FINI )
                ::PostMessage(GetParent(hWnd), PSM_SETWIZBUTTONS, 0, (LPARAM)(DWORD)PSWIZB_FINISH | PSWIZB_BACK);
            else if( nCalState < JCS_Z_MOVE )
                CalStateChange( hWnd, (BYTE)pJoyConfig->hwc.hws.dwFlags );


            SetWindowLongPtr(hWnd,  DWLP_MSGRESULT, (nCalState < JCS_Z_MOVE) ? -1 : IDD_SLIDER );
            return(nCalState < JCS_Z_MOVE) ?  -1 : IDD_SLIDER;


        default:
            return(FALSE);
        }
        break;

        // OnCommand
    case WM_COMMAND:
        switch( LOWORD(wParam) ) {
        case IDC_RAWDATA:
            RawDataSelected(hWnd, bShowRawData = !bShowRawData);
            break;
        }
        break;

        // OnDestroy
    case WM_DESTROY:
        if( pRanges ) {
            delete (pRanges);
            pRanges = NULL;
        }
        break;

    default:
        return(FALSE);
    }
    return(TRUE);   
}


//****************************************************************************
//
//   FUNCTION: CalSliderProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//   PURPOSE: 	Procedure 
//
//   COMMENTS:
//	
//      This function creates the wizard property sheet.
//****************************************************************************
LRESULT CALLBACK CalSliderProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch( uMsg ) {
    case WM_LBUTTONDOWN:
        // Click Drag service for PropSheets!
        PostMessage(GetParent(hWnd), WM_NCLBUTTONDOWN, (WPARAM)HTCAPTION, lParam);
        break;

    case WM_INITDIALOG:
        // Set the Control font!
        ::SendDlgItemMessage(hWnd,IDC_WIZARD_MSG_HDR, WM_SETFONT, (WPARAM)hTitleFont, TRUE);

#ifdef DEADZONE
        ::SendDlgItemMessage(hWnd, IDC_DEADZONE_TITLE,   WM_SETFONT, (WPARAM)hTitleFont, TRUE);
        ::SendDlgItemMessage(hWnd, IDC_SATURATION_TITLE, WM_SETFONT, (WPARAM)hTitleFont, TRUE);
#endif //DEADZONE

        // Setup the Progress bar!
        ProgWndCal = GetDlgItem(hWnd, IDC_SLIDER);

        // do the Gradient fill maddness!
        {
            HDC hDC = ::GetWindowDC(hWnd);
            if( hDC ) {
                bGradient = (BOOL)(GetDeviceCaps(hDC, NUMCOLORS) < 0);

                if( bGradient ) {
                    pGradient = new (CGradientProgressCtrl);
                    pGradient->SubclassWindow(GetDlgItem(hWnd, IDC_SLIDER)); 
                    pGradient->SetDirection(HORIZONTAL);
                    //pGradient->ShowPercent();
                    pGradient->SetStartColor(COLORREF(RGB(0,0,255)));
                    pGradient->SetEndColor(COLORREF(RGB(0,0,0)));
                    pGradient->SetBkColor(COLORREF(RGB(180,180,180)));
                }
                ::ReleaseDC(hWnd, hDC);
            }
        }

        if( nCalState < JCS_FINI ) {
            // UpdateProgressLabel MUST be called Before CalStateChange!!!
            UpdateProgressLabel(hWnd);

            // If we're not using the gradient control, set the bar
            // colour PBM_SETBARCOLOR is WM_USER+9... YES, it's undocumented...
            if( !bGradient ) {
                ::PostMessage(ProgWndCal, WM_USER+9, 0, (LPARAM)ACTIVE_COLOR);
            }
        } else {
           ::PostMessage(GetParent(hWnd), PSM_PRESSBUTTON, (WPARAM)(int)PSBTN_NEXT, 0);
        }
        break;

    case WM_DESTROY:
        if( bGradient )
            if( pGradient )
                delete (pGradient);
        break;

        // OnCommand
    case WM_COMMAND:
        switch( LOWORD(wParam) ) {
        case IDC_RAWDATA:
            RawDataSelected(hWnd, bShowRawData = !bShowRawData);

            if( bGradient )
                pGradient->ShowPercent(bShowRawData);
            break;
        }
        break;

        // Change the background of all Static text fields to WHITE
    case WM_CTLCOLORSTATIC:
        // We only want to paint the background for the items in the top white rectangle!
        switch( GetDlgCtrlID((HWND)lParam) ) {
        case IDC_WIZARD_MSG:
        case IDC_HEADERFRAME:
        case IDC_WIZARD_MSG_HDR:
            return(LRESULT)GetStockObject(WHITE_BRUSH);
        }
        return(FALSE);

    case WM_NOTIFY:
        switch( ((NMHDR FAR *) lParam)->code ) {
        case PSN_KILLACTIVE:
            KillTimer(hWnd, ID_CAL_TIMER);
            break;

        case PSN_SETACTIVE:
            // Set up for first round
            CalStateChange( hWnd, (BYTE)NULL );
            SetTimer( hWnd, ID_CAL_TIMER, CALIBRATION_INTERVAL, (TIMERPROC)TimerProc);
            ::PostMessage(GetParent(hWnd), PSM_SETWIZBUTTONS, 0, (LPARAM)(DWORD)PSWIZB_NEXT | PSWIZB_BACK);
            break;

        case PSN_WIZBACK:
            // Determine what the previous calibration stage is!
            if( nCalState == nPrevCalState ) {
                DWORD dwAxis = pdiCpl->GetStateFlags()->nAxis;
                nPrevCalState --;

                while( ( !(dwAxis & (1<<(--nPrevCalState)) ) ) && (nPrevCalState > JCS_XY_CENTER2) ){
                    ;
                }
                
                nPrevCalState ++;
            }

            nCalState = nPrevCalState;

            if( nCalState > JCS_XY_CENTER2 ) {
                // UpdateProgressLabel MUST be called Before CalStateChange!!!
                UpdateProgressLabel(hWnd);

                CalStateChange( hWnd, (BYTE)NULL );
            }

            SetWindowLongPtr(hWnd,  DWLP_MSGRESULT, (nCalState < JCS_Z_MOVE) ? IDD_XY : -1);
            return(nCalState < JCS_Z_MOVE) ?  IDD_XY : -1;

        case PSN_WIZNEXT:
            nPrevCalState = nCalState;

            // Determine what the next calibration stage is!
            while( (!(pdiCpl->GetStateFlags()->nAxis & 1<<nCalState++)) && (nCalState < JCS_FINI) );

            if( nCalState <=  JCS_S1_MOVE ) {
                UpdateProgressLabel(hWnd);
                
#ifdef WE_SUPPORT_CALIBRATING_POVS
            } else if( bPolledPOV ) {
                nCalState = JCS_S1_MOVE + 1;
                
                SetWindowLongPtr(hWnd,  DWLP_MSGRESULT, IDD_POV );

                return(IDD_POV);
#endif
            } else {
                // Remove the dialog items you no longer need...
                //EnableSliderWindows(hWnd, FALSE);
                const short nCtrlArray[] = {IDC_SLIDER, IDC_RAWDATA, IDC_RAWX, IDC_RAWXOUTPUT, IDC_JOYLIST2_LABEL};
                BYTE nSize = sizeof(nCtrlArray)/sizeof(short);

                do {
                    SetWindowPos( GetDlgItem(hWnd, nCtrlArray[--nSize]), NULL, NULL, NULL, NULL, NULL, 
                                  SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_HIDEWINDOW );
                } while( nSize );

                ::PostMessage(GetParent(hWnd), PSM_SETWIZBUTTONS, 0, (LPARAM)(DWORD)PSWIZB_FINISH);
            }

            CalStateChange( hWnd, (BYTE)NULL );

            // we have no further pages, so don't allow them to go any further!
            SetWindowLongPtr(hWnd,  DWLP_MSGRESULT, -1);
            
            return(-1);


        case PSN_WIZFINISH:
            WizFinish(hWnd);
            break;

        default:
            return(FALSE);

        }
        break;

    default:
        return(FALSE);
    }
    return(TRUE);   
}

//*******************************************************************************
//
//   FUNCTION: EnableSliderWindows(HWND hWnd, BOOL bEnable)
//
//   PURPOSE: 	Procedure to Show/Hide dialog controls during CalSliderProc's life
//
//   COMMENTS:	
//	
//*******************************************************************************
/*
void EnableSliderWindows(const HWND hWnd, BOOL bEnable)
{
    const short nCtrlArray[] = {IDC_SLIDER, IDC_RAWDATA, IDC_RAWX, IDC_RAWXOUTPUT, IDC_JOYLIST2_LABEL};
    BYTE nSize = sizeof(nCtrlArray)/sizeof(short);

    do
    {
       SetWindowPos( GetDlgItem(hWnd, nCtrlArray[--nSize]), NULL, NULL, NULL, NULL, NULL, 
           SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | (bEnable ? SWP_SHOWWINDOW : SWP_HIDEWINDOW ));
    } while (nSize);
}
*/
#ifdef WE_SUPPORT_CALIBRATING_POVS 
//****************************************************************************
//
//   FUNCTION: CalPovProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//   PURPOSE: 	Procedure 
//
//   COMMENTS:
//	
//      This function creates the wizard property sheet.
//****************************************************************************
LRESULT CALLBACK CalPovProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch( uMsg ) {
    case WM_ACTIVATEAPP:
        if( lpDIJoyState )
            DoTestPOV(FORCE_POV_REFRESH, lpDIJoyState->rgdwPOV, hWnd);
        break;

    case WM_INITDIALOG:
    {
        // Set the POV position to the Up position and Set the Text!
        nCalState = JCS_POV_MOVEUP;

        HWND hwndPOV = GetDlgItem(hWnd, IDC_JOYPOV);
        // Disable RTL flag
        SetWindowLongPtr(hwndPOV, GWL_EXSTYLE, GetWindowLongPtr(hwndPOV,GWL_EXSTYLE)&~WS_EX_LAYOUTRTL);

        // Set the Control font!
        ::SendDlgItemMessage(hWnd,IDC_WIZARD_MSG_HDR, WM_SETFONT, (WPARAM)hTitleFont, TRUE);
        break;
    }
    
    case WM_DESTROY:
        break;

    case WM_COMMAND:
        switch( LOWORD(wParam) ) {
        case IDC_RAWDATA:
            RawDataSelected(hWnd, bShowRawData = !bShowRawData);
            break;

        case IDC_SETPOV:

            //if( joyGetPosEx(pdiCpl->GetID(), lpJoyInfo) == JOYERR_NOERROR ) {
            if( SUCCEEDED(DIUtilPollJoystick(pdiDevice2, lpDIJoyState)) ) {
                CollectCalInfo(hWnd, lpDIJoyState);
                // Insert the POV information!
                switch( nCalState ) {
                case JCS_POV_MOVEUP:
                    // Store what we got!
                    pRanges->dwPOV[JOY_POVVAL_FORWARD] = pJoyConfig->hwc.hwv.dwPOVValues[JOY_POVVAL_FORWARD] = (pJoyConfig->hwc.hws.dwFlags & JOY_HWS_POVISPOLL) ? lpDIJoyState->rgdwPOV[0] : 0;
                    
                    // Once you're here... disable the buttons... no going back and forth...
                    ::SendMessage(GetParent(hWnd), PSM_SETWIZBUTTONS, 0, (LPARAM)(DWORD)PSWIZB_DISABLEDFINISH);
                    break;

                case JCS_POV_MOVERIGHT:
                    // Store what we got!
                    pRanges->dwPOV[JOY_POVVAL_RIGHT] = pJoyConfig->hwc.hwv.dwPOVValues[JOY_POVVAL_RIGHT] = (pJoyConfig->hwc.hws.dwFlags & JOY_HWS_POVISPOLL) ? lpDIJoyState->rgdwPOV[0] : 0;
                    break;

                case JCS_POV_MOVEDOWN:
                    // Store what we got!
                    pRanges->dwPOV[JOY_POVVAL_BACKWARD] = pJoyConfig->hwc.hwv.dwPOVValues[JOY_POVVAL_BACKWARD] = (pJoyConfig->hwc.hws.dwFlags & JOY_HWS_POVISPOLL) ? lpDIJoyState->rgdwPOV[0] : 0;
                    break;

                case JCS_POV_MOVELEFT:
                    // Store what we got!
                    pRanges->dwPOV[JOY_POVVAL_LEFT] = pJoyConfig->hwc.hwv.dwPOVValues[JOY_POVVAL_LEFT] = (pJoyConfig->hwc.hws.dwFlags & JOY_HWS_POVISPOLL) ? lpDIJoyState->rgdwPOV[0] : 0;
                    ::PostMessage(GetParent(hWnd), PSM_SETWIZBUTTONS, 0, (LPARAM)(DWORD)PSWIZB_FINISH);

                    // Take away the controls... it's all over!
                    DestroyWindow(GetDlgItem(hWnd, IDC_JOYPOV));
                    DestroyWindow(GetDlgItem(hWnd, IDC_SETPOV));
                    break;
                }
            }

            nCalState++;
            CalStateChange(hWnd, NULL);

            // Set the focus back to IDC_SETPOV button!
            SendMessage(hWnd, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hWnd, IDC_SETPOV), (LPARAM)TRUE);
            break;
        }
        break;

        // Change the background of all Static text fields to WHITE
    case WM_CTLCOLORSTATIC:
        // We only want to paint the background for the items in the top white rectangle!
        switch( GetDlgCtrlID((HWND)lParam) ) {
        case IDC_WIZARD_MSG:
        case IDC_HEADERFRAME:
        case IDC_WIZARD_MSG_HDR:
            return(LRESULT)GetStockObject(WHITE_BRUSH);
        }
        return(FALSE);


    case WM_NOTIFY:
        switch( ((NMHDR FAR *) lParam)->code ) {
        
        case PSN_KILLACTIVE:
            KillTimer(hWnd, ID_CAL_TIMER);
            return(TRUE);

        case PSN_RESET:
            break;

        case PSN_SETACTIVE:
            if( nCalState == JCS_POV_MOVEUP ) {
                DoTestPOV(FORCE_POV_REFRESH, lpDIJoyState->rgdwPOV, hWnd);
            }
            CalStateChange(hWnd, NULL);
            break;

        case PSN_WIZFINISH:
            WizFinish(hWnd);
            break;

        case PSN_WIZBACK:
            // Determine what the next calibration stage is!
            if( nCalState == nPrevCalState ) {
                DWORD dwAxis = pdiCpl->GetStateFlags()->nAxis;
                nPrevCalState --;

                while( ( !(dwAxis & (1<<(--nPrevCalState)) ) ) && (nPrevCalState > JCS_XY_CENTER2) ){
                    ;
                }
                
                nPrevCalState ++;
            }

            nCalState = nPrevCalState;

            if( nCalState > JCS_XY_CENTER2 ) {
                if( nCalState <=  JCS_S1_MOVE ) {
                    UpdateProgressLabel(hWnd);
                    
                    CalStateChange( hWnd, (BYTE)NULL );
                } else if( bPolledPOV ) {
                    SetWindowLongPtr(hWnd,  DWLP_MSGRESULT, IDD_POV );

                    return(IDD_POV);
                }
            } else {
                SetWindowLongPtr(hWnd,  DWLP_MSGRESULT, (nCalState < JCS_Z_MOVE) ? IDD_XY : -1);
                return(nCalState < JCS_Z_MOVE) ?  IDD_XY : -1;
            }

            break;

        case PSN_WIZNEXT:
            // Take away the controls... it's all over!
            DestroyWindow(GetDlgItem(hWnd, IDC_JOYPOV));
            DestroyWindow(GetDlgItem(hWnd, IDC_SETPOV));

            // Go on to Finish!
            nCalState = JCS_FINI;
            CalStateChange(hWnd, NULL);

            // Get rid of Back and bring on Finish!
            ::PostMessage(GetParent(hWnd), PSM_SETWIZBUTTONS, 0, (LPARAM)(DWORD)PSWIZB_FINISH);

            break;

        default:
            return(FALSE);

        }
        break;

    default:
        return(FALSE);
    }
    return(TRUE);   
}
#endif // WE_SUPPORT_CALIBRATING_POVS 

//*******************************************************************************
//
//   FUNCTION: CalStateChange( HWND hDlg, BYTE nDeviceFlags )
//
//   PURPOSE: 	Procedure to set up the dialog for its' Next stage
//
//   COMMENTS:	
//	
//*******************************************************************************
void CalStateChange( HWND hDlg, BYTE nDeviceFlags )
{
    short nMsgID   = IDS_JOYCAL_MOVE;
    short nTitleID = IDS_AXIS_CALIBRATION;

#define MAX_CAL_VAL 0xfffffff

    switch( nCalState ) {
    case JCS_XY_CENTER1:
    case JCS_XY_CENTER2:
        // Set up the string ID
        if( nDeviceFlags & JOY_HWS_ISYOKE )
            nMsgID = IDS_JOYCALXY_CENTERYOKE;
        else if( nDeviceFlags & JOY_HWS_ISCARCTRL )
            nMsgID = IDS_JOYCALXY_CENTERCAR;
        else if( nDeviceFlags & JOY_HWS_ISGAMEPAD )
            nMsgID = IDS_JOYCALXY_CENTERGAMEPAD;
        else nMsgID = IDS_JOYCALXY_CENTER;

        // Setup the Header TextID
        nTitleID    = (nCalState == JCS_XY_CENTER1) ? IDS_CENTER_HDR : IDS_VERIFY_CENTER_HDR;

        EnableXYWindows( hDlg ); 
        break;

    case JCS_XY_MOVE:

        // Set up the string ID
        if( nDeviceFlags & JOY_HWS_ISYOKE )
            nMsgID = IDS_JOYCALXY_MOVEYOKE;
        else if( nDeviceFlags & JOY_HWS_ISCARCTRL )
            nMsgID = IDS_JOYCALXY_MOVECAR;
        else if( nDeviceFlags & JOY_HWS_ISGAMEPAD )
            nMsgID = IDS_JOYCALXY_MOVEGAMEPAD;
        else nMsgID = IDS_JOYCALXY_MOVE;

        // Blast the data so we are sure to get the correct data!
        pRanges->jpMin.dwX =  MAX_CAL_VAL;
        pRanges->jpMax.dwX = -MAX_CAL_VAL;

        pRanges->jpMin.dwY =  MAX_CAL_VAL;
        pRanges->jpMax.dwY = -MAX_CAL_VAL;

        EnableXYWindows( hDlg ); 
        break;

/*
    case JCS_XY_CENTER1:
      // Set up the string ID
      if ( nDeviceFlags & JOY_HWS_ISYOKE ) 
           nMsgID = IDS_JOYCALXY_CENTERYOKE;
        else if( nDeviceFlags & JOY_HWS_ISCARCTRL ) 
            nMsgID = IDS_JOYCALXY_CENTERCAR;
        else if( nDeviceFlags & JOY_HWS_ISGAMEPAD ) 
            nMsgID = IDS_JOYCALXY_CENTERGAMEPAD;
        else nMsgID = IDS_JOYCALXY_CENTER;

        // Setup the Header TextID
        nTitleID	= IDS_CENTER_HDR;

      EnableXYWindows( hDlg ); 
        break;

   case JCS_XY_MOVE:

      // Set up the string ID
       if( nDeviceFlags & JOY_HWS_ISYOKE ) 
           nMsgID = IDS_JOYCALXY_MOVEYOKE;
        else if( nDeviceFlags & JOY_HWS_ISCARCTRL ) 
            nMsgID = IDS_JOYCALXY_MOVECAR;
        else if( nDeviceFlags & JOY_HWS_ISGAMEPAD ) 
            nMsgID = IDS_JOYCALXY_MOVEGAMEPAD;
        else nMsgID = IDS_JOYCALXY_MOVE;

      // Blast the data so we are sure to get the correct data!
      pRanges->jpMin.dwX =  MAX_CAL_VAL;
      pRanges->jpMax.dwX = -MAX_CAL_VAL;

      pRanges->jpMin.dwY =  MAX_CAL_VAL;
      pRanges->jpMax.dwY = -MAX_CAL_VAL;

      EnableXYWindows( hDlg ); 
       break;

    case JCS_XY_CENTER2:

      // Set up the string ID
        if( nDeviceFlags & JOY_HWS_ISYOKE ) 
            nMsgID = IDS_JOYCALXY_CENTERYOKE;
        else if( nDeviceFlags & JOY_HWS_ISCARCTRL ) 
            nMsgID = IDS_JOYCALXY_CENTERCAR;
        else if( nDeviceFlags & JOY_HWS_ISGAMEPAD ) 
            nMsgID = IDS_JOYCALXY_CENTERGAMEPAD;
        else nMsgID = IDS_JOYCALXY_CENTER;

        // Setup the Header TextID
        nTitleID	= IDS_VERIFY_CENTER_HDR;

        EnableXYWindows( hDlg );
       break;
*/
#ifdef DEADZONE
    case JCS_DEADZONE:
        // Set up the message string.
        if( nDeviceFlags & JOY_HWS_ISYOKE )
            nMsgID = IDS_YOKE_DEADZONE;
        else if( nDeviceFlags & JOY_HWS_ISCARCTRL )
            nMsgID = IDS_CAR_DEADZONE;
        else if( nDeviceFlags & JOY_HWS_ISGAMEPAD )
            nMsgID = IDS_GAMEPAD_DEADZONE;
        else nMsgID = IDS_JOYSTICK_DEADZONE;

        // Set up the title string!
        nTitleID = IDS_DEADZONE_TITLE;

        // Setup the controls!
        EnableXYWindows( hDlg );

        // Text Labels are sent in during UpdateXYLabel!
        // Text fonts are set on INIT!

        // Setup the Spin positions!
        {
            LPDIPROPDWORD pDIPropDW =  (LPDIPROPDWORD)_alloca(DIPROPDWORD);
            ASSERT (pDIPropDW);

            ZeroMemory(pDIPropDW, sizeof(DIPROPDWORD));

            pDIPropDW->diph.dwSize          = sizeof(DIPROPDWORD);
            pDIPropDW->diph.dwHeaderSize    = sizeof(DIPROPHEADER);
            pDIPropDW->diph.dwObj           = DIJOFS_X;
            pDIPropDW->diph.dwHow           = DIPH_BYOFFSET;

            HWND hSpinCtrl;

            // Deadzone first...
            if( SUCCEEDED(pdiDevice2->GetProperty(DIPROP_DEADZONE, &pDIPropDW->diph)) ) {
                // First the Deadzone...
                hSpinCtrl = GetDlgItem(hDlg, IDC_X_DEADZONE_SPIN);

                ::PostMessage(hSpinCtrl, UDM_SETRANGE,  0, MAKELPARAM(1000, 1));
                ::PostMessage(hSpinCtrl, UDM_SETBASE,  10, 0L);
                ::PostMessage(hSpinCtrl, UDM_SETPOS,     0, MAKELPARAM(pDIPropDW->dwData, 0));
            }

            // Setup the DIPROPDWORD struct!
            pDIPropDW->diph.dwObj           = DIJOFS_Y;

            if( SUCCEEDED(pdiDevice2->GetProperty(DIPROP_DEADZONE, &pDIPropDW->diph)) ) {
                // First the Deadzone...
                hSpinCtrl = GetDlgItem(hDlg, IDC_Y_DEADZONE_SPIN);

                ::PostMessage(hSpinCtrl, UDM_SETRANGE,  0, MAKELPARAM(1000, 1));
                ::PostMessage(hSpinCtrl, UDM_SETBASE,  10, 0L);
                ::PostMessage(hSpinCtrl, UDM_SETPOS,     0, MAKELPARAM(pDIPropDW->dwData, 0));
            }

            // Now, the Saturation!
            if( SUCCEEDED(pdiDevice2->GetProperty(DIPROP_SATURATION, &pDIPropDW->diph)) ) {
                hSpinCtrl = GetDlgItem(hDlg, IDC_Y_SATURATION_SPIN);

                ::PostMessage(hSpinCtrl, UDM_SETRANGE,  0, MAKELPARAM(1000, 1));
                ::PostMessage(hSpinCtrl, UDM_SETBASE,  10, 0L);
                ::PostMessage(hSpinCtrl, UDM_SETPOS,     0, MAKELPARAM(pDIPropDW->dwData, 0));
            }

            // Setup the DIPROPDWORD struct!
            pDIPropDW->diph.dwObj           = DIJOFS_X;


            if( SUCCEEDED(pdiDevice2->GetProperty(DIPROP_SATURATION, &pDIPropDW->diph)) ) {
                hSpinCtrl = GetDlgItem(hDlg, IDC_X_SATURATION_SPIN);

                ::PostMessage(hSpinCtrl, UDM_SETRANGE,  0, MAKELPARAM(1000, 1));
                ::PostMessage(hSpinCtrl, UDM_SETBASE,  10, 0L);
                ::PostMessage(hSpinCtrl, UDM_SETPOS,     0, MAKELPARAM(pDIPropDW->dwData, 0));
            }
        }

        // Draw the rectangle!

        break;
#endif //DEADZONE

    case JCS_Z_MOVE:
        {
            static long nMin = pRanges->jpMin.dwZ;
            static long nMax = pRanges->jpMax.dwZ;

            // Set the Range
            if( bGradient )
                pGradient->SetRange(nMin, nMax);

            ::PostMessage(ProgWndCal, PBM_SETRANGE32, (WPARAM)nMin, (LPARAM)nMax);

            // Blast the data so we are sure to get the correct data!
            pRanges->jpMin.dwZ =  MAX_CAL_VAL;
            pRanges->jpMax.dwZ = -MAX_CAL_VAL;
        }
        break;

    case JCS_R_MOVE:
        {
            static long nMin = pRanges->jpMin.dwRx;
            static long nMax    = pRanges->jpMax.dwRx;

            // Set the Range
            if( bGradient )
                pGradient->SetRange(nMin, nMax);

            ::PostMessage(ProgWndCal, PBM_SETRANGE32, (WPARAM)nMin, (LPARAM)nMax);

            // Blast the data so we are sure to get the correct data!
            pRanges->jpMin.dwRx =  MAX_CAL_VAL;
            pRanges->jpMax.dwRx = -MAX_CAL_VAL;
        }
        break;

    case JCS_U_MOVE:
        {
            static long nMin = pRanges->jpMin.dwRy;
            static long nMax = pRanges->jpMax.dwRy;

            // Set the Range
            if( bGradient )
                pGradient->SetRange(nMin, nMax);

            ::PostMessage(ProgWndCal, PBM_SETRANGE32, (WPARAM)nMin, (LPARAM)nMax);

            // Blast the data so we are sure to get the correct data!
            pRanges->jpMin.dwRy =  MAX_CAL_VAL;
            pRanges->jpMax.dwRy = -MAX_CAL_VAL;
        }
        break;

    case JCS_V_MOVE:
        {
            static long nMin = pRanges->jpMin.dwRz;
            static long nMax = pRanges->jpMax.dwRz;

            // Set the Range
            if( bGradient )
                pGradient->SetRange(nMin, nMax);

            ::PostMessage(ProgWndCal, PBM_SETRANGE32, (WPARAM)nMin, (LPARAM)nMax);

            // Blast the data so we are sure to get the correct data!
            pRanges->jpMin.dwRz =  MAX_CAL_VAL;
            pRanges->jpMax.dwRz = -MAX_CAL_VAL;
        }
        break;

    case JCS_S0_MOVE:
        {
            static long nMin = pRanges->jpMin.dwS0;
            static long nMax    = pRanges->jpMax.dwS0;

            // Set the Range
            if( bGradient )
                pGradient->SetRange(nMin, nMax);

            ::PostMessage(ProgWndCal, PBM_SETRANGE32, (WPARAM)nMin, (LPARAM)nMax);

            // Blast the data so we are sure to get the correct data!
            pRanges->jpMin.dwS0 =  MAX_CAL_VAL;
            pRanges->jpMax.dwS0 = -MAX_CAL_VAL;
        }
        break;

    case JCS_S1_MOVE:
        {
            static long nMin = pRanges->jpMin.dwS1;
            static long nMax    = pRanges->jpMax.dwS1;

            // Set the Range
            if( bGradient )
                pGradient->SetRange(nMin, nMax);

            ::PostMessage(ProgWndCal, PBM_SETRANGE32, (WPARAM)nMin, (LPARAM)nMax);

            // Blast the data so we are sure to get the correct data!
            pRanges->jpMin.dwS1 =  MAX_CAL_VAL;
            pRanges->jpMax.dwS1 = -MAX_CAL_VAL;
        }
        break;

#ifdef WE_SUPPORT_CALIBRATING_POVS
    case JCS_POV_MOVEUP:
        lpDIJoyState->rgdwPOV[0] = JOY_POVFORWARD;
        DoTestPOV(HAS_POV1 | HAS_CALIBRATED, lpDIJoyState->rgdwPOV, hDlg);

        nMsgID   = IDS_JOYCALPOV_MOVE;
        nTitleID = IDS_POV_CALIBRATION;
        break;

    case JCS_POV_MOVERIGHT:
        lpDIJoyState->rgdwPOV[0] = JOY_POVRIGHT;
        DoTestPOV(HAS_POV1 | HAS_CALIBRATED, lpDIJoyState->rgdwPOV, hDlg);

        nMsgID   = IDS_JOYCALPOV_MOVE;
        nTitleID = IDS_POV_CALIBRATION;
        break;

    case JCS_POV_MOVEDOWN:
        lpDIJoyState->rgdwPOV[0] = JOY_POVBACKWARD;
        DoTestPOV(HAS_POV1 | HAS_CALIBRATED, lpDIJoyState->rgdwPOV, hDlg);

        nMsgID   = IDS_JOYCALPOV_MOVE;
        nTitleID = IDS_POV_CALIBRATION;
        break;

    case JCS_POV_MOVELEFT:
        lpDIJoyState->rgdwPOV[0] = JOY_POVLEFT;
        DoTestPOV(HAS_POV1 | HAS_CALIBRATED, lpDIJoyState->rgdwPOV, hDlg);

        nMsgID   = IDS_JOYCALPOV_MOVE;
        nTitleID = IDS_POV_CALIBRATION;
        break;
#endif //WE_SUPPORT_CALIBRATING_POVS

    case JCS_FINI:
        nMsgID   = IDS_JOYCAL_DONE;
        nTitleID = IDS_CALIBRATION_FINI;
        break;

    default:
#ifdef _DEBUG
        OutputDebugString(TEXT("GCDEF.DLL: CAL.CPP: CalStateChange: nCalState doesn't match any known Calibration States!\n"));
#endif
        return;

    }  // END OF SWITCH

    // load and set the text
    LPTSTR lptszMsg = new TCHAR[MAX_STR_LEN];

    DWORD nStrLen = MAX_STR_LEN - 1;

    // see if there is any OEM text specified
    if( pJoyConfig->hwc.dwUsageSettings & JOY_US_ISOEM ) {
        GetOEMCtrlString(lptszMsg, &nStrLen);
    } else {
    	nStrLen = 0;
    }

    // nStrLen will be non-zero if GetOEMCtrlString is successfull!
    if( nStrLen == 0 ) {
        VERIFY(LoadString(ghInst, nMsgID, lptszMsg, MAX_STR_LEN));

        switch( nMsgID ) {
        case IDS_JOYCAL_MOVE:
            {
                LPTSTR lptszBuff = new TCHAR[STR_LEN_32];
                LPTSTR lpDup = StrDup(lptszMsg);

                if( lptszBuff && lpDup ) {
                    ::SendDlgItemMessage(hDlg, IDC_JOYLIST2_LABEL, WM_GETTEXT, (WPARAM)STR_LEN_32, (LPARAM)lptszBuff);
                    wsprintf(lptszMsg, lpDup, lptszBuff);
                    LocalFree(lpDup);
                    delete []lptszBuff;
                }
            }
            break;
        }
    }

    // Send the smaller message
    ::SendDlgItemMessage(hDlg, IDC_WIZARD_MSG, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)lptszMsg);

    VERIFY(LoadString(ghInst, nTitleID, lptszMsg, MAX_STR_LEN));

    // Send the Bold Header message
    ::SendDlgItemMessage(hDlg, IDC_WIZARD_MSG_HDR, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)lptszMsg);

    if( lptszMsg ) {
        delete[] (lptszMsg);
    }

    // Take care of the RawData dialog items!
    switch( nCalState ) {
    // Don't do the raw data thing if you don't have the checkbox!
    case JCS_XY_CENTER1:
    case JCS_XY_CENTER2:
    case JCS_FINI:
        break;

        // Do the percent for the pages that need it!
    case JCS_Z_MOVE:
    case JCS_R_MOVE:
    case JCS_U_MOVE:
    case JCS_V_MOVE:
    case JCS_S0_MOVE:
    case JCS_S1_MOVE:
        if( bGradient ) {
            if( pGradient ) {
                pGradient->ShowPercent(bShowRawData);
            }
        }
        // Missing break intentional!!!

    default:
        RawDataSelected(hDlg, bShowRawData);
        ::SendDlgItemMessage(hDlg, IDC_RAWDATA, BM_SETCHECK, (bShowRawData) ? BST_CHECKED : BST_UNCHECKED, 0);
        break;
    }


} // *** end of CalStateChange 



//*******************************************************************************
//
//   FUNCTION: CollectCalInfo( HWND hDlg, LPDIJOYSTATE pdiJoyState )
//
//   PURPOSE: 	Procedure to Collect Calibration Data
//
//   COMMENTS:	
//	
//*******************************************************************************
BOOL CollectCalInfo( HWND hDlg, LPDIJOYSTATE pdiJoyState )
{
    TCHAR tsz[16];

    switch( nCalState ) {
    // remember XY center
    case JCS_XY_CENTER1:
        // store the initial centres!
        pRanges->jpCenter.dwY = pdiJoyState->lY;
        pRanges->jpCenter.dwX = pdiJoyState->lX;

        // We Have an X/Y, so let's check for our Pens!
        CreatePens();
        break;

        // remember max/min XY values
    case JCS_XY_MOVE:
        if( pdiJoyState->lX > pRanges->jpMax.dwX )
            pRanges->jpMax.dwX = pdiJoyState->lX;
        else if( pdiJoyState->lX < pRanges->jpMin.dwX )
            pRanges->jpMin.dwX = pdiJoyState->lX;

        if( pdiJoyState->lY > pRanges->jpMax.dwY )
            pRanges->jpMax.dwY = pdiJoyState->lY;
        else if( pdiJoyState->lY < pRanges->jpMin.dwY )
            pRanges->jpMin.dwY = pdiJoyState->lY;

        // if IDC_RAWXOUTPUT is visible, then so is IDC_RAWYOUTPUT...
        // no bother to even ask.
        if( bShowRawData ) {
            static POINT ptOld = {DELTA,DELTA};

            if( (ptOld.x != pdiJoyState->lX) || (ptOld.y != pdiJoyState->lY) ) {
                myitoa(pdiJoyState->lX, &tsz[0]);
                ::SendDlgItemMessage(hDlg, IDC_RAWXOUTPUT, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)tsz);

                myitoa(pdiJoyState->lY, &tsz[0]);
                ::SendDlgItemMessage(hDlg, IDC_RAWYOUTPUT, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)tsz);

                ptOld.x = pdiJoyState->lX;
                ptOld.y = pdiJoyState->lY;
            }
        }

        // Scale before send it to DoJoyMove!
        {
            RECT rc;
            GetClientRect(GetDlgItem(hDlg, IDC_JOYLIST1), &rc);

            // Casting to the UINT will change the sign!
            UINT nRange = (UINT)(pRanges->jpMax.dwX - pRanges->jpMin.dwX);

            float nScaledRange = (float)(rc.right-DELTA);

            if( nRange )
                nScaledRange /= (float)nRange;

            // Scale X
            pdiJoyState->lX = (long)((pdiJoyState->lX - pRanges->jpMin.dwX) * nScaledRange);

            // Scale Y
            if( nRange ) nScaledRange = (float)rc.bottom / (float)nRange;
            pdiJoyState->lY = (long)((pdiJoyState->lY - pRanges->jpMin.dwY) * nScaledRange);
        }
        DoJoyMove( hDlg, (BYTE)HAS_X|HAS_Y );
        break;

    case JCS_XY_CENTER2:
        // Average the Y
        pRanges->jpCenter.dwY = (pRanges->jpCenter.dwY += pdiJoyState->lY)>>1;

        //Average the X
        pRanges->jpCenter.dwX = (pRanges->jpCenter.dwX += pdiJoyState->lX)>>1;
        break;

        // remember max/min Z value
    case JCS_Z_MOVE:
        // Set new Min's and Max's...
        // Set a new Center whenever either is hit!
        if( pdiJoyState->lZ  > pRanges->jpMax.dwZ ) {
            pRanges->jpMax.dwZ    = pdiJoyState->lZ; 
            pRanges->jpCenter.dwZ = (pRanges->jpMax.dwZ+pRanges->jpMin.dwZ)>>1;
        } else if( pdiJoyState->lZ  < pRanges->jpMin.dwZ ) {
            pRanges->jpMin.dwZ    = pdiJoyState->lZ; 
            pRanges->jpCenter.dwZ = (pRanges->jpMax.dwZ+pRanges->jpMin.dwZ)>>1;
        }

        // Do the position status
        // Update the text
        if( bShowRawData ) {
            myitoa(pdiJoyState->lZ, &tsz[0]);
            ::SendDlgItemMessage(hDlg, IDC_RAWXOUTPUT, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)tsz);
        }

        if( bGradient )
            pGradient->SetPos(pdiJoyState->lZ);

        ::PostMessage(ProgWndCal, PBM_SETPOS, (WPARAM)pdiJoyState->lZ, 0L);
        break;

        // remember max/min Rx value
    case JCS_R_MOVE:
        // Set new Min's and Max's...
        // Set a new Center whenever either is hit!
        if( pdiJoyState->lRx  > pRanges->jpMax.dwRx ) {
            pRanges->jpMax.dwRx    = pdiJoyState->lRx; 
            pRanges->jpCenter.dwRx = (pRanges->jpMax.dwRx+pRanges->jpMin.dwRx)>>1;
        } else if( pdiJoyState->lRx  < pRanges->jpMin.dwRx ) {
            pRanges->jpMin.dwRx    = pdiJoyState->lRx; 
            pRanges->jpCenter.dwRx = (pRanges->jpMax.dwRx+pRanges->jpMin.dwRx)>>1;
        }

        // Do the position status
        // Update the text
        if( bShowRawData ) {
            myitoa(pdiJoyState->lRx, &tsz[0]);
            ::SendDlgItemMessage(hDlg, IDC_RAWXOUTPUT, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)tsz);
        }

        if( bGradient )
            pGradient->SetPos(pdiJoyState->lRx);

        ::PostMessage(ProgWndCal, PBM_SETPOS, (WPARAM)pdiJoyState->lRx, 0L);
        break;

        // remember max/min Ry value
    case JCS_U_MOVE:
        // Set new Min's and Max's...
        // Set a new Center whenever either is hit!
        if( pdiJoyState->lRy > pRanges->jpMax.dwRy ) {
            pRanges->jpMax.dwRy    = pdiJoyState->lRy; 
            pRanges->jpCenter.dwRy = (pRanges->jpMax.dwRy+pRanges->jpMin.dwRy)>>1;
        } else if( pdiJoyState->lRy < pRanges->jpMin.dwRy ) {
            pRanges->jpMin.dwRy    = pdiJoyState->lRy; 
            pRanges->jpCenter.dwRy = (pRanges->jpMax.dwRy+pRanges->jpMin.dwRy)>>1;
        }

        // Do the position status
        if( bShowRawData ) {
            myitoa(pdiJoyState->lRy, &tsz[0]);
            ::SendDlgItemMessage(hDlg, IDC_RAWXOUTPUT, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)tsz);
        }

        if( bGradient )
            pGradient->SetPos(pdiJoyState->lRy);

        ::PostMessage(ProgWndCal, PBM_SETPOS, (WPARAM)pdiJoyState->lRy, 0L);
        break;

        // remember max/min Rz value
    case JCS_V_MOVE:
        // Set new Min's and Max's...
        // Set a new Center whenever either is hit!
        if( pdiJoyState->lRz > pRanges->jpMax.dwRz ) {
            pRanges->jpMax.dwRz    = pdiJoyState->lRz; 
            pRanges->jpCenter.dwRz = (pRanges->jpMax.dwRz+pRanges->jpMin.dwRz)>>1;
        } else if( pdiJoyState->lRz < pRanges->jpMin.dwRz ) {
            pRanges->jpMin.dwRz    = pdiJoyState->lRz; 
            pRanges->jpCenter.dwRz = (pRanges->jpMax.dwRz+pRanges->jpMin.dwRz)>>1;
        }

        // Do the position status
        if( bShowRawData ) {
            myitoa(pdiJoyState->lRz, &tsz[0]);
            ::SendDlgItemMessage(hDlg, IDC_RAWXOUTPUT, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)tsz);
        }

        if( bGradient )
            pGradient->SetPos(pdiJoyState->lRz);

        ::PostMessage(ProgWndCal, PBM_SETPOS, (WPARAM)pdiJoyState->lRz, 0L);
        break;

        // remember max/min S0 value
    case JCS_S0_MOVE:
        // Set new Min's and Max's...
        // Set a new Center whenever either is hit!
        if( pdiJoyState->rglSlider[0] > pRanges->jpMax.dwS0 ) {
            pRanges->jpMax.dwS0    = pdiJoyState->rglSlider[0]; 
            pRanges->jpCenter.dwS0 = (pRanges->jpMax.dwS0+pRanges->jpMin.dwS0)>>1;
        } else if( pdiJoyState->rglSlider[0] < pRanges->jpMin.dwS0 ) {
            pRanges->jpMin.dwS0    = pdiJoyState->rglSlider[0]; 
            pRanges->jpCenter.dwS0 = (pRanges->jpMax.dwS0+pRanges->jpMin.dwS0)>>1;
        }

        // Do the position status
        if( bShowRawData ) {
            myitoa(pdiJoyState->rglSlider[0], &tsz[0]);
            ::SendDlgItemMessage(hDlg, IDC_RAWXOUTPUT, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)tsz);
        }

        if( bGradient )
            pGradient->SetPos(pdiJoyState->rglSlider[0]);

        ::PostMessage(ProgWndCal, PBM_SETPOS, (WPARAM)pdiJoyState->rglSlider[0], 0L);
        break;

        // remember max/min S1 value
    case JCS_S1_MOVE:
        // Set new Min's and Max's...
        // Set a new Center whenever either is hit!
        if( pdiJoyState->rglSlider[1] > pRanges->jpMax.dwS1 ) {
            pRanges->jpMax.dwS1    = pdiJoyState->rglSlider[1]; 
            pRanges->jpCenter.dwS1 = (pRanges->jpMax.dwS1+pRanges->jpMin.dwS1)>>1;
        } else if( pdiJoyState->rglSlider[1] < pRanges->jpMin.dwS1 ) {
            pRanges->jpMin.dwS1    = pdiJoyState->rglSlider[1]; 
            pRanges->jpCenter.dwS1 = (pRanges->jpMax.dwS1+pRanges->jpMin.dwS1)>>1;
        }

        // Do the position status
        if( bShowRawData ) {
            myitoa(pdiJoyState->rglSlider[1], &tsz[0]);
            ::SendDlgItemMessage(hDlg, IDC_RAWXOUTPUT, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)tsz);
        }

        if( bGradient )
            pGradient->SetPos(pdiJoyState->rglSlider[1]);

        ::PostMessage(ProgWndCal, PBM_SETPOS, (WPARAM)pdiJoyState->rglSlider[1], 0L);
        break;

    case JCS_POV_MOVEUP:
    case JCS_POV_MOVERIGHT:
    case JCS_POV_MOVEDOWN:
    case JCS_POV_MOVELEFT:
        // Do the position status
        /*
        if( bShowRawData ) {
            myitoa(pdiJoyState->rgdwPOV[0], &tsz[0]);
            ::SendDlgItemMessage(hDlg, IDC_RAWXOUTPUT, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)tsz);
        }
        */
        break;
    }

    return(TRUE);
} // CollectCalInfo


//*******************************************************************************
//
//   FUNCTION: EnableXYWindows( HWND hDlg)
//
//   PURPOSE: 	Enables X/Y Windows
//
//   COMMENTS:	
//	
//*******************************************************************************
void EnableXYWindows( HWND hDlg )
{
    ////// set up the XY window controls ///////		  	 
    USHORT nCtrls[] = {IDC_RAWX, IDC_RAWY, IDC_RAWXOUTPUT, IDC_RAWYOUTPUT};
    BYTE nNumCtrls = sizeof(nCtrls)/sizeof(short);                                                    

    do {
        SetWindowPos( GetDlgItem( hDlg,  nCtrls[--nNumCtrls]), NULL, NULL, NULL, NULL, NULL, 
                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_HIDEWINDOW);
    } while( nNumCtrls );

#ifdef DEADZONE
    {
        USHORT nDZCtrls[] = {IDC_X_DEADZONE_SPIN,   IDC_Y_DEADZONE_SPIN, IDC_X_SATURATION_SPIN,
            IDC_Y_SATURATION_SPIN, IDC_DEADZONE_TITLE,  IDC_X_DEADZONE,
            IDC_Y_DEADZONE,           IDC_X_AXIS_LABEL,    IDC_X_AXIS_LABEL,
            IDC_Y_AXIS_LABEL,     IDC_SATURATION_TITLE,IDC_X_SATURATION,
            IDC_Y_SATURATION,      IDC_X_AXIS_LABEL_SATURATION, IDC_Y_AXIS_LABEL_SATURATION};
        nNumCtrls = sizeof(nCtrls)/sizeof(short);                                                    

        do {
            // Use SetWindowPos here because internally, ShowWindow calls it!
            SetWindowPos( GetDlgItem( hDlg,  nCtrls[nNumCtrls]), NULL, NULL, NULL, NULL, NULL, 
                          SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | ((nCalState == JCS_DEADZONE) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
        } while( nNumCtrls-- );
    }
#endif // DEADZONE	

    nCtrls[0] = IDC_JOYLIST1;
    nCtrls[1] = IDC_JOYLIST1_LABEL;
    nCtrls[2] = IDC_RAWDATA;
    nNumCtrls = 2;

    do {
        // Use SetWindowPos here because internally, ShowWindow calls it!
        SetWindowPos( GetDlgItem( hDlg,  nCtrls[nNumCtrls]), NULL, NULL, NULL, NULL, NULL, 
                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | (((nCalState == JCS_XY_MOVE) 
#ifdef DEADZONE
                                                                 || (nCalState == JCS_DEADZONE)
#endif
                                                                ) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
    } while( nNumCtrls-- );
    
    HWND hwndXY = GetDlgItem(hDlg, IDC_JOYLIST1);
    // Disable RTL flag
    SetWindowLongPtr( hwndXY, GWL_EXSTYLE, GetWindowLongPtr(hwndXY,GWL_EXSTYLE) & ~WS_EX_LAYOUTRTL );
    
}

//*******************************************************************************
//
//   FUNCTION: GetOEMCtrlString(LPTSTR lptStr, BYTE *nStrLen)
//
//   PURPOSE: 	Gets string and string length for OEM controls
//
//   COMMENTS:	
//	
//*******************************************************************************
BOOL GetOEMCtrlString(LPTSTR lptStr, LPDWORD nStrLen)
{
    // there's no REGSTR_VAL_JOYOEM for the sliders so return false and take the defaults
    switch( nCalState ) {
        case JCS_S0_MOVE:
        case JCS_S1_MOVE:
            *nStrLen = 0;
            return(FALSE);
    }

    // Get the DIJOYCONFIG interface pointer!
    LPDIRECTINPUTJOYCONFIG pdiJoyConfig;
    pdiCpl->GetJoyConfig(&pdiJoyConfig);

    BOOL bRet = FALSE;

    if( SUCCEEDED(pdiJoyConfig->Acquire()) ) {
        HKEY hKey;

        // Open the TypeKey
        if( SUCCEEDED(pdiJoyConfig->OpenTypeKey( lpwszTypeName, KEY_ALL_ACCESS, &hKey)) ) {
            // registry strings for calibration messages
            static LPCTSTR pszOEMCalRegStrs[] = { 
                REGSTR_VAL_JOYOEMCAL1, REGSTR_VAL_JOYOEMCAL2,
                REGSTR_VAL_JOYOEMCAL3, REGSTR_VAL_JOYOEMCAL4,
                REGSTR_VAL_JOYOEMCAL5, REGSTR_VAL_JOYOEMCAL6,
                REGSTR_VAL_JOYOEMCAL7, 

#ifdef WE_SUPPORT_CALIBRATING_POVS
                REGSTR_VAL_JOYOEMCAL8, REGSTR_VAL_JOYOEMCAL9, 
                REGSTR_VAL_JOYOEMCAL10,REGSTR_VAL_JOYOEMCAL11, 
#endif  // WE_SUPPORT_CALIBRATING_POVS
                REGSTR_VAL_JOYOEMCAL12
            };

            if( nCalState < (sizeof(pszOEMCalRegStrs)/sizeof(pszOEMCalRegStrs[0])) )
            {
                DWORD dwType = REG_SZ;
                // the -2 is because of JCS_S0_MOVE and JCS_S1_MOVE!
                if( RegQueryValueEx( hKey, pszOEMCalRegStrs[(nCalState == JCS_FINI) ? nCalState-2 : nCalState], NULL, &dwType, (CONST LPBYTE)lptStr, nStrLen ) == ERROR_SUCCESS )
                    bRet = TRUE;
                else
                    *nStrLen = 0;
            }
            else
            {
                *nStrLen = 0;
            }
            RegCloseKey(hKey);
        } else
        {
            *nStrLen = 0;
#ifdef _DEBUG
            OutputDebugString(TEXT("Test.cpp: GetOEMCtrlString: OpenTypeKey FAILED!\n"));
#endif
        }

        pdiJoyConfig->Unacquire();
    }

    return(bRet);
} // *** end of GetOEMCtrlString


#ifdef WE_SUPPORT_CALIBRATING_POVS
///////////////////////////////////////////////////////////////////////////////////////////////////////
//
//	SetDefaultButton( HWND hwdb )
//
//////////////////////////////////////////////////////////////////////////////////////////////////////
void SetDefaultButton( HWND hDlg, HWND hCtrl )
{
    // make the specified button the default
    DWORD style = GetWindowLong( hCtrl, GWL_STYLE );
    style &= ~(BS_PUSHBUTTON|BS_DEFPUSHBUTTON);
    style |= BS_DEFPUSHBUTTON;
    SetWindowLong( hCtrl, GWL_STYLE, style );

} // SetDefaultButton
#endif //WE_SUPPORT_CALIBRATING_POVS

//===========================================================================
// SetCalibrationMode ( BOOL bSet )
// 
// Sets DirectInput Calibration mode (RAW/COOKED)
//
// Parameters:
//  BOOL					bSet			-		TRUE for RAW, FALSE for COOKED
//
// Returns:				return value from SetProperty (standard COM stuff)
//
//===========================================================================
HRESULT SetCalibrationMode( BOOL bSet)
{
    LPDIPROPDWORD pDIPropDword = (LPDIPROPDWORD)_alloca(sizeof(DIPROPDWORD));
    assert (pDIPropDword);

    pDIPropDword->diph.dwSize = sizeof(DIPROPDWORD);
    pDIPropDword->diph.dwHeaderSize = sizeof(DIPROPHEADER);
    pDIPropDword->diph.dwObj  = 0x0;
    pDIPropDword->diph.dwHow  = DIPH_DEVICE;
    pDIPropDword->dwData = bSet ? DIPROPCALIBRATIONMODE_RAW : DIPROPCALIBRATIONMODE_COOKED;

    // Set the mode to Raw Data during Calibration!
    HRESULT hr = pdiDevice2->SetProperty(DIPROP_CALIBRATIONMODE, &pDIPropDword->diph);
#ifdef _DEBUG
    if( FAILED(hr) ) {
        OutputDebugString(TEXT("GCDEF.DLL: CAL.CPP: SetCalibrationMode: SetProperty Failed with a return of "));


        switch( hr ) {
        case DI_PROPNOEFFECT:
            OutputDebugString(TEXT("DI_PROPNOEFFECT\n"));
            break;

        case DIERR_INVALIDPARAM:
            OutputDebugString(TEXT("DIERR_INVALIDPARAM\n"));
            break;

        case DIERR_OBJECTNOTFOUND:
            OutputDebugString(TEXT("DIERR_OBJECTNOTFOUND\n"));
            break;

        case DIERR_UNSUPPORTED:
            OutputDebugString(TEXT("DIERR_UNSUPPORTED\n"));
            break;

        default:
            {
                TCHAR szTmp[32];
                wsprintf(szTmp, TEXT("%x"), hr);
                OutputDebugString(szTmp);
            }
        }
    }
#endif
    return(hr);
}


//===========================================================================
// UpdateXYLabel(HWND hWnd)
//
// Displays the number and names of the device Axis in the provided dialog.
// This	EXPECTS that the controls are not visible by default!
//
// Parameters:
//  HWND             hDlg       - Dialog handle
//
// Returns:
//
//===========================================================================
void UpdateXYLabel(const HWND hDlg)
{
    BYTE nAxisFlags = pdiCpl->GetStateFlags()->nAxis;

    // X and Y use the same control so they are isolated!
    if( (nAxisFlags & HAS_X) || (nAxisFlags & HAS_Y) ) {
        LPDIDEVICEOBJECTINSTANCE_DX3 pDevObjInst = new (DIDEVICEOBJECTINSTANCE_DX3);
        assert (pDevObjInst);

        ZeroMemory(pDevObjInst, sizeof(DIDEVICEOBJECTINSTANCE_DX3));

        pDevObjInst->dwSize = sizeof(DIDEVICEOBJECTINSTANCE_DX3);

        LPTSTR ptszBuff = (LPTSTR) _alloca(sizeof(TCHAR[STR_LEN_32]));
        assert (ptszBuff);

        ZeroMemory(ptszBuff, sizeof(TCHAR[STR_LEN_32]));

        // Set it's text
        if( nAxisFlags & HAS_X ) {
            if( FAILED(pdiDevice2->GetObjectInfo((LPDIDEVICEOBJECTINSTANCE)pDevObjInst, DIJOFS_X, DIPH_BYOFFSET)) ) {
#ifdef _DEBUG
                OutputDebugString(TEXT("GCDEF.DLL: DisplayAvailableAxis: GetObjectInfo Failed to find DIJOFS_X!\n"));
#endif
            }

            int nLen=lstrlen(pDevObjInst->tszName)+1;
            if(nLen>STR_LEN_32)
                nLen=STR_LEN_32;
            StrCpyN(ptszBuff, pDevObjInst->tszName, nLen);

            // Set the Output Label!
            ::SendDlgItemMessage(hDlg, IDC_RAWX, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)pDevObjInst->tszName);

#ifdef DEADZONE
            // Set text labels!
            ::SendDlgItemMessage(hDlg, IDC_X_AXIS_LABEL_DEADZONE,   WM_SETTEXT, 0, (LPARAM)(LPCTSTR)pDevObjInst->tszName);
            ::SendDlgItemMessage(hDlg, IDC_X_AXIS_LABEL_SATURATION, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)pDevObjInst->tszName);
#endif //DEADZONE

            // Remove the HAS_X flag
            nAxisFlags &= ~HAS_X;
        }

        if( nAxisFlags & HAS_Y ) {
            if( FAILED(pdiDevice2->GetObjectInfo((LPDIDEVICEOBJECTINSTANCE)pDevObjInst, DIJOFS_Y, DIPH_BYOFFSET)) ) {
#ifdef _DEBUG
                OutputDebugString(TEXT("GCDEF.DLL: DisplayAvailableAxis: GetObjectInfo Failed to find DIJOFS_Y!\n"));
#endif
            }

#ifdef DEADZONE
            // Set text labels!
            ::SendDlgItemMessage(hDlg, IDC_Y_AXIS_LABEL_DEADZONE,   WM_SETTEXT, 0, (LPARAM)(LPCTSTR)pDevObjInst->tszName);
            ::SendDlgItemMessage(hDlg, IDC_Y_AXIS_LABEL_SATURATION, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)pDevObjInst->tszName);
#endif //DEADZONE

            // just in case it has Y but not X
            if( ptszBuff && lstrlen(ptszBuff) ) {   // Whisltler PREFIX 45092
                int nLen=STR_LEN_32-lstrlen(ptszBuff);
                StrNCat(ptszBuff, TEXT(" / "), nLen);
            }

            int nLen=STR_LEN_32-lstrlen(ptszBuff);
            StrNCat(ptszBuff, pDevObjInst->tszName, nLen);

            // Set the Output Label!
            ::SendDlgItemMessage(hDlg, IDC_RAWY, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)pDevObjInst->tszName);

            // Remove the HAS_Y flag
            nAxisFlags &= ~HAS_Y;
        }

        if( pDevObjInst )
            delete (pDevObjInst);

        ::SendDlgItemMessage(hDlg, IDC_JOYLIST1_LABEL, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)ptszBuff);

//		if (ptszBuff)
//			delete[] (ptszBuff);
    }
} //*** end of UpdateXYLabel

//*******************************************************************************
//
//   FUNCTION: UpdateProgressLabel(HWND hDlg)
//
//   PURPOSE: 	Updates Axis specific labels based on the current Calibration stage.
//
//   COMMENTS:	
//	
//*******************************************************************************
BOOL UpdateProgressLabel(const HWND hDlg)
{
    // Array of supported axis!
    const DWORD dwOffsetArray[] = {DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ, DIJOFS_SLIDER(0), DIJOFS_SLIDER(1)};
    BOOL bRet = FALSE; 

    LPDIDEVICEOBJECTINSTANCE_DX3 pDevObjInst = (LPDIDEVICEOBJECTINSTANCE_DX3)_alloca(sizeof(DIDEVICEOBJECTINSTANCE_DX3));
    assert (pDevObjInst);

    ZeroMemory(pDevObjInst, sizeof(DIDEVICEOBJECTINSTANCE_DX3));

    pDevObjInst->dwSize = sizeof(DIDEVICEOBJECTINSTANCE_DX3);

    // Get it's text
    if( SUCCEEDED(pdiDevice2->GetObjectInfo((LPDIDEVICEOBJECTINSTANCE)pDevObjInst, dwOffsetArray[nCalState-3], DIPH_BYOFFSET)) ) {
        // Set it's text
        ::SendDlgItemMessage(hDlg, IDC_JOYLIST2_LABEL, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)pDevObjInst->tszName);
        ::SendDlgItemMessage(hDlg, IDC_RAWX,              WM_SETTEXT, 0, (LPARAM)(LPCTSTR)pDevObjInst->tszName);
        bRet = TRUE;
    }

    return(bRet);
}

///////////////////////////////////////////////////////////////////////////////
// FUNCTION:	myitoa(long n, LPTSTR lpStr)
//
// PARAMETERS:	BYTE   n     - Number to be translated
//             LPTSTR lpStr - Buffer to recieve translated value
//
// PURPOSE:	Convert BYTE values < 20 to strings.
///////////////////////////////////////////////////////////////////////////////
void myitoa(long n, LPTSTR lpStr)
{
    long sign = n;

    if( n < 0 )
        n = - n;

    LPTSTR pchStart = lpStr;

    do {
        *lpStr++ = (TCHAR)(n % 10 + '0');
    } while( (n /= 10) > 0 );

    if( sign < 0 )
        *lpStr++ = '-';
    *lpStr = '\0';
    reverse(pchStart);
}

void reverse(LPTSTR string)
{
    TCHAR c;
    short i, j;

    for( i = 0, j = lstrlen(string) - 1; i < j; i++, j-- ) {
        c = string[j];
        string[j] = string[i];
        string[i] = c;
    }
}

//*******************************************************************************
//
//   FUNCTION: RawDataSelected( HWND hWnd, BOOL bEnable )
//
//   PURPOSE: 	Shows/Hides Raw data associated windows.
//
//   COMMENTS:	
//	
//*******************************************************************************
void RawDataSelected( const HWND hWnd, BOOL bEnable )
{
    const USHORT nCtrlArray[] = {IDC_RAWX, IDC_RAWY, IDC_RAWXOUTPUT, IDC_RAWYOUTPUT};
    BYTE nCtrls = sizeof(nCtrlArray)/sizeof(short);

    do {
        SetWindowPos( GetDlgItem( hWnd,  nCtrlArray[--nCtrls]), NULL, NULL, NULL, NULL, NULL, 
                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | ((bEnable) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
    } while( nCtrls );
}

//*******************************************************************************
//
//   FUNCTION: TimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
//
//   PURPOSE: 	TimerProc for the Calibration Wizard.
//					Searches for button presses, then moves to next stage/finish.
//
//   COMMENTS:	
//	
//*******************************************************************************
VOID CALLBACK TimerProc(const HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
    if( SUCCEEDED(DIUtilPollJoystick(pdiDevice2, lpDIJoyState)) ) {
        CollectCalInfo(hWnd, lpDIJoyState);

        // Don't bother checking for key presses if the user is in the POV stage!
        if( nCalState <= JCS_S1_MOVE ) {
            // Catch button presses...
            static BYTE nDownButton = 0xff;
            BYTE i = 0;

            int nButtons = pdiCpl->GetStateFlags()->nButtons;

            // only attempt to check buttons we KNOW we have!!!
            while( nButtons ) {
                // check for a button press
                if( lpDIJoyState->rgbButtons[i] & 0x80 ) {
                    if( nDownButton != 0xff )
                        break;

                    // Let the Next button handle the processing
                    ::PostMessage(GetParent(hWnd), PSM_PRESSBUTTON, (WPARAM)(int)(nCalState > JCS_S1_MOVE) ? PSBTN_FINISH : PSBTN_NEXT, 0);

                    // Store the button that went down!
                    nDownButton = i;

                    // mission accomplished!
                    return;
                }
                // reset the nDownButton flag
                else if( i == nDownButton )
                    nDownButton = 0xff;

                nButtons &= ~(HAS_BUTTON1<<i++);
            } 
            // end of catch for button presses!
        }
    }
}

// This is because PSN_WIZFINISH is Documented to be sent to every page dlg proc on exit... but it doesn't!
static void WizFinish(const HWND hWnd)
{
    HRESULT hres;

    KillTimer(hWnd, ID_CAL_TIMER);

    // assign the new ranges
    SetMyRanges(pdiDevice2, pRanges, pdiCpl->GetStateFlags()->nAxis);

    LPDIRECTINPUTJOYCONFIG pdiJoyConfig;
    pdiCpl->GetJoyConfig(&pdiJoyConfig);

    if( pdiCpl->GetStateFlags()->nPOVs ) {
        pdiDevice2->Unacquire();
        SetCalibrationMode( FALSE );
        pdiJoyConfig->Acquire();

        CopyRange( &pJoyConfig->hwc.hwv.jrvHardware, pRanges );
        memcpy( pJoyConfig->hwc.hwv.dwPOVValues, pRanges->dwPOV, sizeof(DWORD)*4 );

        hres = pdiJoyConfig->SetConfig(pdiCpl->GetID(), (LPDIJOYCONFIG)pJoyConfig, DIJC_REGHWCONFIGTYPE);
      #ifdef WE_SUPPORT_CALIBRATING_POVS
        if( SUCCEEDED(hres) ) {
            CalibratePolledPOV( &pJoyConfig->hwc );

            // set POV positions!
            if( bPolledPOV ) {
                SetMyPOVRanges(pdiDevice2);
            }
        }
      #endif
    }

    pdiJoyConfig->SendNotify();
    pdiDevice2->Unacquire();
}