/**************************************************************************
 *
 *  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 - 1995  Microsoft Corporation.  All Rights Reserved.
 *
 **************************************************************************/
/****************************************************************************
 *
 *   dialogs.c: Dialog box processing
 *
 *   Vidcap32 Source code
 *
 ***************************************************************************/

#include <windows.h>
#include <windowsx.h>
#include <commdlg.h>
#include <mmsystem.h>
#include <mmreg.h>
#include <io.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <dos.h>
#include <vfw.h>
#include <tchar.h>

#include "arrow.h"
#include "rlmeter.h"
#include "vidcap.h"
#include "vidframe.h"
#include "help.h"

static long GetFreeDiskSpaceInKB(LPTSTR) ;
static int  CountMCIDevices(UINT) ;

LRESULT FAR PASCAL MCISetupProc(HWND, unsigned, WPARAM, LPARAM);


//--- utility functions  ---------------------------------------------------



/*----------------------------------------------------------------------------*\
|   SmartWindowPosition (HWND hWndDlg, HWND hWndShow)
|                                                                              |
|   Description:                                                               |
|       This function attempts to position a dialog box so that it
|       does not obscure the hWndShow window. This function is
|       typically called during WM_INITDIALOG processing.
|                                                                              |
|   Arguments:                                                                 |
|       hWndDlg         handle of the soon to be displayed dialog
|       hWndShow        handle of the window to keep visible
|                                                                              |
|   Returns:                                                                   |
|       1 if the windows overlap and positions were adjusted
|       0 if the windows don't overlap
|                                                                              |
\*----------------------------------------------------------------------------*/
int SmartWindowPosition (HWND hWndDlg, HWND hWndShow)
{
    RECT rc, rcDlg, rcShow;
    int iHeight, iWidth;
    int iScreenHeight, iScreenWidth;

    GetWindowRect(hWndDlg, &rcDlg);
    GetWindowRect(hWndShow, &rcShow);

    iScreenHeight = GetSystemMetrics(SM_CYSCREEN);
    iScreenWidth = GetSystemMetrics(SM_CXSCREEN);

    InflateRect (&rcShow, 5, 5); // allow a small border
    if (IntersectRect(&rc, &rcDlg, &rcShow)){
        /* the two do intersect, now figure out where to place */
        /* this dialog window.  Try to go below the Show window*/
        /* first and then to the right, top and left.	   */

        /* get the size of this dialog */
        iHeight = rcDlg.bottom - rcDlg.top;
        iWidth = rcDlg.right - rcDlg.left;

        if ((UINT)(rcShow.bottom + iHeight + 1) <  (UINT)iScreenHeight){
                /* will fit on bottom, go for it */
                rc.top = rcShow.bottom + 1;
                rc.left = (((rcShow.right - rcShow.left)/2) + rcShow.left)
    		        - (iWidth/2);
        } else if ((UINT)(rcShow.right + iWidth + 1) < (UINT)iScreenWidth){
                /* will fit to right, go for it */
                rc.left = rcShow.right + 1;
                rc.top = (((rcShow.bottom - rcShow.top)/2) + rcShow.top)
    	        - (iHeight/2);
        } else if ((UINT)(rcShow.top - iHeight - 1) > 0){
                /* will fit on top, handle that */
                rc.top = rcShow.top - iHeight - 1;
                rc.left = (((rcShow.right - rcShow.left)/2) + rcShow.left)
    		        - (iWidth/2);
        } else if ((UINT)(rcShow.left - iWidth - 1) > 0){
                /* will fit to left, do it */
                rc.left = rcShow.left - iWidth - 1;
                rc.top = (((rcShow.bottom - rcShow.top)/2) + rcShow.top)
    	        - (iHeight/2);
        } else {
                /* we are hosed, they cannot be placed so that there is */
                /* no overlap anywhere.  To minimize the damage just put*/
                /* the dialog in the lower left corner of the screen    */
                rc.top = (int)iScreenHeight - iHeight;
                rc.left = (int)iScreenWidth - iWidth;
        }

        /* make any adjustments necessary to keep it on the screen */
        if (rc.left < 0) rc.left = 0;
        else if ((UINT)(rc.left + iWidth) > (UINT)iScreenWidth)
                rc.left = (int)(iScreenWidth - iWidth);

        if (rc.top < 0)  rc.top = 0;
        else if ((UINT)(rc.top + iHeight) > (UINT)iScreenHeight)
                rc.top = (int)iScreenHeight - iHeight;

        SetWindowPos(hWndDlg, NULL, rc.left, rc.top, 0, 0,
    	        SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
        return 1;
    } // if the windows overlap by default

    return 0;
}

//
// GetFreeDiskSpace: Function to Measure Available Disk Space
//
static long GetFreeDiskSpaceInKB(LPTSTR pFile)
{
    DWORD dwFreeClusters, dwBytesPerSector, dwSectorsPerCluster, dwClusters;
    TCHAR RootName[MAX_PATH];
    LPTSTR ptmp;    //required arg

    // need to find path for root directory on drive containing
    // this file.

    GetFullPathName(pFile, sizeof(RootName)/sizeof(RootName[0]), RootName, &ptmp);

    // truncate this to the name of the root directory (god how tedious)
    if ((RootName[0] == TEXT('\\')) && (RootName[1] == TEXT('\\'))) {

        // path begins with  \\server\share\path so skip the first
        // three backslashes
        ptmp = &RootName[2];
        while (*ptmp && (*ptmp != TEXT('\\'))) {
            ptmp++;
        }
        if (*ptmp) {
            // advance past the third backslash
            ptmp++;
        }
    } else {
        // path must be drv:\path
        ptmp = RootName;
    }

    // find next backslash and put a null after it
    while (*ptmp && (*ptmp != TEXT('\\'))) {
        ptmp++;
    }
    // found a backslash ?
    if (*ptmp) {
        // skip it and insert null
        ptmp++;
        *ptmp = TEXT('\0');
    }



    if (!GetDiskFreeSpace(RootName,
		&dwSectorsPerCluster,
		&dwBytesPerSector,
		&dwFreeClusters,
		&dwClusters)) {
	    MessageBoxID(IDS_ERR_MEASUREFREEDISK, MB_OK | MB_ICONINFORMATION);
	    return (-1);
    }
    return(MulDiv (dwSectorsPerCluster * dwBytesPerSector,
		   dwFreeClusters,
		   1024));
}

//
// CountMCIDevices: Function to Find the Number of MCI Devices of a Type
//
static int CountMCIDevices(UINT wType)
{
    int               nTotal = 0 ;
    DWORD             dwCount ;
    MCI_SYSINFO_PARMS mciSIP ;

    mciSIP.dwCallback = 0 ;
    mciSIP.lpstrReturn = (LPTSTR)(LPVOID) &dwCount ;
    mciSIP.dwRetSize = sizeof(DWORD) ;
    mciSIP.wDeviceType = wType ;

    // Use an MCI command to get the info
    if (! mciSendCommand(0, MCI_SYSINFO, MCI_SYSINFO_QUANTITY,
                         (DWORD_PTR)(LPVOID) &mciSIP))
        nTotal = (int) *((LPDWORD) mciSIP.lpstrReturn) ;

    return nTotal ;
}



/* lMicroSec = StringRateToMicroSec(szRate)
 *
 * Convert <szRate> (e.g. "3.75" representing 3.75 frames per second)
 * to microseconds (e.g. 266667L microseconds per frame).
 *
 * If the rate is close to zero or negative, then 0L is returned.
 */
DWORD StringRateToMicroSec(PSTR szRate)
{
	double		dRate;

	dRate = atof(szRate);
	
	if (dRate < 0.0001) {
		return 0L;
	} else {
		return (DWORD) /*floor*/((1e6 / dRate) + 0.5);
        }
}

/* ach = MicroSecToStringRate(achRate, lMicroSec)
 *
 * Convert <lMicroSec> (e.g. 266667L microseconds per frame) to a
 * string rate (e.g. "3.75" representing 3.75 frames per second).
 * Returns <achRate>.
 */
PSTR MicroSecToStringRate(PSTR achRate, DWORD dwMicroSec)
{
	sprintf(achRate, "%.3f",
		(dwMicroSec == 0L) ? 0.0 : (1e6 / (double) dwMicroSec));

	return achRate;
}

/*
 * update the text of an edit field based on a comarrow up or down change
 * - write the text in N.NNN format (truncated to an integer)
 */
LONG FAR PASCAL
MilliSecVarArrowEditChange(
    HWND hwndEdit,
    UINT uCode,
    LONG lMin,
    LONG lMax,
    UINT uInc
)
{
    TCHAR achTemp[32];
    LONG l;

    GetWindowText(hwndEdit, achTemp, sizeof(achTemp));

    l = atol(achTemp);
    if(uCode == SB_LINEUP ) {

	if(l + (long)uInc <= lMax ) {
	    l += uInc;
	    wsprintf(achTemp, "%ld.000", l );
	    SetWindowText(hwndEdit, achTemp );
        } else {
	    MessageBeep( 0 );
	}
    } else if (uCode == SB_LINEDOWN ) {
	if( l-(long)uInc >= lMin ) {
	    l -= uInc;
	    wsprintf( achTemp, "%ld.000", l );
	    SetWindowText( hwndEdit, achTemp );
        } else {
	    MessageBeep( 0 );
	}
    }
    return( l );
}


BOOL MCIGetDeviceNameAndIndex (HWND hwnd, LPINT lpnIndex, LPTSTR lpName)
{
    HWND hwndCB;
    TCHAR buf[160];
    TCHAR *cp;

    hwndCB = GetDlgItem( hwnd, IDD_MCI_SOURCE );
    *lpnIndex = (int)SendMessage( hwndCB, CB_GETCURSEL, 0, 0L);
    SendMessage( hwndCB, CB_GETLBTEXT, *lpnIndex,
    		(LONG_PTR)(LPTSTR) buf );
    // Point cp to the system name
    for (cp = buf + lstrlen(buf); cp > buf; cp--) {
        if (*cp == ' ' && *(cp-1) == ',') {
            cp++;
            break;
	}
    }
    lstrcpy (lpName, cp);
    return TRUE;
}


/*--------------------------------------------------------------+
| TimeMSToHMSString() - change milliseconds into a time string   |
+--------------------------------------------------------------*/
void FAR PASCAL TimeMSToHMSString (DWORD dwMS, LPTSTR lpTime)
{
	DWORD	dwTotalSecs;
	LONG	lHundredths;
	WORD	wSecs;
	WORD	wMins;
	WORD	wHours;

	/* convert to number of seconds */
	dwTotalSecs = dwMS / 1000;
	
	/* keep the remainder part */
	lHundredths = (dwMS - (dwTotalSecs * 1000)) / 10;
		
	/* break down into other components */
	wHours = (WORD)(dwTotalSecs / 3600);	// get # Hours
	dwTotalSecs -= (wHours * 3600);
	
	wMins = (WORD)(dwTotalSecs / 60);	// get # Mins
	dwTotalSecs -= (wMins * 60);
	
	wSecs = (WORD)dwTotalSecs;	// what's left is # seconds
	
	/* build the string */
	wsprintf((TCHAR far *)lpTime, "%02u:%02u:%02u.%02lu", wHours, wMins,
		    wSecs, lHundredths);
}


/*--------------------------------------------------------------+
| TimeHMSStringToMS() - change Time string to milliseconds     |
|                       returns dwMilliseconds or -1 if error  |
+--------------------------------------------------------------*/
LONG NEAR PASCAL  TimeHMSStringToMS (LPTSTR lpsz)
{
    TCHAR	achTime[12];	// buffer for time string (input)
    DWORD	dwMSecs;	// total MSecs for this thing */
    TCHAR	*pDelim;	// pointer to next delimeter
    TCHAR	*p;		// general pointer
    DWORD	dwHours = 0;	// # of hours
    DWORD	dwMins = 0;	// # of minutes
    DWORD	dwSecs = 0;		// # of seconds
    UINT	wHundredths = 0;	// # hundredths

    _tcsncpy(achTime, lpsz, sizeof (achTime));

    if (achTime[0] == '\0')
        return -1;	// bad TCHAR so error out
    	
    /* rip through the whole string and look for illegal TCHARs */
    for (p = achTime; *p ; p++){
        if (!_istdigit(*p) && *p != '.' && *p != ':')
    	return -1;	// bad char so error out
    }

    /* go find the hundredths portion if it exists */
    pDelim = _tcschr(achTime, '.');
    if (pDelim && *pDelim){
        p = _tcsrchr(achTime, '.');
        if (pDelim != p) {
    	    return -1;		// string has > 1 '.', return error
        }

        p++;			// move up past delim
        if (_tcslen(p) > 2) {
    	    *(p+2) = '\0';		// knock off all but hundredths
        }

        wHundredths = _ttoi(p);	// get the fractional part

        *pDelim = '\0';		// null out this terminator
    }

    /* try and find seconds */
    pDelim = _tcsrchr(achTime, ':');	// get last ':'
    if (pDelim) {
        p = (pDelim+1);
    } else {
        // no colon - assume just seconds in string
        p = achTime;
    }
    dwSecs = _ttoi(p);

    if (pDelim) {
        *pDelim = '\0';

        /* go and get the minutes part */
        pDelim = _tcsrchr(achTime, ':');
        if (pDelim) {
            p = (pDelim + 1);
        } else {
            // no more colons - assume remainder is just minutes
            p = achTime;
        }
        dwMins = _ttoi(p);

        if (pDelim) {
            *pDelim = '\0';

            /* get the hours */
            p = achTime;
            dwHours = _ttoi(p);
        }
    }

    /* now we've got the hours, minutes, seconds and any	*/
    /* fractional part.  Time to build up the total time	*/

    dwSecs += (dwHours * 3600);	// add in hours worth of seconds
    dwSecs += (dwMins * 60);	// add in minutes worth of seconds
    dwMSecs = (dwSecs * 1000L);
    dwMSecs += (wHundredths * 10L);

    /* now we've got the total number of milliseconds */
    return dwMSecs;
}


/*
 *  MCIDeviceClose
 *      This routine closes the open MCI device.
 */

void MCIDeviceClose (void)
{
    mciSendString( "close mciframes", NULL, 0, NULL );
}



/*
 *  MCIDeviceOpen
 *      This routine opens the mci device for use, and sets the
 *      time format to milliseconds.
 *      Return FALSE on error;
 */

BOOL MCIDeviceOpen (LPTSTR lpDevName)
{
    TCHAR        ach[160];
    DWORD dwMCIError;

    wsprintf( ach, "open %s shareable wait alias mciframes", (LPTSTR) lpDevName);
    dwMCIError = mciSendString( ach, NULL, 0, NULL );
    if( dwMCIError )  {
        return(FALSE);
    }

    dwMCIError = mciSendString( "set mciframes time format milliseconds",
	    NULL, 0, NULL );
    if( dwMCIError ) {
        MCIDeviceClose();
        return(FALSE);
    }
    return ( TRUE );

}


/*
 *  MCIDeviceGetPosition
 *      Stores the current device position in milliseconds in lpdwPos.
 *      Returns TRUE on success, FALSE if error.
 */
BOOL FAR PASCAL MCIDeviceGetPosition (LPDWORD lpdwPos)
{
    TCHAR        ach[80];
    DWORD dwMCIError;

    dwMCIError = mciSendString( "status mciframes position wait",
	    ach, sizeof(ach), NULL );
    if( dwMCIError ) {
        *lpdwPos = 0L;
        return FALSE;
    }

    *lpdwPos = _ttol( ach );
    return TRUE;
}

#ifndef USE_ACM

// --- audio streaming ------------------------------------------------

// the ShowLevel dialog streams data in from the input and
// shows the current volume.

// buffers into which sound data is recorded
#define NUM_LEVEL_BUFFERS   2

// the buffer size is calculated to be about 1/20 sec
#define UPDATES_PER_SEC     20

/*
 * we save all our data in one of these, and write a pointer to it
 * into the dialog DWL_USER field.
 */

typedef struct _LevelStreamData {
    LPWAVEHDR alpWave[NUM_LEVEL_BUFFERS];
    PCMWAVEFORMAT FAR * pwf;
    HWAVEIN hwav;
    int buffersize;
} LEVELSTREAMDATA, FAR * PLEVELSTREAMDATA;


//open the wave-device in the given format, queue all the buffers and
//start data streaming. Save the wavein device to the dialog DWL_USER window
//data area so we can close it on dialog dismissal.
BOOL
OpenStream(HWND hDlg, PCMWAVEFORMAT FAR * pwf)
{
    PLEVELSTREAMDATA pInfo;
    int i;


    pInfo = (PLEVELSTREAMDATA) GlobalLock(GlobalAlloc(GHND, sizeof(LEVELSTREAMDATA)));

    if (pInfo == NULL) {
        return(FALSE);
    }


    // complete remaining areas of wf
    pwf->wf.wFormatTag = WAVE_FORMAT_PCM;
    pwf->wf.nBlockAlign = pwf->wf.nChannels * pwf->wBitsPerSample / 8;
    pwf->wf.nAvgBytesPerSec = pwf->wf.nSamplesPerSec * pwf->wf.nBlockAlign;

    // save for later use
    pInfo->pwf = pwf;

    // buffer size a fixed fraction of a second
    pInfo->buffersize = pwf->wf.nAvgBytesPerSec/UPDATES_PER_SEC;


    pInfo->hwav = NULL;

    if (waveInOpen(
        &pInfo->hwav,
        WAVE_MAPPER,
        (LPWAVEFORMATEX)pwf,
        (DWORD) hDlg,               // callback via MM_WIM_ messages to dialogproc
        0,
        CALLBACK_WINDOW)) {
            SetWindowLong(hDlg, DWL_USER, 0);
            return(FALSE);
    }

    // store the info structure in the dialog, so that even if we fail
    // on this routine we will clean up correctly
    SetWindowLong(hDlg, DWL_USER, (long) pInfo);

    // set all the wave headers to null (for cleanup if error)
    for (i = 0; i < NUM_LEVEL_BUFFERS; i++) {
        pInfo->alpWave[i] = NULL;
    }

    // alloc, prepare and add all the buffers
    for (i = 0; i < NUM_LEVEL_BUFFERS; i++) {

        pInfo->alpWave[i] = GlobalLock(GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE,
                        sizeof(WAVEHDR) + pInfo->buffersize));
        if (pInfo->alpWave[i] == NULL) {
            return(FALSE);
        }

        pInfo->alpWave[i]->lpData = (LPBYTE) (pInfo->alpWave[i] + 1);
        pInfo->alpWave[i]->dwBufferLength = pInfo->buffersize;
        pInfo->alpWave[i]->dwBytesRecorded = 0;
        pInfo->alpWave[i]->dwUser = 0;
        pInfo->alpWave[i]->dwFlags = 0;
        pInfo->alpWave[i]->dwLoops = 0;

        if (waveInPrepareHeader(pInfo->hwav, pInfo->alpWave[i], sizeof(WAVEHDR))) {
            return(FALSE);
        }

        if (waveInAddBuffer(pInfo->hwav, pInfo->alpWave[i], sizeof(WAVEHDR))) {
            return(FALSE);
        }
    }

    waveInStart(pInfo->hwav);

    return(TRUE);
}

