/*

 * MIDI.C
 *
 * Copyright (C) 1990 Microsoft Corporation.
 *
 * The MIDI control panel applet.
 *
 * History:
 *
 *  t-mikemc    10-Apr-90 Created.
 */
/* Revision history:
   March 92 Ported to 16/32 common code by Laurie Griffiths (LaurieGr)
*/

/*-=-=-=-=- Include Files       -=-=-=-=-*/

#include        "preclude.h"
#include        <windows.h>
#include        <mmsystem.h>
#if defined(WIN32)
#include        <port1632.h>
#endif
#include "hack.h"
#include        "midimap.h"
#include        <cpl.h>
#include        "cphelp.h"
#include        "cparrow.h"
#include        "midi.h"

/*-=- poop -=-*/

#define MM_MAXRESLEN    32      // arbitrary maximum resource string length
/*-=-=-=-=- Prototypes          -=-=-=-=-*/

INT_PTR CALLBACK MainBox (HWND, UINT, WPARAM, LPARAM);

BOOL NEAR PASCAL LibMain(HINSTANCE,UINT,LPSTR);
#if defined(WIN16)
#pragma alloc_text(_INIT, LibMain)
#endif //WIN16

LRESULT FAR PASCAL _loadds CPlApplet(HWND,UINT,WPARAM,LPARAM);
#if defined(WIN16)
#pragma alloc_text(_INIT, CPlApplet)
#endif //WIN16

void NEAR PASCAL InitCPL(void);
#if defined(WIN16)
#pragma alloc_text(_INIT, InitCPL)
#endif //WIN16


/*-=-=-=-=- Global Variables    -=-=-=-=-*/

/* This many global variables is a sure sign of sickness.  LaurieGr */

HINSTANCE       hLibInst;               // Library instance handle.
HGLOBAL hKeyMap;                        // Midikeymap handle.
HFONT   hFont;                          // Dialog box font handle.
HWND    hWnd,                           // Current window handle.
        hCombo,                         // Combo box handle.
        hEdit,                          // Edit control handle.
        hArrow;                         // Arrow control handle.
RECT    rcBox;                          // Clipping/scroll rectangle.
int     rgxPos[8],                      // horizontal line positions.
        yBox,                           // rows of data y extent
        xClient,                        // Window client area x pixels.
        yClient,                        // Window client area y pixels.
        iCurPos,                        // Current position on screen.
        iVertPos,                       // Current vertical scroll position.
        iVertMax,                       // Maximum veritcal scroll position.
        nLines,                         // Number of lines of data.
        yChar,                          // Height of character in font.
        xChar,                          // Width of average character in font.
        iMap;                           // Current map type being edited.
char    szCurrent[MMAP_MAXNAME],        // Current map name.
        szCurDesc[MMAP_MAXDESC],        // Current map description.
        szCurSetup[MMAP_MAXNAME],       // Current setup
        szNone[16],                     // Generic global string.
        szMidiCtl[64],                  // Window caption for midicpl.
        aszSourceKey[32],
        aszSourceKeyName[40],
        aszPatchNumber[40],
        aszSourcePatch[32],
        aszSourcePatchName[40],
        aszSourceMnumonic[5],
        aszSourceChannel[32],
        aszActive[32],
        szMidiHlp[24];
BOOL    fModified,                      // Flag; Has the map been modified?
        fChanged,                       // Flag; Has anything ever been changed?
        fNew,                           // Flag; Is this a new map?
        fHidden;                        // Flag; Is the acitve line hidden?
BOOL    fReadOnly;
char    aszClose[16];

static  char aszCaptionFormat[24];
static  SZCODE szHelpMessage[] = "ShellHelp";
static  SZCODE aszTempPrefix[] = "mmr";         // limited to 3 characters
static  SZCODE aszFontName[] = "MS Sans Serif";
static  BOOL fAppletEntered;            // Disallow multiple applet instances
UINT near uHelpMessage;
DWORD near dwContext;

typedef struct {
        int idIcon;
        int idName;
        int idInfo;
        BOOL bEnabled;
        DWORD dwContext;
        PSTR pszHelp;
} APPLET_INFO;

#define NUM_APPLETS 1
APPLET_INFO near applets[NUM_APPLETS];


/* Move the window, if necessary, to keep it on the desktop, as far as possible */
VOID PlaceWindow(HWND hwnd)
{
    RECT rcWind;  /* window rectangle */
    HDC hdc;      /* so we can get device capabilities -i.e. screen size */
    int up;       /* amount to move window up */
    int left;     /* amount to move window left */
    int HorzRes;  /* horizontal screen resolution in pixels */
    int VertRes;  /* vertical screen resolution in lines */

     /* GetWindowRect(HWND_DESKTOP, &rcDesk) doesn't work! */
    hdc = GetDC(hwnd);
    HorzRes = GetDeviceCaps(hdc,HORZRES);
    VertRes = GetDeviceCaps(hdc,VERTRES);
    GetWindowRect(hwnd, &rcWind);
    up = rcWind.bottom - VertRes;       /* how much to move up to get onto screen... */
    if (up<0) up = 0;			/* don't bother to go down to get to the bottom */
    if (up>rcWind.top) up = rcWind.top;	/* don't ever go off the top */

    left = rcWind.right - HorzRes;       /* how much to move left to get all on screen... */
    if (left<0) left = 0;		 /* don't bother to go right */
    if (left>rcWind.left) left = rcWind.left; /* but don't ever go off the left */

    SetWindowPos( hwnd, HWND_TOP, rcWind.left-left, rcWind.top-up, 0,0,SWP_NOSIZE);

} /* PlaceWindow */


VOID NEAR PASCAL CancelToClose(HWND hDlg)
{
    if (!fReadOnly) {
        char    aszText[16];

        GetDlgItemText(hDlg, IDOK, aszText, sizeof(aszText));
        if (lstrcmp(aszText, aszClose))
            SetDlgItemText(hDlg, IDOK, aszClose);
    }
}