// terminate the data streaming on a wavein device associated with a
// dialog, and clean up the buffers allocated
void
CloseStream(HWND hDlg)
{
    PLEVELSTREAMDATA pInfo;
    int i;


    // pick up our info from the dialog
    pInfo = (PLEVELSTREAMDATA) GetWindowLong(hDlg, DWL_USER);
    if ((pInfo == NULL) || (pInfo->hwav == NULL)) {
        return;
    }

    // stop streaming data
    waveInStop(pInfo->hwav);

    // release all buffers
    waveInReset(pInfo->hwav);

    // unlock and free buffers
    for (i = 0; i < NUM_LEVEL_BUFFERS; i++) {
        if (pInfo->alpWave[i]) {
            waveInUnprepareHeader(pInfo->hwav, pInfo->alpWave[i], sizeof(WAVEHDR));
            GlobalFree(GlobalHandle(pInfo->alpWave[i]));
            pInfo->alpWave[i] = NULL;
        }

    }
    waveInClose(pInfo->hwav);

    GlobalFree(GlobalHandle(pInfo));

    SetWindowLong(hDlg, DWL_USER, 0);


}

// we have received a block of data. work out the level(s) and send to
// the appropriate control on the dialog, and then requeue the buffer.
// return FALSE if any error occurs, otherwise TRUE
BOOL
StreamData(HWND hDlg, HWAVEIN hwav, LPWAVEHDR pHdr)
{
    PLEVELSTREAMDATA pInfo;
    int n = 0;
    int LevelLeft = 0, LevelRight = 0;
    int i, l;

    // pick up our info from the dialog
    pInfo = (PLEVELSTREAMDATA) GetWindowLong(hDlg, DWL_USER);
    if ((pInfo == NULL) || (pInfo->hwav != hwav)) {
        return FALSE;
    }

    // go through all samples in buffer looking for maximum absolute level
    while (n < pInfo->buffersize) {

        /*
         * volumes go above and below the mean level - we are
         * interested in the absolute volume
         * 8 bit samples are in the range 0..255
         * 16-bit samples are in the range -32768..+32767
         */

        // skip the first byte if 16-bit
        // and adjust to be in range -127..+128
        if (pInfo->pwf->wBitsPerSample == 16) {
            n++;
            i = (int) (signed char) pHdr->lpData[n];
        } else {
            i = (int) ((unsigned char) pHdr->lpData[n]) - 128;
        }

        // skip past the byte we've picked up
        n++;

        // take absolute volume level
        if (i < 0) {
            i = -i;
        }

        // convert to percentage
        l = (i*100) / 128;

        // compare against current max
        if (LevelLeft < l) {
            LevelLeft = l;
        }


        // if stereo, repeat for right channel
        if (pInfo->pwf->wf.nChannels == 2) {
            // skip the first byte if 16-bit
            if (pInfo->pwf->wBitsPerSample == 16) {
                n++;
                i = (int) (signed char) pHdr->lpData[n];
            } else {
                i = (int) ((unsigned char) pHdr->lpData[n]) - 128;
            }

            // skip past the byte we've picked up
            n++;

            // take absolute volume level
            if (i < 0) {
                i = -i;
            }

            // convert to percentage
            l = (i*100) / 128;

            // compare against current max
            if (LevelRight < l) {
                LevelRight = l;
            }
        }
    }

    // put the buffer back on the queue
    if (waveInAddBuffer(pInfo->hwav, pHdr, sizeof(WAVEHDR))) {
        return(FALSE);
    }

    // send new level to dialog control
    SendDlgItemMessage(hDlg, IDRL_LEVEL1, WMRL_SETLEVEL, 0, LevelLeft);
    if (pInfo->pwf->wf.nChannels == 2) {
        SendDlgItemMessage(hDlg, IDRL_LEVEL2, WMRL_SETLEVEL, 0, LevelRight);
    }

    return(TRUE);
}

#endif  // ! USE_ACM


// --- dialog procs -----------------------------------------------------


//
// AboutProc: About Dialog Box Procedure
//
LRESULT FAR PASCAL AboutProc(HWND hDlg, UINT Message, WPARAM wParam, LPARAM lParam)
{
    switch (Message) {
        case WM_INITDIALOG :
             return TRUE ;

        case WM_COMMAND :
            switch (GET_WM_COMMAND_ID(wParam, lParam)) {
                case IDOK :
                    EndDialog(hDlg, TRUE) ;
                    return TRUE ;

                case IDCANCEL :
                    EndDialog(hDlg, FALSE) ;
                    return TRUE ;
            }
            break ;
    }

    return FALSE ;
}

#ifndef USE_ACM

/*
 * dialog proc for IDD_RECLVLMONO and IDD_RECLVLSTEREO - show current
 * volume level
 */
LRESULT FAR PASCAL
ShowLevelProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message) {

    case WM_INITDIALOG:
        if (!OpenStream(hDlg, (PCMWAVEFORMAT FAR *) lParam)) {
            MessageBoxID(IDS_ERR_ACCESS_SOUNDDRIVER, MB_OK|MB_ICONSTOP);
            EndDialog(hDlg, FALSE);
        }
        return(TRUE);

    case WM_COMMAND:
        switch(GET_WM_COMMAND_ID(wParam, lParam)) {
        case IDOK:
        case IDCANCEL:

            CloseStream(hDlg);
            EndDialog(hDlg, TRUE);
            return(TRUE);
        }
        break;

    case MM_WIM_DATA:
        if (!StreamData(hDlg, (HWAVEIN)wParam, (LPWAVEHDR)lParam)) {
            MessageBoxID(IDS_ERR_ACCESS_SOUNDDRIVER, MB_OK|MB_ICONSTOP);
            CloseStream(hDlg);
            EndDialog(hDlg, FALSE);
        }
        return(TRUE);

    }
    return FALSE;
}