//      -       -       -       -       -       -       -       -       -

//      Windows entry point.

BOOL NEAR PASCAL LibMain(
        HINSTANCE       hInstance,
        UINT    uHeapSize,
        LPSTR   lpCmdLine)
{
        hLibInst = hInstance;
        return TRUE;
}


void NEAR PASCAL InitCPL(void)
{
        if ( applets[0].idIcon == 0 )
        {
                LoadString(hLibInst, IDS_CLOSE,  aszClose, sizeof(aszClose));
                applets[0].idIcon = ID_ICON;
                applets[0].idName = IDS_NAME;
                applets[0].idInfo = IDS_INFO;
                applets[0].bEnabled = TRUE;
                applets[0].dwContext = IDH_CHILD_MIDI;
                applets[0].pszHelp = szMidiHlp;
                LoadString(hLibInst, IDS_NONE, szNone, sizeof(szNone));
                LoadString(hLibInst, IDS_HELPFILE, szMidiHlp, sizeof(szMidiHlp));
                LoadString(hLibInst, IDS_SOURCEKEY, aszSourceKey, sizeof(aszSourceKey));
                LoadString(hLibInst, IDS_SOURCEKEYNAME, aszSourceKeyName, sizeof(aszSourceKeyName));
                LoadString(hLibInst, IDS_PATCHNUMBER, aszPatchNumber, sizeof(aszPatchNumber));
                LoadString(hLibInst, IDS_SOURCEPATCH, aszSourcePatch, sizeof(aszSourcePatch));
                LoadString(hLibInst, IDS_SOURCEPATCHNAME, aszSourcePatchName, sizeof(aszSourcePatchName));
                LoadString(hLibInst, IDS_SOURCEMNUMONIC, aszSourceMnumonic, sizeof(aszSourceMnumonic));
                LoadString(hLibInst, IDS_SOURCECHANNEL, aszSourceChannel, sizeof(aszSourceChannel));
                LoadString(hLibInst, IDS_ACTIVETITLE, aszActive, sizeof(aszActive));
        }
}

#ifdef STUPID
/*
 * ComboBox String Lookup, written because we
 * let an intern design this applet.
 */

int FAR PASCAL ComboLookup(
        HWND hCombo,
        LPSTR szLookup)
{
        int iEntries;
        static char szBuf[29];
        PSTR pstrBuf;

        iEntries = (int)(LONG)SendMessage(hCombo,CB_GETCOUNT,(WPARAM)0,(LPARAM)0);

        if (iEntries > CB_ERR)
        {
                do
                {
                        iEntries--;
                        pstrBuf = szBuf;
                        if ((int)(LONG)SendMessage(hCombo,CB_GETLBTEXT,(WPARAM)iEntries,(LPARAM)(LPSTR)szBuf) == CB_ERR)
                                return CB_ERR;

                        if (lstrcmpi(szLookup,pstrBuf) == 0)
                                return iEntries;
                }
                while(iEntries);
        }
        return CB_ERR;
}
#endif

//      Given an error code, display an error message.  If the error code
//      was invalid, displays a default bogus English-language text string.

VOID FAR PASCAL VShowError(
        HWND    hwnd,           // Window to tie the message box to.
        MMAPERR mmaperr)        // Error code to display text for.
{
        char    asz[256];

        LoadString(hLibInst, IDS_MMAPERR_BASE + mmaperr, asz, sizeof(asz));
        MessageBox(hwnd, asz, NULL, MB_ICONEXCLAMATION | MB_OK);
}

//      -       -       -       -       -       -       -       -       -

//      Returns TRUE if the file exists and is correct, or was initialized
//      properly.  Returns FALSE if the user didn't want it initialized, or
//      if the initialization failed.
//
//      This function deals with English-language specific stuff.  There is
//      no good reason for this that I can see.

static  BOOL FAR PASCAL FGetMapFile(
        HWND    hwnd)
{
        char    aszBuf[256];
        DWORD   dwVersion;
        MMAPERR mmaperr;

        //
        //      An error return from "mapFileVersion" indicates a problem that
        //      I don't want to go into too specifically at this time.  The
        //      most likely event is that the file doesn't exist.  The code in
        //      in this function assumes that this is what happened.  If it
        //      was another problem, hopefully initializing the file will
        //      solve it.  This can be looked into later.
        //
        dwVersion = mapFileVersion();
        if ((HIWORD(dwVersion) == MMAPERR_SUCCESS) &&
                (LOWORD(dwVersion) == 1))
                return TRUE;    // File was fine (opened + was right version).
        //
        //      This code is executing if there was a problem opening the
        //      file.  Hopefully this problem involved non-existence.
        //
        LoadString(hLibInst, IDS_CREATE_QUESTION, aszBuf, sizeof(aszBuf));
        if (MessageBox(hwnd, aszBuf, szMidiCtl,
                MB_YESNO | MB_ICONHAND) == IDYES)
                if ((mmaperr = mapInitMapFile()) == MMAPERR_SUCCESS)
                        return TRUE;
                else
                        VShowError(hwnd, mmaperr);

        return FALSE;
}

//      -       -       -       -       -       -       -       -       -

//      Control panel entry point.