//
// AudioFormatProc: Audio Format Setting Dialog Box Procedure
//
LRESULT FAR PASCAL AudioFormatProc(HWND hDlg, UINT Message, WPARAM wParam, LPARAM lParam)
{
    static int                nChannels ;
    static UINT               wSample ;
    static DWORD              dwFrequency ;

    switch (Message) {
        case WM_INITDIALOG :
            nChannels = IDD_ChannelIDs + glpwfex->nChannels ;
            CheckRadioButton(hDlg, IDD_ChannelMono, IDD_ChannelStereo, nChannels) ;
            wSample = IDD_SampleIDs + glpwfex->wBitsPerSample / 8 ;
            CheckRadioButton(hDlg, IDD_Sample8Bit, IDD_Sample16Bit, wSample) ;
            dwFrequency = IDD_FreqIDs + glpwfex->nSamplesPerSec / 11025 ;
            CheckRadioButton(hDlg, IDD_Freq11kHz, IDD_Freq44kHz, (UINT)dwFrequency) ;
            return TRUE ;

        case WM_COMMAND :
            switch (GET_WM_COMMAND_ID(wParam, lParam)) {
                case IDD_SetLevel:
                {
                    // get the current data into a PCMWAVEFORMAT struct,
                    // and run the ShowLevel dialog
                    PCMWAVEFORMAT wf;
                    UINT dlgid;

                    if (IsDlgButtonChecked(hDlg, IDD_ChannelMono)) {
                        wf.wf.nChannels = 1;
                        dlgid = IDD_RECLVLMONO;
                    } else {
                        wf.wf.nChannels = 2;
                        dlgid = IDD_RECLVLSTEREO;
                    }

                    if (IsDlgButtonChecked(hDlg, IDD_Sample8Bit)) {
                        wf.wBitsPerSample = 8;
                    } else {
                        wf.wBitsPerSample = 16;
                    }

                    if (IsDlgButtonChecked(hDlg, IDD_Freq11kHz)) {
                        wf.wf.nSamplesPerSec = 11025 ;
                    } else if (IsDlgButtonChecked(hDlg, IDD_Freq22kHz)) {
                        wf.wf.nSamplesPerSec = 22050 ;
                    } else {
                        wf.wf.nSamplesPerSec =  44100 ;
                    }

                    DoDialog(
                        hDlg,
                        dlgid,
                        ShowLevelProc,
                        (LPARAM) &wf);
                    break;
                }

                case IDOK :
                    if (IsDlgButtonChecked(hDlg, IDD_ChannelMono))
                        nChannels = 1 ;
                    else
                        if (IsDlgButtonChecked(hDlg, IDD_ChannelStereo))
                            nChannels = 2 ;
                        else {
                            MessageBeep(MB_ICONEXCLAMATION) ;
                            return FALSE ;
                        }

                    if (IsDlgButtonChecked(hDlg, IDD_Sample8Bit))
                        wSample = 8 ;
                    else
                        if (IsDlgButtonChecked(hDlg, IDD_Sample16Bit))
                            wSample = 16 ;
                        else {
                            MessageBeep(MB_ICONEXCLAMATION) ;
                            return FALSE ;
                        }

                    if (IsDlgButtonChecked(hDlg, IDD_Freq11kHz))
                        dwFrequency = 11025 ;
                    else
                        if (IsDlgButtonChecked(hDlg, IDD_Freq22kHz))
                            dwFrequency = 22050 ;
                        else
                            if (IsDlgButtonChecked(hDlg, IDD_Freq44kHz))
                                dwFrequency = 44100 ;
                            else {
                                MessageBeep(MB_ICONEXCLAMATION) ;
                                return FALSE ;
                            }

                    // All the entries verfied OK -- save them now
                    glpwfex->nChannels = nChannels ;
                    glpwfex->wBitsPerSample = wSample ;
                    glpwfex->nSamplesPerSec = dwFrequency ;
                    glpwfex->nBlockAlign =  glpwfex->nChannels * (glpwfex->wBitsPerSample / 8) ;
                    glpwfex->nAvgBytesPerSec = (long) glpwfex->nSamplesPerSec *
                                                      glpwfex->nBlockAlign ;
                    glpwfex->cbSize = 0 ;
                    glpwfex->wFormatTag = WAVE_FORMAT_PCM ;
                    EndDialog(hDlg, TRUE) ;
                    return TRUE ;

                case IDCANCEL :
                    EndDialog(hDlg, FALSE) ;
                    return TRUE ;
            }
            break ;
    }

    return FALSE ;
}

#endif // ! USE_ACM

//
// AllocCapFileProc: Capture file Space Allocation Dialog Box Procedure
//
LRESULT FAR PASCAL AllocCapFileProc(HWND hDlg, UINT Message, WPARAM wParam, LPARAM lParam)
{
    static int      nFreeMBs = 0 ;

    switch (Message) {
        case WM_INITDIALOG :
        {
            int              fh ;
            long             lFileSize = 0 ;
            long             lFreeSpaceInKB ;
            TCHAR	     achCapFile[_MAX_PATH] ;

            // Get current capture file name and measure its size
            capFileGetCaptureFile(ghWndCap, achCapFile, sizeof(achCapFile) / sizeof(TCHAR)) ;
            if ((fh = _open(achCapFile, _O_RDONLY)) != -1) {
                if ((lFileSize = _lseek(fh, 0L, SEEK_END)) == -1L) {
                    MessageBoxID(IDS_ERR_SIZECAPFILE,
#ifdef BIDI
                MB_RTL_READING |
#endif

                    MB_OK | MB_ICONEXCLAMATION) ;
                    lFileSize = 0 ;
                }
                _close(fh) ;
            }

            // Get free disk space and add current capture file size to that.
            // Convert the available space to MBs.
            if ((lFreeSpaceInKB = GetFreeDiskSpaceInKB(achCapFile)) != -1L) {
                lFreeSpaceInKB += lFileSize / 1024 ;
                nFreeMBs = lFreeSpaceInKB / 1024 ;
                SetDlgItemInt(hDlg, IDD_SetCapFileFree, nFreeMBs, TRUE) ;
            } else {

                EnableWindow(GetDlgItem(hDlg, IDD_SetCapFileFree), FALSE);

            }

            gwCapFileSize = (WORD) (lFileSize / ONEMEG);

            SetDlgItemInt(hDlg, IDD_SetCapFileSize, gwCapFileSize, TRUE) ;
            return TRUE ;
        }

        case WM_COMMAND :
            switch (GET_WM_COMMAND_ID(wParam, lParam)) {
                case IDOK :
                {
                    int         iCapFileSize ;

                    iCapFileSize = (int) GetDlgItemInt(hDlg, IDD_SetCapFileSize, NULL, TRUE) ;
                    if (iCapFileSize <= 0 || iCapFileSize > nFreeMBs) {
                        // You are asking for more than we have !! Sorry, ...
                        SetDlgItemInt(hDlg, IDD_SetCapFileSize, iCapFileSize, TRUE) ;
                        SetFocus(GetDlgItem(hDlg, IDD_SetCapFileSize)) ;
                        MessageBeep(MB_ICONEXCLAMATION) ;
                        return FALSE ;
                    }
                    gwCapFileSize = (WORD) iCapFileSize ;

                    EndDialog(hDlg, TRUE) ;
                    return TRUE ;
                }

                case IDCANCEL :
                    EndDialog(hDlg, FALSE) ;
                    return TRUE ;

                case IDD_SetCapFileSize:
                {
                    long l;
                    BOOL bchanged;
                    TCHAR achBuffer[21];

                    // check that entered size is a valid number
                    GetDlgItemText(hDlg, IDD_SetCapFileSize, achBuffer, sizeof(achBuffer));
                    l = atol(achBuffer);
                    bchanged = FALSE;
                    if (l < 1) {
                        l = 1;
                        bchanged = TRUE;
                    } else if (l > nFreeMBs) {
                        l = nFreeMBs;
                        bchanged = TRUE;
                    } else {
                        // make sure there are no non-digit chars
                        // atol() will ignore trailing non-digit characters
                        int c = 0;
                        while (achBuffer[c]) {
                            if (IsCharAlpha(achBuffer[c]) ||
                                !IsCharAlphaNumeric(achBuffer[c])) {

                                // string contains non-digit chars - reset
                                l = 1;
                                bchanged = TRUE;
                                break;
                            }
                            c++;
                        }
                    }
                    if (bchanged) {
                        wsprintf(achBuffer, "%ld", l);
                        SetDlgItemText(hDlg, IDD_SetCapFileSize, achBuffer);
                    }
                    break;
                }
            }
            break ;
    }

    return FALSE ;

}

#if 0
//
// MakePaletteProc: Palette Details Dialog Box Procedure
//
BOOL CALLBACK MakePaletteProc(HWND hDlg, UINT Message, WPARAM wParam, LPARAM lParam)
{
    switch (Message) {
        case WM_INITDIALOG :
            SetDlgItemInt(hDlg, IDD_MakePalColors, gwPalColors, FALSE) ;
            SetDlgItemInt(hDlg, IDD_MakePalFrames, gwPalFrames, FALSE) ;
            return TRUE ;

        case WM_COMMAND :
            switch (GET_WM_COMMAND_ID(wParam, lParam)) {
                case IDOK :
                {
                    int         iColors ;
                    int         iFrames ;

                    iColors = (int) GetDlgItemInt(hDlg, IDD_MakePalColors, NULL, TRUE) ;
                    if (! (iColors > 0 && iColors <= 236 || iColors == 256)) {
                        // invalid number of palette colors
                        SetDlgItemInt(hDlg, IDD_MakePalColors, iColors, TRUE) ;
                        SetFocus(GetDlgItem(hDlg, IDD_MakePalColors)) ;
                        MessageBeep(MB_ICONEXCLAMATION) ;
                        return FALSE ;
                    }
                    iFrames = (int) GetDlgItemInt(hDlg, IDD_MakePalFrames, NULL, TRUE) ;
                    if (iFrames <= 0 || iFrames > 10000) {
                        // no frame or way t-o-o many frames !!!
                        SetDlgItemInt(hDlg, IDD_MakePalFrames, iFrames, TRUE) ;
                        SetFocus(GetDlgItem(hDlg, IDD_MakePalFrames)) ;
                        MessageBeep(MB_ICONEXCLAMATION) ;
                        return FALSE ;
                    }
                    gwPalColors = iColors ;
                    gwPalFrames = iFrames ;

                    EndDialog(hDlg, TRUE) ;
                    return TRUE ;
                }

                case IDCANCEL :
                    EndDialog(hDlg, FALSE) ;
                    return TRUE ;
            }
            break ;
    }

    return FALSE ;

}

#endif


#define CAPPAL_TIMER    902    
#define CAPTIMER_DELAY  100       // get timers as fast as possible
//
// MakePaletteProc: Palette Details Dialog Box Procedure
//
static int      siNumColors = 256;

LRESULT CALLBACK MakePaletteProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static UINT_PTR shTimer;
    static int  siNumFrames;
    UINT        w;
    TCHAR        ach[40];
    TCHAR        achFormat[40];
    int         i, k;

    switch(msg) {
        case WM_INITDIALOG:
            siNumFrames = 0;
            SetDlgItemInt(hwnd, IDD_MakePalColors, siNumColors, FALSE);
            SmartWindowPosition (hwnd, ghWndCap);
            return TRUE;
            break;

        case WM_VSCROLL:
            /* now handle the scroll */
            i = GetDlgItemInt(hwnd, IDD_MakePalColors, NULL, FALSE);
            ArrowEditChange(GetDlgItem(hwnd, IDD_MakePalColors),
                GET_WM_VSCROLL_CODE(wParam, lParam), 2, 256);
            k = GetDlgItemInt(hwnd, IDD_MakePalColors, NULL, FALSE);
            // Jump over the range 237 to 255
            if (k > 236 && k < 256) {
                if (k > i) 
                   w = 256;
                else
                   w = 236;
                SetDlgItemInt (hwnd, IDD_MakePalColors, w, TRUE);
            }
            break;

        case WM_COMMAND:
            switch (GET_WM_COMMAND_ID(wParam, lParam)) {
                case IDCANCEL:
                    if (siNumFrames) {
                        // The following finishes building the new palette
                        capPaletteManual (ghWndCap, FALSE, siNumColors);
                    }

                    if (shTimer){
                        KillTimer(hwnd, CAPPAL_TIMER);
                        shTimer = 0;
                    }
                    siNumColors = GetDlgItemInt(hwnd, IDD_MakePalColors, (BOOL FAR *)ach, FALSE);
                    siNumColors = max (2, min (256, siNumColors)); 
                    EndDialog(hwnd, siNumFrames);
                    break;
                    
                case IDD_MakePalStart:
                    /* see if we are in START or STOP mode at   */
                    /* this time and handle each one.           */
                    SetFocus (GetDlgItem (hwnd, IDD_MakePalStart));
                    if (!siNumFrames){
                        /* this is the first frame, change the CANCEL */
                        /* button to CLOSE                              */
                        LoadString(ghInstApp, IDS_CAPPAL_CLOSE, ach, sizeof(ach));
                        SetDlgItemText(hwnd, IDCANCEL, ach);
                    }
                    if (!shTimer) {

                        shTimer = SetTimer(hwnd, CAPPAL_TIMER, CAPTIMER_DELAY, NULL);

                        if (shTimer == 0) {
                            //!!!error message here.
                            MessageBeep(0);
                            return TRUE;
                        }

                        /* button said START, let's set up to   */
                        /* do continuous capture.  This involves*/
                        /*   1 - disabling FRAME button         */
                        /*   2 - turning myself to STOP button  */
                        /*   3 - setting up frame timer         */
                        EnableWindow(GetDlgItem(hwnd, IDD_MakePalSingleFrame), FALSE);
                        LoadString(ghInstApp, IDS_CAPPAL_STOP, ach, sizeof(ach));
                        SetDlgItemText(hwnd, IDD_MakePalStart, ach);
                    } else {
                        /* button said STOP, turn things around */
                        /* by:                                  */
                        /*   1 - killing off timers             *
                        /*   2 - turning back into START button */
                        /*   3 - re-enabling FRAME button       */
                        // "&Start"
                        LoadString(ghInstApp, IDS_CAPPAL_START, ach, sizeof(ach));
                        SetDlgItemText(hwnd, IDD_MakePalStart, ach);
                        EnableWindow(GetDlgItem(hwnd, IDD_MakePalSingleFrame), TRUE);
                        KillTimer(hwnd, CAPPAL_TIMER);
                        shTimer = 0;
                    }
                    return TRUE;
                    break;
                    
                case IDD_MakePalSingleFrame:
                    if (!siNumFrames){
                        /* this is the first frame, change the CANCEL */
                        /* button to CLOSE                              */
                        LoadString(ghInstApp, IDS_CAPPAL_CLOSE, ach, sizeof(ach));
                        SetDlgItemText(hwnd, IDCANCEL, ach);
                        siNumColors = GetDlgItemInt(hwnd, IDD_MakePalColors, (BOOL FAR *)ach, FALSE);
                        siNumColors = max (2, min (256, siNumColors)); 
                    }
                    // Get the palette for a single frame
                    capPaletteManual (ghWndCap, TRUE, siNumColors);

                    siNumFrames++;
                    LoadString(ghInstApp, IDS_CAPPAL_STATUS, achFormat, sizeof(achFormat));
                    wsprintf(ach, achFormat, siNumFrames);
                    SetDlgItemText(hwnd, IDD_MakePalNumFrames, ach);
                    return TRUE;
                    break;

                case IDD_MakePalColors:
                    if (HIWORD (lParam) == EN_KILLFOCUS) {
                        w = GetDlgItemInt (hwnd, (UINT) wParam, NULL, FALSE);
                        if ( w < 2) {
                            MessageBeep (0);
                            SetDlgItemInt (hwnd, (UINT) wParam, 2, FALSE);
                        }
                        else if (w > 256) {
                            MessageBeep (0);
                            SetDlgItemInt (hwnd, (UINT) wParam, 256, FALSE);
                        }
                    }
                    return TRUE;
                    break;

                default:
                    return FALSE;
                    
            } // switch(wParam) on WM_COMMAND 
            break;
            
        case WM_TIMER:
            if (wParam == CAPPAL_TIMER){
                SendMessage(hwnd, WM_COMMAND, IDD_MakePalSingleFrame, 0L);
            }
            break;
        default:
            return FALSE;
            
    } // switch(msg)
    return FALSE;
}