LRESULT FAR PASCAL _loadds CPlApplet(
        HWND    hCPlWnd,
        UINT    uMessage,
        WPARAM  lParam1,
        LPARAM  lParam2)
{
        LPNEWCPLINFO    lpCPlInfo;
        BOOL    fSuccess = TRUE;
        BOOL    fBackup;
        LPSTR   lpstrBakPath;
        LPSTR   lpstrCfgPath;
        int     iApplet;

        switch (uMessage) {
        case CPL_INIT :
                InitCPL();
                uHelpMessage = RegisterWindowMessage(szHelpMessage);
                hWnd = hCPlWnd;
                return (LRESULT)TRUE;
        case CPL_GETCOUNT :
                return (LRESULT)NUM_APPLETS;
        case CPL_NEWINQUIRE :
                lpCPlInfo = (LPNEWCPLINFO)lParam2;
                iApplet = (int)(LONG)lParam1;
                lpCPlInfo->hIcon = LoadIcon(hLibInst,
                                          MAKEINTRESOURCE(applets[iApplet].idIcon));
                LoadString(hLibInst, applets[iApplet].idName, lpCPlInfo->szName, sizeof(lpCPlInfo->szName));
                LoadString(hLibInst, applets[iApplet].idInfo, lpCPlInfo->szInfo, sizeof(lpCPlInfo->szInfo));
                lpCPlInfo->dwSize = sizeof(NEWCPLINFO);
                lpCPlInfo->lData = (LONG)iApplet;
                lpCPlInfo->dwHelpContext = applets[iApplet].dwContext;
                lstrcpy(lpCPlInfo->szHelpFile, applets[iApplet].pszHelp);
                return (LRESULT)TRUE;
        case CPL_DBLCLK :
                if (fAppletEntered)
                        break;
                //
                // We can enter into lots of bizarre states:

                // We become READ-ONLY IF:
                //      We cannot lock the mapper.
                //      There is a disk or memory exception backing up the
                //              existing MIDIMAP.CFG file or the MIDIMAP.CFG
                //              is READ ONLY.
                // We DO NOT use a backup IF:
                //      The mapper is locked.
                //      No MIDIMAP.CFG file is found.
                //      There is an exception dealing with backing up the
                //              the existing MIDIMAP.CFG file.
                //

                lpstrCfgPath = GlobalLock(GlobalAlloc(GHND,MAXPATHLEN));
                if (lpstrCfgPath == NULL)
                {
                        VShowError(hCPlWnd,IDS_MMAPERR_MEMORY);
                        break;
                }

                lpstrBakPath = GlobalLock(GlobalAlloc(GHND,MAXPATHLEN));
                if (lpstrBakPath == NULL)
                {
                        VShowError(hCPlWnd,IDS_MMAPERR_MEMORY);
                        goto exitfree;
                }

                fAppletEntered = TRUE;
                if (!mapLock()) // Attempt to lock the mapper
                {
                        char szApp[32],
                             szMessage[256];

                        LoadString(hLibInst, IDS_READONLYMODE, szMessage, sizeof(szMessage));
                        LoadString(hLibInst, IDS_TITLE, szApp, sizeof(szApp));

                        MessageBox(hCPlWnd, szMessage, szApp,
                                MB_ICONINFORMATION | MB_OK);

                        fBackup = FALSE;        // No backups
                        fReadOnly = TRUE;       // Read Only Mode
                }
                else
                {
                        fBackup = TRUE;         // Try to do a backup
                        fReadOnly = FALSE;      // Not in READ ONLY mode
                }

                if (fBackup)
                {
                        char    szMapCfg[MMAP_MAXCFGNAME];

                        LoadString(hLibInst,IDS_MIDIMAPCFG,szMapCfg,MMAP_MAXCFGNAME);
                        GetSystemDirectory(lpstrCfgPath,MAXPATHLEN - sizeof(szMapCfg));
                        lstrcat(lpstrCfgPath,szMapCfg);

                        MGetTempFileName(0, aszTempPrefix,0,lpstrBakPath);

                        if (DupMapCfg(lpstrCfgPath,lpstrBakPath)) {
                                mapConnect(lpstrBakPath);

                        }
                        else
                        {
                                // No Backups.
                                fBackup = FALSE;
                                // if fReadOnly == TRUE, the exception
                                //    has forced us into READ-ONLY mode
                                // else
                                //    we didn't have a MIDIMAP.CFG file
                                //    and we'll read/write directly.
                                if (fReadOnly)
                                        mapUnlock();

                                DosDelete(lpstrBakPath);  // GetTempFileName Creates a File of size 0
                        }
                }

                if (!FGetMapFile(hCPlWnd))
                {
                        fSuccess = FALSE;
                        goto appexit;
                }
                // !!! Never unregisters the class, so this will come back
                //  FALSE on subsequent startups.  This is of course bogus.
                (VOID)RegisterArrowClass(hLibInst);
#if 0
                if ((hFont = CreateFont(8, NULL, NULL, NULL,
                        FW_NORMAL, NULL, NULL, NULL,
                        ANSI_CHARSET, OUT_DEFAULT_PRECIS,
                        CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
                        VARIABLE_PITCH | FF_DONTCARE, aszFontName)) == NULL)
                        goto appexit;
#else
                {
                        LOGFONT lf;

                        SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), (LPVOID)&lf, 0);
                        if (!(hFont = CreateFontIndirect(&lf)))
                                goto appexit;
                }
#endif

                fSuccess = (BOOL)DialogBox(hLibInst, MAKEINTRESOURCE(ID_MAINBOX),
                        hCPlWnd, MainBox);
                UnregisterArrowClass(hLibInst);
                DeleteObject(hFont);

appexit:
                if (fBackup)
                {
                        MDOUT("Disconnecting from mapper");
                        mapDisconnect();
                        if (fSuccess)
                        {
                                MDOUT("Updating Backup");
                                UpdateMapCfg(lpstrCfgPath,lpstrBakPath);
                        }
                        mapUnlock();
                        DosDelete(lpstrBakPath);
                }
                else
                if (!fReadOnly)
                {
                        mapUnlock();
                        if (!fSuccess)
                                DosDelete(lpstrCfgPath);
                }

                fAppletEntered = FALSE;
                GlobalFree((HGLOBAL)lpstrBakPath);
exitfree:       GlobalFree((HGLOBAL)lpstrCfgPath);

                break;
        }
        return (LRESULT)0;
}

//      -       -       -       -       -       -       -       -       -

//      This will return either "MMAPERR_SUCCESS" if everything is fine,
//      or will return "MMAPERR_INVALIDPORT" if the setup references a
//      bogus port.  These are both expected conditions.
//
//      Unexpected conditions can also happen, which are returned.

static  MMAPERR NEAR PASCAL MmaperrInvalidPortCheck(
        LPSTR   lszName)                // Name of current setup.
{
        LPMIDIMAP lpMap;
        MMAPERR mmaperr;
        DWORD   dwSize;
        HGLOBAL hMidiMap;

        if ((dwSize = mapGetSize(MMAP_SETUP, lszName)) < MMAPERR_MAXERROR)
                return (MMAPERR)dwSize;
        if ((hMidiMap = GlobalAlloc(GHND, dwSize)) == NULL)
                return MMAPERR_MEMORY;
        lpMap = (LPMIDIMAP)GlobalLock(hMidiMap);
        mmaperr = mapRead(MMAP_SETUP, lszName, (LPVOID)lpMap);
        GlobalUnlock(hMidiMap);
        GlobalFree(hMidiMap);
        return mmaperr;
}

//      -       -       -       -       -       -       -       -       -

//      Converts a "MMAP_" into a "IDS_", i.e. converts "MMAP_SETUP" into
//      "IDS_SETUP", which can be used to get the word "Setup" out of the
//      "midi.rc" file.

static  int PASCAL IdsGetMapNameId( int iMap)
{
        if (iMap == MMAP_SETUP)
                return IDS_SETUP;
        if (iMap == MMAP_PATCH)
                return IDS_PATCH;
        return IDS_KEY;
}

//      -       -       -       -       -       -       -       -       -

/*
 * DELETEMAP
 *
 * This function returns TRUE if it is OK to delete whatever map
 * exists in szCurrent.
 */

static  BOOL NEAR PASCAL FConfirmDeleteMap(
        HWND    hwnd)
{
        char    szSetup[MMAP_MAXNAME],
                szSrc[MM_MAXRESLEN],
                szUsedBy[MM_MAXRESLEN],
                szDel[50],
                szCap[50],
                szRsrc[256],
                szBuf[256];
        DWORD   dwRet;
        UINT    uSrcID;
        UINT    uUsage;
        MMAPERR mmaperr;


        uSrcID = IdsGetMapNameId(iMap);
        LoadString(hLibInst, uSrcID, szSrc, sizeof(szSrc));
        LoadString(hLibInst, IDS_DELETE, szDel, sizeof(szDel));
        wsprintf(szCap, szDel, (LPSTR)szSrc);
//      AnsiUpperBuff(szSrc, 1);        // Upper-case first character.
        switch (iMap) {
        case MMAP_SETUP:
                if ((mmaperr = mapGetCurrentSetup(szSetup,
                        MMAP_MAXNAME)) != MMAPERR_SUCCESS) {
                        VShowError(hwnd, mmaperr);
                        return FALSE;
                }
                if (lstrcmpi(szSetup, szCurrent))
                        break;
                LoadString(hLibInst, IDS_NODELISCURRENT, szRsrc, sizeof(szRsrc));
                wsprintf(szBuf, szRsrc, (LPSTR)szCurrent);
                MessageBox(hwnd, szBuf, szCap,
                        MB_ICONINFORMATION | MB_OK);
                return FALSE;
        case MMAP_PATCH:
        case MMAP_KEY:
                dwRet = mapGetUsageCount(iMap, szCurrent);
                if (LOWORD(dwRet) != MMAPERR_SUCCESS) {
                        VShowError(hwnd, LOWORD(dwRet));
                        return FALSE;
                }
                if (!(uUsage = HIWORD(dwRet)))
                        break;
                uSrcID--;               // Patch->Setup, Key->Patch
                if (uUsage > 1)         // Make a singular a plural.  This
                        uSrcID += 3;    //  "3" is commented in "midi.h".
                LoadString(hLibInst, uSrcID, szUsedBy, sizeof(szUsedBy));
//              AnsiLowerBuff(szUsedBy, 1);     // Lower-case first character.
                LoadString(hLibInst, IDS_NODELISREFERENCED, szRsrc, sizeof(szRsrc));
                wsprintf(szBuf, szRsrc, (LPSTR)szSrc, (LPSTR)szCurrent,
                        uUsage, (LPSTR)szUsedBy);
                MessageBox (hwnd, szBuf, szCap,
                        MB_ICONINFORMATION | MB_OK);
                return FALSE;
        }
        LoadString(hLibInst, IDS_VERIFYDELETE, szRsrc, sizeof(szRsrc));
        wsprintf(szBuf, szRsrc, (LPSTR)szSrc, (LPSTR)szCurrent);
        return (IDYES == MessageBox (hwnd, szBuf, szCap,
                MB_ICONEXCLAMATION | MB_YESNO));
}

static VOID NEAR PASCAL VFreeItemData(
        HWND    hdlg,
        int     idCtrl)
{
        UINT    uCount;

        uCount = (UINT)SendDlgItemMessage(hdlg, idCtrl, CB_GETCOUNT, (WPARAM)NULL, (LPARAM)0);
        for (; uCount--; ) {
                HGLOBAL hDescription;

                if ((hDescription = (HGLOBAL)(DWORD)SendDlgItemMessage(hdlg, idCtrl,
                        CB_GETITEMDATA, (WPARAM)uCount, (LPARAM)0)) != NULL)
                        GlobalFree(hDescription);
        }
}

static  VOID NEAR PASCAL GetMBData(
        UINT    uFlag,
        LPMBDATA        lpmbData)
{
        switch (uFlag) {
        case MMAP_SETUP:
                lpmbData->lpfnBox = SetupBox;
                lpmbData->idBox = DLG_SETUPEDIT;
                break;
        case MMAP_PATCH:
                lpmbData->lpfnBox = PatchBox;
                lpmbData->idBox = DLG_PATCHEDIT;
                break;
        case MMAP_KEY:
                lpmbData->lpfnBox = KeyBox;
                lpmbData->idBox = DLG_KEYEDIT;
                break;
        default:
                lpmbData->lpfnBox = 0L;
                lpmbData->idBox = 0;
                break;
        }
} /* GetMBData */