//
// CapSetUpProc: Capture SetUp Details Dialog Box Procedure
//
LRESULT FAR PASCAL CapSetUpProc(HWND hDlg, UINT Message, WPARAM wParam, LPARAM lParam)
{
    static TCHAR     achBuffer[21] ;
    UINT fValue;

    switch (Message) {
        case WM_INITDIALOG :
        {

            // Convert from MicroSecPerFrame to FPS -- that's easier !!
            MicroSecToStringRate(achBuffer, gCapParms.dwRequestMicroSecPerFrame);
            SetDlgItemText(hDlg, IDD_FrameRateData, achBuffer);


            // If time limit isn't enabled, disable the time data part
            CheckDlgButton(hDlg, IDD_TimeLimitFlag, (fValue = gCapParms.fLimitEnabled)) ;
            EnableWindow(GetDlgItem(hDlg, IDD_SecondsText), fValue) ;
            EnableWindow(GetDlgItem(hDlg, IDD_SecondsData), fValue) ;
            EnableWindow(GetDlgItem(hDlg, IDD_SecondsArrow), fValue);

            SetDlgItemInt(hDlg, IDD_SecondsData, gCapParms.wTimeLimit, FALSE) ;


            // disable audio buttons if no audio hardware
            {
                CAPSTATUS cs;

                capGetStatus(ghWndCap, &cs, sizeof(cs));
                EnableWindow(GetDlgItem(hDlg, IDD_CapAudioFlag), cs.fAudioHardware);
                EnableWindow(GetDlgItem(hDlg, IDD_AudioConfig), cs.fAudioHardware);

                CheckDlgButton(hDlg, IDD_CapAudioFlag, gCapParms.fCaptureAudio);
            }



            /*
             * Capture To Memory means allocate as many memory buffers
             *  as possible.
             * Capture To Disk means only allocate enough buffers
             *  to get us through disk seeks and thermal recalibrations.
             */

            // The use of fUsingDOSMemory is now just a means of keeping
            // track of whether using lots of buffers.  We never actually
            // allocate exclusively from memory under 1Meg.

            CheckRadioButton(hDlg, IDD_CaptureToDisk, IDD_CaptureToMemory,
              (gCapParms.fUsingDOSMemory)? IDD_CaptureToDisk : IDD_CaptureToMemory);

            // Find out how many MCI devices can source video
            if (CountMCIDevices(MCI_DEVTYPE_VCR) +
                CountMCIDevices(MCI_DEVTYPE_VIDEODISC) == 0) {
                // if no VCRs or Videodiscs, disable the controls
                EnableWindow(GetDlgItem(hDlg, IDD_MCIControlFlag), FALSE);
                EnableWindow(GetDlgItem(hDlg, IDD_MCISetup), FALSE);
            } else {
                EnableWindow(GetDlgItem(hDlg, IDD_MCIControlFlag), TRUE);

                // if MCI Control is selected, enable the setup button
                CheckDlgButton(hDlg, IDD_MCIControlFlag,
                    gCapParms.fMCIControl);
                EnableWindow(GetDlgItem(hDlg, IDD_MCISetup), gCapParms.fMCIControl);
            }

            // place the dialog to avoid covering the capture window
            SmartWindowPosition(hDlg, ghWndCap);
            return TRUE ;
        }

        case WM_COMMAND :
            switch (GET_WM_COMMAND_ID(wParam, lParam)) {
                case IDD_TimeLimitFlag :
                    // If this flag changes, en/dis-able time limit data part
                    fValue = IsDlgButtonChecked(hDlg, IDD_TimeLimitFlag) ;
                    EnableWindow(GetDlgItem(hDlg, IDD_SecondsText), fValue) ;
                    EnableWindow(GetDlgItem(hDlg, IDD_SecondsData), fValue) ;
                    EnableWindow(GetDlgItem(hDlg, IDD_SecondsArrow), fValue);
                    return TRUE ;

                case IDD_MCIControlFlag :
                    // If this flag changes, en/dis-able MCI Setup button
                    fValue = IsDlgButtonChecked(hDlg, IDD_MCIControlFlag) ;
                    EnableWindow(GetDlgItem(hDlg, IDD_MCISetup), fValue) ;
                    return TRUE ;

                case IDD_CapAudioFlag:
                    fValue = IsDlgButtonChecked(hDlg, IDD_CapAudioFlag) ;
                    EnableWindow(GetDlgItem(hDlg, IDD_AudioConfig), fValue) ;
                    return TRUE ;


                case IDD_FrameRateData:
                    // get the requested frame rate and check it against bounds
                    if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_KILLFOCUS) {
                        long l, new_l;

                        GetDlgItemText(hDlg, IDD_FrameRateData, achBuffer, sizeof(achBuffer));
                        new_l = l = StringRateToMicroSec(achBuffer);

                        // note that the MAX rate is SMALL! hence <max, >min
                        if (l == 0) {
                            new_l = DEF_CAPTURE_RATE;
                        } else if (l < MAX_CAPTURE_RATE) {
                            new_l = MAX_CAPTURE_RATE;
                        } else if (l > MIN_CAPTURE_RATE) {
                            new_l = MIN_CAPTURE_RATE;
                        }
                        if (l != new_l) {
                            MicroSecToStringRate(achBuffer, new_l);
                            SetDlgItemText(hDlg, IDD_FrameRateData, achBuffer);
                        }
                    }
                    break;

                case IDD_SecondsData:
                {
                    long l, new_l;

                    // get requested time limit and check validity
                    GetDlgItemText(hDlg, IDD_SecondsData, achBuffer, sizeof(achBuffer));
                    new_l = l = atol(achBuffer);
                    if (l < 1) {
                        new_l = 1;
                    } else if (l > 9999) {
                        new_l = 9999;
                    } else {
                        // make sure there are no non-digit chars
                        // atol() will ignore trailing non-digit characters
                        int c = 0;
                        while (achBuffer[c]) {
                            if (IsCharAlpha(achBuffer[c]) ||
                                !IsCharAlphaNumeric(achBuffer[c])) {

                                // string contains non-digit chars - reset
                                new_l = 1;
                                break;
                            }
                            c++;
                        }
                    }
                    if (new_l != l) {
                        wsprintf(achBuffer, "%ld", new_l);
                        SetDlgItemText(hDlg, IDD_SecondsData, achBuffer);
                        // select the changed text so that if you delete the
                        // '1' and then insert '10' you get 10 not 110
                        SendDlgItemMessage(hDlg, IDD_SecondsData,
                                EM_SETSEL, 0, -1);

                    }
                    break;
                }

                // show audio format setup dialog
                case IDD_AudioConfig:

                    // rather than duplicate lots of code from the
                    // main vidcap winproc, lets just ask it to show the dlg...
                    SendMessage(ghWndMain, WM_COMMAND,
                            GET_WM_COMMAND_MPS(IDM_O_AUDIOFORMAT, NULL, 0));

                    break;


                // show MCI step control dialog
                case IDD_MCISetup:
                    DoDialog(hDlg, IDD_MCISETUP, MCISetupProc, 0);
                    break;

                // show video format setup dialog
                case IDD_VideoConfig:
                    // rather than duplicate lots of code from the
                    // main vidcap winproc, lets just ask it to show the dlg...
                    SendMessage(ghWndMain, WM_COMMAND,
                            GET_WM_COMMAND_MPS(IDM_O_VIDEOFORMAT, NULL, 0));
                    break;

                // show the compressor selector dialog
                case IDD_CompConfig:
                    capDlgVideoCompression(ghWndCap);
                    break;



                case IDOK :
                {

                    gCapParms.fCaptureAudio =
                                IsDlgButtonChecked(hDlg, IDD_CapAudioFlag) ;
                    gCapParms.fMCIControl =
                            IsDlgButtonChecked(hDlg, IDD_MCIControlFlag);
                    gCapParms.fLimitEnabled = IsDlgButtonChecked(hDlg, IDD_TimeLimitFlag) ;

                    GetDlgItemText(hDlg, IDD_FrameRateData, achBuffer, sizeof(achBuffer));
                    gCapParms.dwRequestMicroSecPerFrame = StringRateToMicroSec(achBuffer);
                    if (gCapParms.dwRequestMicroSecPerFrame == 0) {
                        gCapParms.dwRequestMicroSecPerFrame = DEF_CAPTURE_RATE;
                    }

                    GetDlgItemText(hDlg, IDD_SecondsData, achBuffer, sizeof(achBuffer));
                    if (gCapParms.fLimitEnabled) {
                         gCapParms.wTimeLimit  = (UINT) atol(achBuffer);
                    }

                    // fUsingDOSMemory is archaic and is now just a flag reflecting
                    // the "CaptureToDisk" selection.
                    // 
                    gCapParms.fUsingDOSMemory = 
                                IsDlgButtonChecked(hDlg, IDD_CaptureToDisk);

                    EndDialog(hDlg, TRUE) ;
                    return TRUE ;
                }

                case IDCANCEL :
                    EndDialog(hDlg, FALSE) ;
                    return TRUE ;
            }
            break ;

        case WM_VSCROLL:
        // message from one of the arrow spinbuttons
        {
            UINT id;

            id = GetDlgCtrlID(GET_WM_COMMAND_HWND(wParam, lParam));
            if (id == IDD_FrameRateArrow) {
                // format n.nnn
                MilliSecVarArrowEditChange(
                    GetDlgItem(hDlg, IDD_FrameRateData),
                    GET_WM_VSCROLL_CODE(wParam, lParam),
                    1, 100, 1);
            } else {
                // simple integer format
                ArrowEditChange(
                    GetDlgItem(hDlg, IDD_SecondsData),
                    GET_WM_VSCROLL_CODE(wParam, lParam),
                    1, 30000);
            }
            break;
        }

    }

    return FALSE ;
}