static  BOOL PASCAL FEditMap(
        HWND    hwnd)
{
        HWND    hTmpWnd;
        MBDATA  mbData;
        DWORD   dwRet;
        int     iRet;
        BOOL    fInSetup;
        char    szSetup[MMAP_MAXNAME];
        MMAPERR mmaperr;

        if ((mmaperr = mapGetCurrentSetup(szSetup,
                MMAP_MAXNAME)) != MMAPERR_SUCCESS) {
                VShowError(hwnd, mmaperr);
                return FALSE;
        }
        switch (iMap) {
        case MMAP_SETUP :
                fInSetup = (BOOL)!lstrcmpi(szCurrent, szSetup);
                break;
        case MMAP_PATCH :
                dwRet = mapPatchMapInSetup(szCurrent, szSetup);
                if (LOWORD(dwRet) != MMAPERR_SUCCESS)
                        return FALSE;
                fInSetup = (BOOL)HIWORD(dwRet);
                break;
        case MMAP_KEY :
                dwRet = mapKeyMapInSetup (szCurrent, szSetup);
                if (LOWORD(dwRet) != MMAPERR_SUCCESS)
                        return FALSE;
                fInSetup = (BOOL)HIWORD(dwRet);
                break;
        }
        hTmpWnd = hWnd;         // "hWnd", not "hwnd".
        GetMBData(iMap, &mbData);
        iRet = (int)DialogBox(hLibInst, MAKEINTRESOURCE(mbData.idBox),
                hWnd, mbData.lpfnBox);
        hWnd = hTmpWnd;
        return iRet;
} /* FEditMap */

static  VOID NEAR PASCAL EnableMain(
        BOOL    fEnable)
{
        char    aszNoEntries[48];

        EnableWindow(hCombo, fEnable);
        if (fReadOnly)
        {
                EnableWindow(GetDlgItem (hWnd, ID_MAINDELETE), FALSE);
                EnableWindow(GetDlgItem (hWnd,ID_MAINNEW),FALSE);
        }
        else
                EnableWindow(GetDlgItem (hWnd, ID_MAINDELETE), fEnable);
        EnableWindow(GetDlgItem (hWnd, ID_MAINEDIT), fEnable);
        EnableWindow(GetDlgItem (hWnd, ID_MAINDESC), fEnable);
        EnableWindow(GetDlgItem (hWnd, ID_MAINNAME), fEnable);
        if (!fEnable) {
                LoadString(hLibInst, IDS_NOENTRIES, aszNoEntries, sizeof(aszNoEntries));
                SetDlgItemText(hWnd, ID_MAINDESC, aszNoEntries);
        }

} /* EnableMain */

static VOID NEAR PASCAL ShowMaps (int nMap)
{
        if (iMap == nMap)
                return;
        iMap = nMap;
        SendMessage(hWnd, WM_MY_INITDIALOG, (WPARAM)NULL, (LPARAM)0);
} /* ShowMaps */

INT_PTR CALLBACK MainBox(
        HWND    hdlg,
        UINT    uMessage,
        WPARAM  wParam,
        LPARAM  lParam)
{
        static BOOL     fChange,                // has edittext changed?
                        fPatchEnum,             // have patches been enum'd?
                        fKeyEnum,               // have keys been enum'd?
                        fEnabled;
        static UINT     uDeleted;               // which maps types deleted
        static char     aszInitialSetup[MMAP_MAXNAME]; // initial setup name
        MMAPERR mmaperr;
        HGLOBAL hDesc;
        LPSTR   lpDesc;
        UINT    uIdx;
        int     idCurCombo;
        BOOL    fEnum;
//      char    szBuf[128];
        char    szTmpDesc[MMAP_MAXDESC];

        switch (uMessage) {
        case WM_INITDIALOG:
                // get the current setup name and store in
                // static aszInitialSetup
                fChanged = FALSE;
                if ((mmaperr = mapGetCurrentSetup(aszInitialSetup,
                        MMAP_MAXNAME)) != MMAPERR_SUCCESS) {
exit00:                 VShowError(hdlg, mmaperr);
/*exit01:*/             EndDialog (hdlg, FALSE);
                        return TRUE;
                }
                lstrcpy(szCurSetup, aszInitialSetup);
                // Load caption string and set the window text
                LoadString(hLibInst, IDS_TITLE, szMidiCtl, sizeof(szMidiCtl));
                hWnd = hdlg;
                SetWindowText(hdlg, szMidiCtl);
/*!!
                // check for invalid devices
                if ((mmaperr = MmaperrInvalidPortCheck(
                        aszInitialSetup)) == MMAPERR_INVALIDPORT) {
                        if (!InvalidPortMsgBox(hdlg))
                                goto exit01;
                }
                else if (mmaperr != MMAPERR_SUCCESS)
                        goto exit00;
!!*/
                // hide the patch and key comboboxes
                ShowWindow(GetDlgItem(hdlg, ID_MAINPATCHCOMBO), SW_HIDE);
                ShowWindow(GetDlgItem(hdlg, ID_MAINKEYCOMBO), SW_HIDE);
                // enumerate setups into setup combo box
                mmaperr = mapEnumerate( MMAP_SETUP
                                      , EnumFunc
                                      , MMENUM_INTOCOMBO
                                      , GetDlgItem( hdlg, ID_MAINSETUPCOMBO)
                                      , NULL
                                      );
                if ( mmaperr != MMAPERR_SUCCESS )
                        goto exit00;
                // set up the auto-radiobuttons
                CheckRadioButton(hdlg, ID_MAINFIRSTRADIO,
                        ID_MAINLASTRADIO, ID_MAINSETUP);
                // intialize some variables
                *szCurrent = 0;
                hCombo = NULL;
                iMap = MMAP_SETUP;
                fEnabled = FALSE;       // Fix for bug #2039.  13-Feb-90, BLM.
                                        //  It used to set the variable TRUE.
                fChange = FALSE;
                fPatchEnum = FALSE;
                fKeyEnum = FALSE;
                uDeleted = 0;
                // fall through
        case WM_MY_INITDIALOG:
                fEnum = FALSE;
                switch (iMap) {
                case MMAP_SETUP:
                        idCurCombo = ID_MAINSETUPCOMBO;
                        uIdx = ComboLookup(GetDlgItem(hdlg,idCurCombo),(LPSTR)szCurSetup);//-jyg
                        break;
                case MMAP_PATCH:
                        idCurCombo = ID_MAINPATCHCOMBO;
                        if (!fPatchEnum) {
                                fEnum = TRUE;
                                fPatchEnum = TRUE;
                        }
                        uIdx = 0;
                        break;
                case MMAP_KEY:
                        idCurCombo = ID_MAINKEYCOMBO;
                        if (!fKeyEnum) {
                                fEnum = TRUE;
                                fKeyEnum = TRUE;
                        }
                        uIdx = 0;
                        break;
                default:uIdx = 0;
                        idCurCombo = 0; /* kill compiler warning about use before set */
                }
                // hide the old combobox, if any
                if (hCombo != NULL)
                        ShowWindow (hCombo, SW_HIDE);
                // get the new combobox handle
                hCombo = GetDlgItem (hdlg, idCurCombo);
                // show the new combobox
                ShowWindow(hCombo, SW_SHOW);
                // if not done already, enumerate maps into the box
                if (fEnum)
                {       mmaperr = mapEnumerate(iMap, EnumFunc, MMENUM_INTOCOMBO, hCombo, NULL);
                        if (mmaperr != MMAPERR_SUCCESS)
                                goto exit00;
                }
                // set the current selection
                uIdx = (UINT)SendMessage(hCombo, CB_SETCURSEL, (WPARAM)uIdx, (LPARAM)0);
                // if no maps of that type, disable all necessary controls
                if (uIdx == CB_ERR) {
                        *szCurrent = 0;
                        EnableMain(fEnabled = FALSE);
                        break;
                }
                // if we were disabled, enable us
                if (!fEnabled)
                        EnableMain(fEnabled = TRUE);
                // fill the edit control with description
#if defined(WIN16)
                SendMessage(hdlg, WM_COMMAND, (WPARAM)ID_MAINCOMBO,
                        MAKELPARAM(hCombo, CBN_SELCHANGE));
#else
                SendMessage( hdlg
                           , WM_COMMAND
                           , (WPARAM)MAKELONG(ID_MAINCOMBO, CBN_SELCHANGE)
                           , (LPARAM)hCombo
                           );

#endif  // WIN16
                break;
        case WM_COMMAND:
            {   WORD wNotifCode;
#if defined(WIN16)
                wNotifCode = HIWORD(lParam);
#else
                wNotifCode = HIWORD(wParam);
#endif //WIN16

                switch (LOWORD(wParam)) {

                case  IDH_CHILD_MIDI:
                        goto DoHelp;

                case IDOK:
                        if (fReadOnly || !fChanged)
                        {
                                PostMessage(hdlg,WM_COMMAND,(WPARAM)IDCANCEL,(LPARAM)0);
                                break;
                        }
                        // check for invalid ports
                        if ((mmaperr = MmaperrInvalidPortCheck(
                                szCurSetup)) == MMAPERR_INVALIDPORT) {
                                if (!InvalidPortMsgBox(hdlg))
                                        break;
                        } else if (mmaperr != MMAPERR_SUCCESS)
                                goto exit00;
                        if (lstrcmpi(szCurSetup, aszInitialSetup))
                                {
                                        mmaperr = mapSetCurrentSetup(
                                                szCurSetup);
                                        if (mmaperr !=
                                                MMAPERR_SUCCESS)
                                                goto exit00;
                                }


                        // this is where any deleted maps actually get the axe
                        //
                        //      I'm going to leave this as is until I figure
                        //      out how to deal with any errors that happen.
                        //      brucemo
                        //
#if 0
                        if (uDeleted & MMAP_SETUP)
                                mapEnumerate( iMap = MMAP_SETUP
                                            , EnumFunc
                                            , MMENUM_DELETE
                                            , GetDlgItem(hdlg,ID_MAINSETUPCOMBO)
                                            , NULL
                                            );
                        if (uDeleted & MMAP_PATCH)
                                mapEnumerate( iMap = MMAP_PATCH
                                            , EnumFunc
                                            , MMENUM_DELETE
                                            , GetDlgItem(hdlg,ID_MAINPATCHCOMBO)
                                            , NULL
                                            );
                        if (uDeleted & MMAP_KEY)
                                mapEnumerate( iMap = MMAP_KEY
                                            , EnumFunc,
                                            , MMENUM_DELETE
                                            , GetDlgItem (hdlg,ID_MAINKEYCOMBO)
                                            , NULL
                                            );
#endif

                case IDCANCEL:
                        // clean up and go home
                        VFreeItemData(hdlg, ID_MAINSETUPCOMBO);
                        if (fPatchEnum)
                                VFreeItemData(hdlg, ID_MAINPATCHCOMBO);
                        if (fKeyEnum)
                                VFreeItemData(hdlg, ID_MAINKEYCOMBO);
                        if (LOWORD(wParam) == IDOK)                    // eh?
                                EndDialog(hdlg,TRUE);
                        else
                                EndDialog(hdlg,FALSE);
                        break;
                case ID_MAINDELETE:
                        if (!FConfirmDeleteMap(hdlg))
                                break;
                        CancelToClose(hdlg);
                        // set a bit in the deleted word
                        uDeleted |= iMap;
                        // get the current selections index
                        uIdx = (UINT)SendMessage(hCombo,
                                CB_GETCURSEL, (WPARAM)0, (LPARAM)0);
                        // get the handle to selections description
                        if ((hDesc = (HGLOBAL)(DWORD)SendMessage(hCombo,
                                CB_GETITEMDATA, (WPARAM)uIdx, (LPARAM)0)) != NULL)
                                GlobalFree(hDesc);
                        // delete the entry from the combobox
                        SendMessage(hCombo, CB_DELETESTRING, (WPARAM)uIdx, (LPARAM)0);

                        // -jyg-

                        mapEnumerate(iMap, EnumFunc, MMENUM_DELETE, hCombo, NULL);

                        // reset to initial setup or first entry
                        if (iMap == MMAP_SETUP)
                        {
                                uIdx = ComboLookup(hCombo,(LPSTR)aszInitialSetup); //-jyg
                                lstrcpy(szCurSetup,aszInitialSetup); // reset current string to initial setup
                        }
                        else
                                uIdx = 0;

                        uIdx = (UINT)SendMessage(hCombo, CB_SETCURSEL, (WPARAM)uIdx, (LPARAM)0);
                        // if deleted last one then disable window,
                        // otherwise update the edit control
                        if (uIdx == CB_ERR) {
                                EnableMain(fEnabled = FALSE);
                                SendMessage(hdlg, DM_SETDEFID, (WPARAM)ID_MAINNEW, (LPARAM)0);
                                SetFocus(GetDlgItem(hdlg, ID_MAINNEW));

                        } else
#if defined(WIN16)
                        SendMessage(hdlg, WM_COMMAND,
                                (WPARAM)ID_MAINCOMBO, MAKELPARAM(hCombo,
                                CBN_SELCHANGE));
#else
                        SendMessage( hdlg
                                   , WM_COMMAND
                                   , (WPARAM)MAKELONG(ID_MAINCOMBO, CBN_SELCHANGE)
                                   , (LPARAM)hCombo
                                   );
#endif //WIN16
                        break;
                case ID_MAINNEW:
                        // if they don't specify a new map get outta here
                        fNew = TRUE;
                        if (!DialogBox(hLibInst, MAKEINTRESOURCE(ID_PROPBOX),
                                hdlg, (DLGPROC)PropBox)) {
                                fNew = FALSE;
                                break;
                        }
                        // if they don't want to save new map, restore
                        // name and description and get outta here
                        if (!FEditMap(hdlg)) {
                                fNew = FALSE;
                                GetWindowText(hCombo, (LPSTR)szCurrent,
                                        MMAP_MAXNAME);
                                GetDlgItemText(hdlg, ID_MAINDESC, szCurDesc,
                                        MMAP_MAXDESC);
                                break;
                        }
                        uIdx = ComboLookup(hCombo,(LPSTR)szCurrent);//-jyg
                        if (uIdx != CB_ERR) {
                                char    aszName[MMAP_MAXNAME];
                                SendMessage(hCombo, CB_GETLBTEXT, (WPARAM)uIdx, (LPARAM)(LPSTR)aszName);
                                if (!lstrcmpi(aszName, szCurrent)) {
                                        hDesc = (HGLOBAL)(DWORD)SendMessage(hCombo, CB_GETITEMDATA, (WPARAM)uIdx, (LPARAM)0);
                                        if (hDesc != NULL)
                                                GlobalFree(hDesc);
                                        //break;
                                } else
                                        uIdx = (UINT)CB_ERR;
                        }
                        if (uIdx == CB_ERR)
                                uIdx = (UINT)SendMessage(hCombo, CB_ADDSTRING,
                                        (WPARAM)0, (LPARAM)(LPCSTR)szCurrent);
                        // make the new map the current one
                        SendMessage(hCombo, CB_SETCURSEL, (WPARAM)uIdx, (LPARAM)0);
                        if (IsDlgButtonChecked(hdlg, ID_MAINSETUP))
                                lstrcpy(szCurSetup, szCurrent);
                        // allocate buffer for map description data
                        if ((hDesc = GlobalAlloc(GHND, (DWORD)(sizeof(char) *
                                (lstrlen (szCurDesc) + 1)))) != NULL) {
                                lpDesc = (LPSTR)GlobalLock(hDesc);
                                lstrcpy(lpDesc, szCurDesc);
                                GlobalUnlock(hDesc);
                        }
                        SetDlgItemText (hdlg, ID_MAINDESC, szCurDesc);
                        // put the handle in the entrys item data
                        SendMessage(hCombo, CB_SETITEMDATA,
                                (WPARAM)uIdx, (LPARAM)hDesc);
                        // enable windows if it is the first map of type
                        if (!IsWindowEnabled(hCombo))
                                EnableMain(TRUE);
                        CancelToClose(hdlg);
                        break;
                case ID_MAINEDIT:
                        if (!FEditMap(hdlg))
                                break;
                        CancelToClose(hdlg);
                        if (iMap != MMAP_SETUP)
                                break;
                        // reset to current setup
                        uIdx = ComboLookup(hCombo,(LPSTR)szCurSetup); //-jyg
                        SendMessage(hCombo, CB_SETCURSEL, (WPARAM)uIdx, (LPARAM)0);

#if defined(WIN16)
                        SendMessage(hdlg, WM_COMMAND, (WPARAM)ID_MAINCOMBO,
                                MAKELPARAM(hCombo, CBN_SELCHANGE));
#else
                        SendMessage( hdlg
                                   , WM_COMMAND
                                   , (WPARAM)MAKELONG(ID_MAINCOMBO, CBN_SELCHANGE)
                                   , (LPARAM)hCombo
                                   );
#endif //WIN16
                        break;
                case ID_MAINSETUP:
                        ShowMaps(MMAP_SETUP);
                        break;
                case ID_MAINPATCH:
                        ShowMaps(MMAP_PATCH);
                        break;
                case ID_MAINKEY:
                        ShowMaps(MMAP_KEY);
                        break;
                case ID_MAINSETUPCOMBO:
                case ID_MAINPATCHCOMBO:
                case ID_MAINKEYCOMBO:
                case ID_MAINCOMBO:
                        if (wNotifCode == CBN_SELENDOK) {
                                CancelToClose(hdlg);
                                fChanged = TRUE;
                        }
                        if (wNotifCode != CBN_SELCHANGE)
                                return FALSE;
                        uIdx = (UINT)SendMessage(hCombo, CB_GETCURSEL,
                                (WPARAM)0, (LPARAM)0);
                        if (LOWORD(wParam) == ID_MAINSETUPCOMBO)
                                SendMessage(hCombo, CB_GETLBTEXT, (WPARAM)uIdx,
                                        (LPARAM) (LPSTR) szCurSetup);
                        hDesc = (HGLOBAL)(DWORD)SendMessage(hCombo, CB_GETITEMDATA,
                                (WPARAM)uIdx, (LPARAM)0);
                        lpDesc = GlobalLock(hDesc);
                        lstrcpy(szCurDesc, lpDesc);
                        GlobalUnlock(hDesc);
                        SetDlgItemText (hdlg, ID_MAINDESC, szCurDesc);
                        SendMessage(hCombo, CB_GETLBTEXT, (WPARAM)uIdx,
                                (LPARAM)(LPSTR)szCurrent);
                        break;
                case ID_MAINDESC:
                        // NOTE:  this is not implemented.
                        if (wNotifCode == EN_CHANGE)
                                fChange = TRUE;
                        else if ((wNotifCode == EN_KILLFOCUS) && fChange) {
                                GetDlgItemText (hdlg, ID_MAINDESC, szTmpDesc,
                                        MMAP_MAXDESC);
                                if (!lstrcmpi(szCurDesc, szTmpDesc)) {
                                        // description change logic here.
                                        // There's no API to changedesc!!
                                }
                        }
                        break;
                default:
                        return FALSE;
                }
            }  /* end of WM_COMMAND */
                break;
        default:
                if (uMessage == uHelpMessage) {
DoHelp:
                        WinHelp(hWnd, szMidiHlp, HELP_CONTEXT,
                                                        IDH_CHILD_MIDI);
                        return TRUE;
                }
                else
                        return FALSE;
        break;
        }
        return TRUE;
} /* MainBox */