/*
 * preferences dialog - sets global options about background colour,
 * presence of toolbar, status bar etc
 */
LRESULT FAR PASCAL
PrefsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    DWORD indexsz;

    switch(message) {


    case WM_INITDIALOG:
        CheckDlgButton(hDlg, IDD_PrefsStatus, gbStatusBar);
        CheckDlgButton(hDlg, IDD_PrefsToolbar, gbToolBar);
        CheckDlgButton(hDlg, IDD_PrefsCentre, gbCentre);
        CheckDlgButton(hDlg, IDD_PrefsSizeFrame, gbAutoSizeFrame);
        CheckRadioButton(hDlg, IDD_PrefsDefBackground, IDD_PrefsBlack, gBackColour);

        CheckRadioButton(hDlg, IDD_PrefsSmallIndex, IDD_PrefsBigIndex,
                    (gCapParms.dwIndexSize == CAP_LARGE_INDEX) ?
                    IDD_PrefsBigIndex : IDD_PrefsSmallIndex);

        CheckRadioButton(hDlg, IDD_PrefsMasterAudio, IDD_PrefsMasterNone,
                    gCapParms.AVStreamMaster + IDD_PrefsMasterAudio);

        return(TRUE);

    case WM_COMMAND:
        switch(GET_WM_COMMAND_ID(wParam, lParam)) {
        case IDCANCEL:
            EndDialog(hDlg, FALSE);
            return(TRUE);

        case IDOK:
            gbStatusBar = IsDlgButtonChecked(hDlg, IDD_PrefsStatus);
            gbToolBar = IsDlgButtonChecked(hDlg, IDD_PrefsToolbar);
            gbCentre = IsDlgButtonChecked(hDlg, IDD_PrefsCentre);
            gbAutoSizeFrame = IsDlgButtonChecked(hDlg, IDD_PrefsSizeFrame);

            if (IsDlgButtonChecked(hDlg, IDD_PrefsDefBackground)) {
                gBackColour = IDD_PrefsDefBackground;
            } else if (IsDlgButtonChecked(hDlg, IDD_PrefsLtGrey)) {
                gBackColour = IDD_PrefsLtGrey;
            } else if (IsDlgButtonChecked(hDlg, IDD_PrefsDkGrey)) {
                gBackColour = IDD_PrefsDkGrey;
            } else {
                gBackColour = IDD_PrefsBlack;
            }

            if (IsDlgButtonChecked(hDlg, IDD_PrefsSmallIndex)) {
                indexsz = CAP_SMALL_INDEX;

            } else {
                indexsz = CAP_LARGE_INDEX;
            }
            if (indexsz != gCapParms.dwIndexSize) {
                gCapParms.dwIndexSize = indexsz;
            }

            if (IsDlgButtonChecked(hDlg, IDD_PrefsMasterAudio)) {
                gCapParms.AVStreamMaster = AVSTREAMMASTER_AUDIO;
            }
            else {
                gCapParms.AVStreamMaster = AVSTREAMMASTER_NONE;
            }

            EndDialog(hDlg, TRUE);
            return(TRUE);
        }
        break;
    }
    return FALSE;
}


LRESULT FAR PASCAL
NoHardwareDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HBRUSH hbr;

    switch(message) {
    case WM_INITDIALOG:
        // lParam contains the argument to DialogBoxParam which is the
        // reason text
        SetDlgItemText(hDlg, IDD_FailReason, (LPTSTR) lParam);

        hbr = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
        return TRUE;

    case WM_DESTROY:
        DeleteObject(hbr);

#ifdef _WIN32
    case WM_CTLCOLORSTATIC:
#else
    case WM_CTLCOLOR:
#endif
        if (GET_WM_CTLCOLOR_HWND(wParam, lParam, message) == GetDlgItem(hDlg, IDD_FailReason)) {

            HDC hdc;

            hdc = GET_WM_CTLCOLOR_HDC(wParam, lParam, message);

            SetTextColor(hdc, RGB(0xff, 0, 0));
            SetBkColor(hdc, GetSysColor(COLOR_WINDOW));

            // in order to ensure that the text colour we have chosen for
            // this control is used, we need to actually return a brush.
            // for win31, we also need to align the brush
#ifndef _WIN32
            {
                POINT pt;

                pt.x = 0;
                pt.y = 0;
                ClientToScreen(hDlg, &pt);
                UnrealizeObject(hbr);
                SetBrushOrg(hdc, pt.x, pt.y);
            }
#endif

            return((INT_PTR) hbr);

        }
        break;

    case WM_COMMAND:
        switch(GET_WM_COMMAND_ID(wParam, lParam)) {
        case IDOK:
            EndDialog(hDlg, TRUE);
            return(TRUE);
        case IDCANCEL:
            EndDialog(hDlg, FALSE);
            return(TRUE);
        }
        break;
    }

    return(FALSE);
}


//capture selected single frames
LRESULT
FAR PASCAL
CapFramesProc(HWND hDlg, UINT Message, WPARAM wParam, LPARAM lParam)
{
    TCHAR ach[MAX_PATH*2];
    TCHAR achName[MAX_PATH];

    static BOOL bFirst;
    static int iFrames;

    switch(Message) {
    case WM_INITDIALOG:

        // write out the prompt message including the capture file name
        capFileGetCaptureFile(ghWndCap, achName, sizeof(achName));
        wsprintf(ach, tmpString(IDS_PROMPT_CAPFRAMES), achName);
        SetDlgItemText(hDlg, IDD_CapMessage, ach);

        bFirst = TRUE;

        //move dialog so it doesn't obscure the capture window
        SmartWindowPosition(hDlg, ghWndCap);

        return(TRUE);

    case WM_COMMAND:
        switch(GET_WM_COMMAND_ID(wParam, lParam)) {

        case IDCANCEL:
            if (!bFirst) {
                capCaptureSingleFrameClose(ghWndCap);
                EndDialog(hDlg, TRUE);
            } else {
                EndDialog(hDlg, FALSE);
            }
            return(TRUE);

        case IDOK:
            if (bFirst) {
                bFirst = FALSE;
                iFrames = 0;
                capCaptureSingleFrameOpen(ghWndCap);

                SetDlgItemText(hDlg, IDCANCEL, tmpString(IDS_CAP_CLOSE));

            }
            capCaptureSingleFrame(ghWndCap);
            iFrames++;

            wsprintf(ach, tmpString(IDS_STATUS_NUMFRAMES), iFrames);
            SetDlgItemText(hDlg, IDD_CapNumFrames, ach);
            return(TRUE);

        }
        break;
    }
    return(FALSE);
}

// enumerate all the MCI devices of a particular type and add them and
// their descriptions to a combo box list.
//
void
AddMCIDeviceNames(UINT wDeviceType, HWND hwndCB)
{
    int   nIndex;
    MCI_OPEN_PARMS mciOp;
    MCI_INFO_PARMS mciIp;
    MCI_SYSINFO_PARMS mciSIP;
    MCI_GENERIC_PARMS mciGp;
    TCHAR buf[MAXPNAMELEN + 128]; // Contains eg. Name\t\tVideodisc1
    TCHAR buf2 [64];
    int maxdevs;
    DWORD dwRet;

    // To get the user readable names of the devices, we
    // must open all appropriate devices, and then get info.

    // MCI Open structure
    mciOp.dwCallback = 0;
    mciOp.lpstrElementName = NULL;
    mciOp.lpstrAlias = NULL;

    // MCI Info structure
    mciIp.dwCallback = 0;
    mciIp.lpstrReturn = (LPTSTR) buf;
    mciIp.dwRetSize = MAXPNAMELEN - 1;

    // MCI SysInfo structure
    mciSIP.dwCallback = 0;
    mciSIP.lpstrReturn = (LPTSTR) buf2;
    mciSIP.dwRetSize = sizeof (buf2);
    mciSIP.wDeviceType = wDeviceType;

    // MCI Generic structure
    mciGp.dwCallback = 0;

    // Load the combobox with the product info name, followed by
    // a comma, then a space, and then the mci device name. This allows a
    // single alphabetized list to be kept.

    // eg.
    // Pioneer Laserdisc, videodisc1

    maxdevs = CountMCIDevices((UINT)mciSIP.wDeviceType);
    for (nIndex = 0; nIndex < maxdevs; nIndex++) {

       // Get the system name eg. Videodisc1
       mciSIP.dwNumber = nIndex + 1;
       dwRet = mciSendCommand (0, MCI_SYSINFO,
                    MCI_SYSINFO_NAME,
                    (DWORD_PTR) (LPVOID) &mciSIP);

       mciOp.lpstrDeviceType =
            (LPTSTR) MAKELONG (wDeviceType, nIndex);

       if (!(dwRet = mciSendCommand(0, MCI_OPEN,
                    MCI_WAIT | MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID |
                    MCI_OPEN_SHAREABLE,
                    (DWORD_PTR) (LPVOID) &mciOp))) {
            if (!(dwRet = mciSendCommand (mciOp.wDeviceID, MCI_INFO,
                            MCI_WAIT | MCI_INFO_PRODUCT,
                            (DWORD_PTR) (LPVOID) &mciIp))) {
                lstrcat (buf, ", ");         // append the delimiter
                lstrcat (buf, buf2);         // append the system name
                // Whew, finally put it in the listbox
                SendMessage( hwndCB, CB_ADDSTRING, 0,
                                (LONG_PTR)(LPTSTR) buf);
            } //endif got INFO
            // Close it now
            mciSendCommand (mciOp.wDeviceID, MCI_CLOSE,
                            MCI_WAIT,
                            (DWORD_PTR) (LPVOID) &mciGp);
       } // endif OPEN
    } // endif for all devices of this type
}