BOOL    FAR PASCAL InvalidPortMsgBox (
        HWND    hwnd)
{
        int     iRet;
        char    szBuf[256];

        LoadString(hLibInst, IDS_INVALIDPORT, szBuf, sizeof(szBuf));
        iRet = MessageBox (hwnd, szBuf, szMidiCtl,
                MB_ICONSTOP | MB_YESNO);
        return iRet == IDYES;
} /* InvalidPortMsgBox */

VOID FAR PASCAL Modify(
        BOOL    fSet)
{
        fModified = fSet;
        if (fModified)
                fChanged = TRUE;
} /* Modify */

/*
 * ENUMFUNC
 *
 * Enumerate setup, patchmap or keymap names.
 */
BOOL FAR PASCAL _loadds EnumFunc(
        LPSTR   lpName,
        LPSTR   lpDesc,
        UINT    uCase,
        HWND    hCombo,
        LPSTR   unused
        )
{
        HGLOBAL hMem;
        LPSTR   lpStr;
        UINT    uIdx;
        MMAPERR mmaperr;

        // see if we're dealing with 'delete' enumeration.
        if (uCase == MMENUM_DELETE) {
                uIdx = ComboLookup(hCombo, (LPSTR)lpName);//-jyg

                if (uIdx != CB_ERR)
                        return TRUE;
                mmaperr = mapDelete(iMap, lpName);
                if (mmaperr == MMAPERR_SUCCESS)
                        return TRUE;
                VShowError(hWnd, mmaperr);
                return FALSE;
        }
        uIdx = (UINT)SendMessage(hCombo, CB_ADDSTRING, (WPARAM)NULL, (LPARAM)lpName);
        // see if we're dealing with enumeration from the main dialog
        if (uCase == MMENUM_INTOCOMBO) {
                hMem = GlobalAlloc(GHND, (DWORD)(lstrlen(lpDesc) + 1));
                if (hMem != NULL) {
                        lpStr = (LPSTR)GlobalLock(hMem);
                        lstrcpy(lpStr, lpDesc);
                        GlobalUnlock(hMem);
                }
                SendMessage( hCombo,
                        CB_SETITEMDATA, (WPARAM)uIdx, (LPARAM)hMem);
        }
        return TRUE;
 } /* EnumFunc */

#if 0
/*
 * SETWINDOWCAPTION
 *
 * Set the caption of a 'window', such as 'MIDI Setup: "foo"', even though
 * these are actually dialog boxes.
 */
VOID FAR PASCAL SetWindowCaption(
        VOID)
{
        char    szCaption[80],
                szName[MM_MAXRESLEN];

        LoadString(hLibInst, IdsGetMapNameId(iMap), szName, sizeof(szName));
        wsprintf(szCaption, aszCaptionFormat,
                (LPSTR)szName, (LPSTR)szCurrent);
        SetWindowText(hWnd, szCaption);
} /* SetWindowCaption */
#endif //0

int FAR PASCAL QuerySave (VOID)
{
        char    szBuf[256];
        char    aszSave[64];
        char    aszFormat[128];
        char    szFunc[MM_MAXRESLEN];

        LoadString(hLibInst, IdsGetMapNameId(iMap), szFunc, sizeof(szFunc));
        if (fNew) {
//              AnsiUpperBuff(szFunc, 1);       // Upper-case first character.
                LoadString(hLibInst, IDS_NEW_QUESTION, aszFormat, sizeof(aszFormat));
                wsprintf (szBuf, aszFormat, (LPSTR)szCurrent, (LPSTR)szFunc);
        }
    else {
                LoadString(hLibInst, IDS_CHANGE_QUESTION, aszFormat, sizeof(aszFormat));
                wsprintf (szBuf, aszFormat, (LPSTR)szFunc, (LPSTR)szCurrent);
    }
        LoadString(hLibInst, IDS_SAVE_CHANGES, aszSave, sizeof(aszSave));
        return MessageBox(hWnd, szBuf, aszSave, MB_YESNOCANCEL | MB_ICONEXCLAMATION);
} /* QuerySave */