//
// dialog proc to select MCI device and parameters, including start,
// stop times.
LRESULT FAR PASCAL
MCISetupProc(HWND hwnd, unsigned msg, WPARAM wParam, LPARAM lParam)
{
  HWND  hwndCB;
  DWORD dw;
  TCHAR buf[MAXPNAMELEN];
  BOOL f;
  int j;
  static int nLastCBIndex = 0;
  static DWORD tdwMCIStartTime;
  static DWORD tdwMCIStopTime;



  switch (msg) {
    case WM_INITDIALOG:

      	CheckRadioButton(hwnd, IDD_MCI_PLAY, IDD_MCI_STEP,
			    gCapParms.fStepMCIDevice ?
                            IDD_MCI_STEP : IDD_MCI_PLAY );

        // enable averaging options only in step mode
        EnableWindow (GetDlgItem (hwnd, IDD_MCI_AVERAGE_2X), gCapParms.fStepMCIDevice);
        EnableWindow (GetDlgItem (hwnd, IDD_MCI_AVERAGE_FR), gCapParms.fStepMCIDevice);
	SetDlgItemInt(hwnd, IDD_MCI_AVERAGE_FR, gCapParms.wStepCaptureAverageFrames, FALSE);
        CheckDlgButton (hwnd, IDD_MCI_AVERAGE_2X, gCapParms.fStepCaptureAt2x);

        // save current dialog time settings
        tdwMCIStartTime = gCapParms.dwMCIStartTime;
        tdwMCIStopTime  = gCapParms.dwMCIStopTime;

        TimeMSToHMSString (gCapParms.dwMCIStartTime, buf);
        SetDlgItemText (hwnd, IDD_MCI_STARTTIME, buf);
        TimeMSToHMSString (gCapParms.dwMCIStopTime, buf);
        SetDlgItemText (hwnd, IDD_MCI_STOPTIME, buf);


        // fill combo box with list of MCI devices
	hwndCB = GetDlgItem( hwnd, IDD_MCI_SOURCE );
        AddMCIDeviceNames(MCI_DEVTYPE_VIDEODISC, hwndCB);
        AddMCIDeviceNames(MCI_DEVTYPE_VCR, hwndCB);


        // set the selection to whatever he chose last time through this dlg
        // default is the first entry.
       	SendMessage( hwndCB, CB_SETCURSEL, nLastCBIndex, 0L);
	break;

    case WM_COMMAND:
	switch (GET_WM_COMMAND_ID(wParam, lParam)) {
	    case IDOK:
                // i think the point of this is to ensure that
                // the KILLFOCUS processing for the edit boxes has been done
                // and thus the temp times are the same as the dialog text
                SetFocus(GET_WM_COMMAND_HWND(wParam, lParam));


                MCIGetDeviceNameAndIndex (hwnd, &nLastCBIndex, gachMCIDeviceName);
                capSetMCIDeviceName(ghWndCap, gachMCIDeviceName) ;
                gCapParms.fStepMCIDevice = IsDlgButtonChecked (hwnd, IDD_MCI_STEP);

                // pick up the temp times - these were set on KILLFOCUS msgs
                // (when we did validation and string->dword conversion
                gCapParms.dwMCIStartTime = tdwMCIStartTime;
                gCapParms.dwMCIStopTime  = tdwMCIStopTime;

                gCapParms.fStepCaptureAt2x = IsDlgButtonChecked (hwnd, IDD_MCI_AVERAGE_2X);
                gCapParms.wStepCaptureAverageFrames = GetDlgItemInt (hwnd, IDD_MCI_AVERAGE_FR, NULL, FALSE);

		EndDialog(hwnd, TRUE);
		break;
		
	    case IDCANCEL:
		EndDialog(hwnd, 0);
		break;

            case IDD_MCI_STEP:
            case IDD_MCI_PLAY:
                //averaging only enabled in play mode
                f = IsDlgButtonChecked (hwnd, IDD_MCI_STEP);
                EnableWindow (GetDlgItem (hwnd, IDD_MCI_AVERAGE_2X), f);
                EnableWindow (GetDlgItem (hwnd, IDD_MCI_AVERAGE_FR), f);
                break;

            case IDD_MCI_AVERAGE_FR:
                // validate the count of frames to average 1..100
                if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_KILLFOCUS) {
                    j = GetDlgItemInt(hwnd,
                            GET_WM_COMMAND_ID(wParam, lParam), NULL, FALSE);
                    // Limit frames to average between 1 and 100
                    if (j < 1 || j > 100) {
	                SetDlgItemInt (hwnd,
                            GET_WM_COMMAND_ID(wParam, lParam), 1, FALSE);
                    }
                }
                break;

            case IDD_MCI_STARTSET:
	    case IDD_MCI_STOPSET:
                // set the start or stop time to be the time
                // on the device right now.

                // MCI devices could yield and cause us to re-enter - the
                // simplest answer seems to be to disable the dialog
                EnableWindow(hwnd, FALSE);

                MCIGetDeviceNameAndIndex (hwnd, &nLastCBIndex, buf);

                if (MCIDeviceOpen (buf)) {
                    if (GET_WM_COMMAND_ID(wParam, lParam) == IDD_MCI_STARTSET) {
                        if (MCIDeviceGetPosition (&tdwMCIStartTime)) {
                           TimeMSToHMSString (tdwMCIStartTime, buf);
                           SetDlgItemText (hwnd, IDD_MCI_STARTTIME, buf);
                        }
                        else {
                            MessageBoxID(IDS_MCI_CONTROL_ERROR,
                                        MB_OK|MB_ICONEXCLAMATION);
                        }
                    }
                    else {
                        if (MCIDeviceGetPosition (&tdwMCIStopTime)) {
                            TimeMSToHMSString (tdwMCIStopTime, buf);
                            SetDlgItemText (hwnd, IDD_MCI_STOPTIME, buf);
                        }
                        else {
                            MessageBoxID(IDS_MCI_CONTROL_ERROR,
                                        MB_OK|MB_ICONEXCLAMATION);
                        }
                    }
                    MCIDeviceClose ();

                } else {
                    // cant open device
                    MessageBoxID(IDS_MCI_CONTROL_ERROR,
                                MB_OK|MB_ICONEXCLAMATION);
                }
                EnableWindow(hwnd, TRUE);
                break;


            case IDD_MCI_STARTTIME:
            case IDD_MCI_STOPTIME:
                if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_KILLFOCUS) {
                    GetDlgItemText (hwnd,
                        GET_WM_COMMAND_ID(wParam, lParam), buf, sizeof (buf));
                    if ((dw = TimeHMSStringToMS (buf)) == -1) {
                        // Error in string, reset
                        MessageBeep (0);
                        if (GET_WM_COMMAND_ID(wParam, lParam) == IDD_MCI_STARTTIME)
                            dw = tdwMCIStartTime;
                        else
                            dw = tdwMCIStopTime;
                    }
                    if (GET_WM_COMMAND_ID(wParam, lParam) == IDD_MCI_STARTTIME) {
                        tdwMCIStartTime = dw;
                        TimeMSToHMSString (tdwMCIStartTime, buf);
                        SetDlgItemText (hwnd, IDD_MCI_STARTTIME, buf);
                    }
                    else {
                        tdwMCIStopTime = dw;
                        TimeMSToHMSString (tdwMCIStopTime, buf);
                        SetDlgItemText (hwnd, IDD_MCI_STOPTIME, buf);
                    }
                }
                break;
	}
	break;

  }
  return FALSE;
}