/*==========================================================================*/ // // class.c // // Copyright (C) 1993-1994 Microsoft Corporation. All Rights Reserved. // Mod Log: Modified by Shawn Brown (10/95) // - Ported to NT (Unicode, etc.) /*==========================================================================*/ #include "mmcpl.h" #include #include #include #include #define NOSTATUSBAR #include #include #include #include #include #include #include "utils.h" #include "midi.h" #include "mmdebug.h" #if defined DEBUG || defined DEBUG_RETAIL extern TCHAR szNestLevel[]; #endif #include "medhelp.h" #ifndef TVIS_ALL #define TVIS_ALL 0xFF7F #endif static CONST TCHAR cszIdfWildcard[] = TEXT ("*.idf"); static CONST TCHAR cszIdf[] = TEXT (".idf"); static CONST TCHAR cszSetupKey[] = REGSTR_PATH_SETUP REGSTR_KEY_SETUP; static CONST TCHAR cszMachineDir[] = REGSTR_VAL_WINDIR; static CONST TCHAR cszConfigDir[] = TEXT ("config\\"); extern CONST TCHAR cszMidiSlash[]; extern CONST TCHAR cszFriendlyName[]; extern CONST TCHAR cszDescription[]; extern CONST TCHAR cszSlashInstruments[]; extern CONST TCHAR cszExternal[]; extern CONST TCHAR cszDefinition[]; extern CONST TCHAR cszPort[]; extern CONST TCHAR cszDriversRoot[]; extern CONST TCHAR cszSchemeRoot[]; extern CONST TCHAR cszMidiMapRoot[]; extern CONST TCHAR cszDriversRoot[]; extern CONST TCHAR csz02d[]; extern CONST TCHAR cszSlash[]; extern CONST TCHAR cszEmpty[]; extern int lstrnicmp (LPTSTR pszA, LPTSTR pszB, size_t cch); typedef struct _midi_class { LPPROPSHEETPAGE ppsp; HKEY hkMidi; BOOL bDetails; BOOL bRemote; // device connected via midi cable UINT bChanges; UINT ixDevice; // registry enum index of driver key BYTE nPort; BYTE bFill[3]; BOOL bFillingList; #ifdef USE_IDF_ICONS HIMAGELIST hIDFImageList; #endif LPTSTR pszKey; TCHAR szFullKey[MAX_PATH]; TCHAR szAlias[MAX_PATH]; TCHAR szFile[MAX_PATH*2]; } MCLASS, FAR * PMCLASS; #define MCL_ALIAS_CHANGED 1 #define MCL_TREE_CHANGED 2 #define MCL_IDF_CHANGED 4 #define MCL_PORT_CHANGED 8 /*+ * Determines if a given string has a given prefix and if * the next character in the string is a given charater. * * if so, it returns a pointer to the first character in the * string after the prefix. * * this is useful for parsing off the file in file * or parts of registry paths. * * note that we do NOT consider a string to be a prefix of itself. * psz MUST be longer than than pszPrefix or this function returns NULL. * *-=================================================================*/ STATICFN LPTSTR WINAPI IsPrefix ( LPTSTR pszPrefix, LPTSTR psz, TCHAR chTerm) { UINT cb = lstrlen(pszPrefix); UINT cb2 = lstrlen(psz); TCHAR ch; if (cb2 < cb) return NULL; ch = psz[cb]; if (ch != chTerm) return NULL; psz[cb] = 0; if (lstrcmpi(pszPrefix, psz)) { psz[cb] = ch; return NULL; } psz[cb] = ch; return psz + cb; } /*+ IsFullPath * * returns true if the filename passed in is a fully qualified * pathname. returns false if it is a relative path * * unc paths are treated as fully qualified always * *-=================================================================*/ BOOL IsFullPath ( LPTSTR pszFile) { // fully qualified paths either begin with a backslash // or with a drive letter, colon, then backslash // if ((pszFile[0] == TEXT('\\')) || (pszFile[1] == TEXT(':') && pszFile[2] == TEXT('\\'))) return TRUE; return FALSE; } /*+ GetIDFDirectory * *-=================================================================*/ BOOL GetIDFDirectory ( LPTSTR pszDir, UINT cchDir) { HKEY hKey; UINT cbSize; *pszDir = 0; #if(_WIN32_WINNT >= 0x0400) if (!GetSystemDirectory (pszDir, cchDir)) return FALSE; #else if (!RegOpenKey (HKEY_LOCAL_MACHINE, cszSetupKey, &hKey)) { cbSize = cchDir * sizeof(TCHAR); RegQueryValueEx (hKey, cszMachineDir, NULL, NULL, (LPBYTE)pszDir, &cbSize); RegCloseKey (hKey); cchDir = cbSize/sizeof(TCHAR); if (!cchDir--) return FALSE; } else if (!GetWindowsDirectory (pszDir, cchDir)) return FALSE; #endif cchDir = lstrlen (pszDir); if (pszDir[cchDir -1] != TEXT('\\')) pszDir[cchDir++] = TEXT('\\'); lstrcpy (pszDir + cchDir, cszConfigDir); #ifdef DEBUG AuxDebugEx (4, DEBUGLINE TEXT("IDFDir='%s'\r\n"), pszDir); #endif return TRUE; } /*+ GetIDFFileName * *-=================================================================*/ BOOL GetIDFFileName ( HWND hWnd, LPTSTR lpszFile, UINT cchFile) { OPENFILENAME ofn; TCHAR szFilter[MAX_PATH]; UINT cch; assert (hWnd); // load filter string from resource and convert '#' characters // into NULLs // LoadString (ghInstance, IDS_IDFFILES, szFilter, NUMELMS(szFilter)); cch = lstrlen(szFilter); assert2 (cch, TEXT ("IDFFILES resource is empty!")); while (cch--) { if (TEXT('#') == szFilter[cch]) szFilter[cch] = 0; } ZeroMemory (&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = hWnd; ofn.hInstance = ghInstance; ofn.lpstrFilter = szFilter; ofn.nFilterIndex = 1; ofn.lpstrFile = lpszFile; ofn.nMaxFile = cchFile; ofn.Flags = OFN_HIDEREADONLY | OFN_NOCHANGEDIR; ofn.lpstrDefExt = cszIdf; return GetOpenFileName (&ofn); } /*+ InstallNewIDF * *-=================================================================*/ BOOL WINAPI InstallNewIDF ( HWND hWnd) { TCHAR szWinPath[MAX_PATH]; TCHAR szNewIDF[MAX_PATH]; UINT cch; UINT oBasename; // prompt for an IDF file // szNewIDF[0] = 0; if ( ! GetIDFFileName (hWnd, szNewIDF, NUMELMS(szNewIDF))) return FALSE; // set oBasename to pointer to the first character of the // basename of the new idf file // oBasename = lstrlen (szNewIDF); if (!oBasename) return FALSE; while (oBasename && (TEXT('\\') != szNewIDF[oBasename-1])) --oBasename; // build the new filename from windows directory and idf basename // GetIDFDirectory (szWinPath, NUMELMS(szWinPath)); cch = lstrlen (szWinPath); if (cch && szWinPath[cch-1] != TEXT('\\')) szWinPath[cch++] = TEXT('\\'); lstrcpyn (szWinPath + cch, szNewIDF + oBasename, NUMELMS(szWinPath)-cch); oBasename = cch; #ifdef DEBUG AuxDebugEx (5, DEBUGLINE TEXT("install IDF to '%s'\r\n"), szWinPath); #endif // now force .idf as the extension for new file // for (cch = lstrlen (szWinPath); cch && szWinPath[cch] != TEXT('.'); --cch) if (TEXT('\\') == szWinPath[cch]) { cch = lstrlen(szWinPath); break; } lstrcpy (szWinPath + cch, cszIdf); // quit now if we are trying to copy a file to itself // if (IsSzEqual(szWinPath, szNewIDF)) return FALSE; // copy the file, but fail if destination already exists // #ifdef DEBUG AuxDebugEx (5, DEBUGLINE TEXT("Copying %s to %s\r\n"), szNewIDF, szWinPath); #endif if (CopyFile (szNewIDF, szWinPath, TRUE)) return TRUE; // // if copy fails, query to overwrite because destination // already exists. // else { TCHAR szQuery[255]; TCHAR sz[255]; #ifdef DEBUG AuxDebugEx (1, DEBUGLINE TEXT ("InstallIDF -CopyFile failed w/ %d\r\n"), GetLastError()); #endif LoadString (ghInstance, IDS_QUERY_OVERIDF, sz, NUMELMS(sz)); wsprintf (szQuery, sz, szWinPath + oBasename); LoadString (ghInstance, IDS_IDF_CAPTION, sz, NUMELMS(sz)); if (MessageBox (hWnd, szQuery, sz, MB_YESNO | MB_ICONQUESTION) == IDYES) return CopyFile (szNewIDF, szWinPath, FALSE); } return FALSE; } /*+ * * FEATURE: Please remove the #ifdef UNICODE sections * when mmioOpen gets UNICODE enabled !!! *-=================================================================*/ typedef BOOL (WINAPI * FNIDFENUM)(LPVOID pvArg, UINT nEnum, LPIDFHEADER pHdr, LPIDFINSTINFO pInst); UINT WINAPI idfEnumInstruments ( LPTSTR lpszFile, FNIDFENUM fnEnum, LPVOID lpvArg) { MMCKINFO chkIDFX; // Grandparent chunk MMCKINFO chkMMAP; // Parent chunk HMMIO hmmio; // Handle to the file. UINT nInstruments; #ifdef DEBUG AuxDebugEx (3, DEBUGLINE TEXT("idfEnumInstruments('%s',%08X,%08X)\r\n"), lpszFile, fnEnum, lpvArg); #endif // Open the file for reading. hmmio = mmioOpen(lpszFile, NULL, MMIO_READ); if ( ! hmmio) { // What were they thinking?? You can't assert this. // assert3(0, TEXT("Cant open IDF file %s"), lpszFile ? lpszFile : TEXT("")); return 0; } // the whole IDF instrument stuff is wrapped in an 'IDF ' RIFF chunk // chkIDFX.fccType = MAKEFOURCC('I','D','F',' '); if (mmioDescend(hmmio, &chkIDFX, NULL, MMIO_FINDRIFF)) { #ifdef DEBUG AuxDebugEx (0, DEBUGLINE TEXT ("idfEnum: '%s' is not a valid IDF File\r\n"), lpszFile); #endif mmioClose(hmmio, 0); return 0; } // Count the number of instruments by counting // the number of "MMAP"'s in the file. // nInstruments = 0; chkMMAP.fccType = MAKEFOURCC('M','M','A','P'); while ( ! mmioDescend(hmmio, &chkMMAP, &chkIDFX, MMIO_FINDLIST)) { union { IDFHEADER idf; TCHAR sz[MAX_ALIAS + sizeof(IDFHEADER)]; } hdr; union { IDFINSTINFO iii; BYTE ab[MAX_ALIAS * 8 + sizeof(IDFINSTINFO)]; } inst; MMCKINFO chk; DWORD cb; #ifdef DEBUG AuxDebugEx (15, DEBUGLINE TEXT ("MMAP[%d] id=%08X siz=%08x\r\n"), nInstruments, chkMMAP.ckid, chkMMAP.cksize); #endif // read the hdr chunk // chk.ckid = MAKEFOURCC('h','d','r',' '); if (mmioDescend(hmmio, &chk, &chkMMAP, MMIO_FINDCHUNK)) break; #ifdef DEBUG AuxDebugEx (15, DEBUGLINE TEXT(" hdr.id=%08X hdr.siz=%08x\r\n"), chk.ckid, chk.cksize); #endif assert (chk.cksize > 0 && chk.cksize < 0x0080000); //AuxDebugDump (6, &chk, sizeof(chk)); cb = min(chk.cksize, sizeof(hdr)); if ((DWORD)mmioRead (hmmio, (LPVOID)&hdr, cb) != cb) break; //AuxDebugDump (6, &chk, sizeof(chk)); hdr.sz[NUMELMS(hdr.sz)-1] = 0; mmioAscend (hmmio, &chk, 0); #ifdef DEBUG AuxDebugEx (15, DEBUGLINE TEXT("hdr = '%s'\r\n"), hdr.idf.abInstID); #endif //AuxDebugDump (6, &chk, sizeof(chk)); // read the inst chunk and locate the product name // field. // chk.ckid = MAKEFOURCC('i','n','s','t'); if (mmioDescend(hmmio, &chk, &chkMMAP, MMIO_FINDCHUNK)) { #ifdef DEBUG AuxDebug (TEXT ("mmioDescend failed for 'inst' chunk")); #endif } #ifdef DEBUG AuxDebugEx (15, DEBUGLINE TEXT(" inst.id=%08X inst.siz=%08x\r\n"), chk.ckid, chk.cksize); #endif assert (chk.cksize > 0 && chk.cksize < 0x0080000); cb = min(chk.cksize, sizeof(inst)); if ((DWORD)mmioRead (hmmio, (LPVOID)&inst, cb) != cb) { #ifdef DEBUG AuxDebug ( TEXT ("mmioRead failed for 'inst' chunk")); #endif } inst.ab[NUMELMS(inst.ab)-1] = 0; mmioAscend (hmmio, &chk, 0); #ifdef DEBUG AuxDebugEx (15, DEBUGLINE TEXT ("inst.mfg = '%s'\r\n"), inst.iii.abData); AuxDebugEx (15, TEXT ("\t.prod = '%s'\r\n"), inst.iii.abData + inst.iii.cbManufactASCII + inst.iii.cbManufactUNICODE); #endif // call the enum callback for this instrument // if ( ! fnEnum (lpvArg, nInstruments, &hdr.idf, &inst.iii)) break; ++nInstruments; assert (nInstruments < 20); // ascend and loop back to look for the next instrument // if (mmioAscend(hmmio, &chkMMAP, 0)) break; } mmioClose(hmmio, 0); return nInstruments; } /*+ LoadTypesIntoTree * *-=================================================================*/ struct types_enum_data { HANDLE hWndT; TV_INSERTSTRUCT * pti; LPTSTR pszInstr; HTREEITEM htiSel; }; STATICFN BOOL WINAPI fnTypesEnum ( LPVOID lpv, UINT nEnum, LPIDFHEADER pHdr, LPIDFINSTINFO pInst) { struct types_enum_data * pted = lpv; HTREEITEM hti; assert (pted); #ifdef DEBUG AuxDebugEx (7, DEBUGLINE TEXT ("enum[%d] '%s' instr=%x\r\n"), nEnum, pHdr->abInstID, pted->pszInstr); #endif MultiByteToWideChar(GetACP(), 0, pHdr->abInstID, -1, pted->pti->item.pszText, sizeof(pted->pti->item.pszText)); hti = TreeView_InsertItem (pted->hWndT, pted->pti); // this item is the 'selected' one, if it is the first // item or if it matches the name // if ((nEnum == 0) || (pted->pszInstr && pted->pszInstr[0] && IsPrefix(pted->pti->item.pszText, pted->pszInstr + sizeof(TCHAR), TEXT('>')))) { pted->htiSel = hti; #ifdef DEBUG AuxDebugEx (7, DEBUGLINE TEXT("\t'%s' hti %08X is select\r\n"), pted->pszInstr ? pted->pszInstr : TEXT (""), hti); #endif } // return true to continue enumeration // return TRUE; } STATICFN void SetTypesEdit ( HWND hWnd, UINT uId, PMCLASS pmcl) { SetDlgItemText (hWnd, uId, pmcl->szFile); } STATICFN void LoadTypesIntoTree ( HWND hWnd, UINT uId, PMCLASS pmcl) { HWND hWndT; UINT cchBase; TCHAR szPath[MAX_PATH]; TCHAR szDefaultIDF[MAX_PATH]; int ix; WIN32_FIND_DATA ffd; HANDLE hFind; #ifdef USE_IDF_ICONS HIMAGELIST hImageList; #endif HTREEITEM htiSelect = NULL; // item to select hWndT = GetDlgItem (hWnd, uId); if (!hWndT) return; #ifdef DEBUG AuxDebugEx (5, DEBUGLINE TEXT ("LoadTypesIntoTree( ,%x, )\r\n"), uId); #endif LoadString (ghInstance, IDS_GENERAL, szDefaultIDF, NUMELMS(szDefaultIDF)); #ifdef USE_IDF_ICONS // if we have not already loaded an image list for the IDF types // do so now. // if (!(hImageList = pmcl->hIDFImageList)) { static LPCTSTR aid[] = { MAKEINTRESOURCE(IDI_IDFICON), MAKEINTRESOURCE(IDI_BLANK), }; int cx = GetSystemMetrics(SM_CXSMICON); int cy = GetSystemMetrics(SM_CYSMICON); DWORD dwLayout; UINT uFlags = ILC_MASK | ILC_COLOR32; if (GetProcessDefaultLayout(&dwLayout) && (dwLayout & LAYOUT_RTL)) { uFlags |= ILC_MIRROR; } pmcl->hIDFImageList = hImageList = ImageList_Create (cx, cy, uFlags, NUMELMS(aid), 2); if (hImageList) { UINT ii; for (ii = 0; ii < NUMELMS(aid); ++ii) { HICON hIcon = LoadImage (ghInstance, aid[ii], IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR); if (hIcon) ImageList_AddIcon (hImageList, hIcon); } } } #endif pmcl->bFillingList = TRUE; //SetWindowRedraw (hWndT, FALSE); #ifdef DEBUG AuxDebugEx (6, DEBUGLINE TEXT ("tv_deleteAllItems(%08X)\r\n"), hWndT); #endif TreeView_DeleteAllItems(hWndT); #ifdef DEBUG AuxDebugEx (6, DEBUGLINE TEXT ("tv_deleteAllItems(%08X) ends\r\n"), hWndT); #endif #ifdef USE_IDF_ICONS TreeView_SetImageList (hWndT, hImageList, TVSIL_NORMAL); #endif htiSelect = NULL; pmcl->bFillingList = FALSE; GetIDFDirectory (szPath, NUMELMS(szPath)); cchBase = lstrlen (szPath); if (cchBase && szPath[cchBase-1] != TEXT('\\')) szPath[cchBase++] = TEXT('\\'); lstrcpyn (szPath + cchBase, cszIdfWildcard, NUMELMS(szPath)-cchBase); #ifdef DEBUG AuxDebugEx (3, DEBUGLINE TEXT ("scanning for idfs at '%s'\r\n"), szPath); #endif ix = 0; hFind = FindFirstFile (szPath, &ffd); if (hFind != INVALID_HANDLE_VALUE) { TV_INSERTSTRUCT ti; struct types_enum_data ted = {hWndT, &ti, NULL, NULL}; ZeroMemory (&ti, sizeof(ti)); do { UINT nInstr; UINT cch; // patch off the extension before we add // this name to the list // cch = lstrlen(ffd.cFileName); while (cch) if (ffd.cFileName[--cch] == TEXT('.')) { ffd.cFileName[cch] = 0; break; } ti.hParent = TVI_ROOT; ti.hInsertAfter = TVI_SORT; #ifdef USE_IDF_ICONS ti.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; #else ti.item.mask = TVIF_TEXT | TVIF_STATE; #endif // the TV_ITEM structure may not be unicode enabled ?!? ti.item.pszText = ffd.cFileName; ti.item.state = 0; ti.item.stateMask = TVIS_ALL; #ifdef DEBUG AuxDebugEx (7, DEBUGLINE TEXT ("adding '%s' to types tree\r\n"), ti.item.pszText); #endif ti.hParent = TreeView_InsertItem (hWndT, &ti); if ( ! ti.hParent) break; ti.hInsertAfter = TVI_LAST; // put the extension back // if (cch > 0) ffd.cFileName[cch] = TEXT('.'); // check to see if this file is a match for the // current definition file. // #ifdef DEBUG AuxDebugEx (7, DEBUGLINE TEXT ("comparing '%s' with '%s'\r\n"), ffd.cFileName, pmcl->szFile); #endif ted.pszInstr = IsPrefix (ffd.cFileName, pmcl->szFile, TEXT('<')); #ifdef DEBUG AuxDebugEx (7, DEBUGLINE TEXT ("\tpszInstr = '%s'\r\n"), ted.pszInstr ? ted.pszInstr : TEXT ("NULL")); #endif // add instruments as subkeys to this file // this also has the side effect of setting ted.htiSel // when the instrument name matches // lstrcpy (szPath + cchBase, ffd.cFileName); nInstr = idfEnumInstruments (szPath, fnTypesEnum, &ted); // if this idf has no instruments. ignore it. // if it has more than one instrument, expand the list // so that instruments are visible // if (0 == nInstr) TreeView_DeleteItem (hWndT, ti.hParent); else if (nInstr > 1) TreeView_Expand (hWndT, ti.hParent, TVE_EXPAND); else ted.htiSel = ti.hParent; // if we have a match on filename, then we need to select // either the parent or one of the children // if (ted.pszInstr || IsSzEqual(ffd.cFileName,pmcl->szFile) || IsSzEqual(ffd.cFileName,szDefaultIDF)) { #ifdef DEBUG AuxDebugEx (7, DEBUGLINE TEXT ("will be selecting %08X '%s'\r\n"), ted.htiSel, ffd.cFileName); #endif htiSelect = ted.htiSel; } } while (FindNextFile (hFind, &ffd)); FindClose (hFind); } if (htiSelect) { pmcl->bFillingList = TRUE; #ifdef DEBUG AuxDebugEx (7, DEBUGLINE TEXT ("selecting %08X\r\n"), htiSelect); #endif TreeView_SelectItem (hWndT, htiSelect); #ifdef DEBUG AuxDebugEx (7, DEBUGLINE TEXT ("FirstVisible %08X\r\n"), htiSelect); #endif TreeView_SelectSetFirstVisible (hWndT, htiSelect); pmcl->bFillingList = FALSE; } #ifdef DEBUG AuxDebugEx (5, DEBUGLINE TEXT ("LoadTypesIntoTree( ,%d, ) ends\r\n"), uId); #endif //SetWindowRedraw (hWndT, TRUE); } /*+ * *-=================================================================*/ STATICFN void WINAPI HandleTypesSelChange ( PMCLASS pmcl, LPNMHDR lpnm) { LPNM_TREEVIEW pntv = (LPVOID)lpnm; LPTV_ITEM pti = &pntv->itemNew; HTREEITEM htiParent; TV_ITEM ti; assert (pmcl->bDetails); // setup ti to get text & # of children // from the IDF filename entry. // ti.mask = TVIF_TEXT; ti.pszText = pmcl->szFile; ti.cchTextMax = NUMELMS(pmcl->szFile); ti.hItem = pti->hItem; #ifdef DEBUG AuxDebugEx (6, DEBUGLINE TEXT ("Type Change pti=%08X hItem=%08X\r\n"), pti, pti->hItem); #endif // if this entry has a parent, it must be a IDF // instrument name. if so, then we want to read // from its parent first. // htiParent = TreeView_GetParent (lpnm->hwndFrom, pti->hItem); if (htiParent) ti.hItem = htiParent; TreeView_GetItem (lpnm->hwndFrom, &ti); lstrcat (pmcl->szFile, cszIdf); #ifdef DEBUG AuxDebugEx (6, DEBUGLINE TEXT ("mask=%08x htiParent=%08X %08x nChild=%d '%s'\r\n"), ti.mask, htiParent, ti.hItem, ti.cChildren, ti.pszText); #endif // if the selection had a parent, and we are not it's first child // then we need to append child (delimited by <>) after parent // if (htiParent && (TreeView_GetChild(lpnm->hwndFrom, htiParent) != pti->hItem)) { static CONST TCHAR cszAngle[] = TEXT(">"); UINT cch = lstrlen(pmcl->szFile); pmcl->szFile[cch++] = TEXT('<'); ti.mask = TVIF_TEXT; ti.pszText = pmcl->szFile + cch; ti.cchTextMax = NUMELMS(pmcl->szFile) - cch; ti.hItem = pti->hItem; TreeView_GetItem (lpnm->hwndFrom, &ti); lstrcat (pmcl->szFile, cszAngle); #ifdef DEBUG AuxDebugEx (6, DEBUGLINE TEXT ("appending child %08X; '%s'\r\n"), pti->hItem, pmcl->szFile); #endif } pmcl->bChanges |= MCL_IDF_CHANGED; } /*+ * *-=================================================================*/ STATICFN void LoadDevicesIntoList ( HWND hWnd, UINT uId, PMCLASS pmcl, BOOL bList) { HWND hWndT; TCHAR sz[MAX_ALIAS]; DWORD cch = sizeof(sz) / sizeof(TCHAR); UINT ii; BOOL bAdded = FALSE; hWndT = GetDlgItem (hWnd, uId); if (!hWndT) return; SetWindowRedraw (hWndT, FALSE); if (bList) ListBox_ResetContent(hWndT); else ComboBox_ResetContent(hWndT); if (!pmcl->hkMidi && RegCreateKey (HKEY_LOCAL_MACHINE, cszDriversRoot, &pmcl->hkMidi)) return; for (cch = sizeof(sz)/sizeof(TCHAR), ii = 0; ! RegEnumKey (pmcl->hkMidi, ii, sz, cch); ++ii) { TCHAR szAlias[MAX_ALIAS]; int ix; BOOL bExtern; BOOL bActive; // read in the friendly name for this driver // if (GetAlias (pmcl->hkMidi, sz, szAlias, sizeof(szAlias)/sizeof(TCHAR), &bExtern, &bActive)) continue; if (IsPrefix (sz, pmcl->pszKey, TEXT('\\'))) pmcl->ixDevice = ii; // ignore if this is not an external device or if it is disabled // if ( ! bExtern || ! bActive) continue; // otherwise, add the driver name to the combobox/list // if (bList) { ix = ListBox_AddString (hWndT, szAlias); if (ix >= 0) { ListBox_SetItemData (hWndT, ix, ii); bAdded = TRUE; } } else { ix = ComboBox_AddString (hWndT, szAlias); if (ix >= 0) { ComboBox_SetItemData (hWndT, ix, ii); bAdded = TRUE; } } } SetWindowRedraw (hWndT, TRUE); EnableWindow (hWndT, bAdded); if (ii > 0) InvalidateRect (hWndT, NULL, TRUE); // iterate back through the items and select the one // that has item data that corresponds to driver that // owns the current device // if (bList) { UINT jj; for (jj = 0; jj < ii; ++jj) { if ((UINT)ListBox_GetItemData (hWndT, jj) == pmcl->ixDevice) { ListBox_SetCurSel (hWndT, jj); break; } } if (jj >= ii) ListBox_SetCurSel (hWndT, 0); } else { UINT jj; for (jj = 0; jj < ii ; ++jj) { if ((UINT)ComboBox_GetItemData (hWndT, jj) == pmcl->ixDevice) { ComboBox_SetCurSel (hWndT, jj); break; } } if (jj >= ii) ComboBox_SetCurSel (hWndT, 0); } } /*+ LoadClass * *-=================================================================*/ STATICFN BOOL WINAPI LoadClass ( HWND hWnd, PMCLASS pmcl) { HKEY hKeyA = NULL; BOOL bRet = FALSE; UINT cbSize; UINT cch; DWORD dw; if (!pmcl->hkMidi && RegCreateKey (HKEY_LOCAL_MACHINE, cszDriversRoot, &pmcl->hkMidi)) goto cleanup; if (RegOpenKey (pmcl->hkMidi, pmcl->pszKey, &hKeyA)) goto cleanup; // read data from this key // cbSize = sizeof(pmcl->szFile); RegQueryValueEx (hKeyA, cszDefinition, NULL, &dw, (LPBYTE)pmcl->szFile, &cbSize); // strip off leading directory (if there is one). // cch = lstrlen(pmcl->szFile); while (cch && (pmcl->szFile[cch-1] != TEXT('\\'))) --cch; if (cch) { TCHAR szFile[MAX_PATH]; lstrcpy (szFile, pmcl->szFile + cch); lstrcpy (pmcl->szFile, szFile); } // get scheme alias // cbSize = sizeof(pmcl->szAlias); RegQueryValueEx (hKeyA, cszFriendlyName, NULL, &dw, (LPBYTE)pmcl->szAlias, &cbSize); // // pmcl->nPort = 0; cbSize = sizeof(pmcl->nPort); RegQueryValueEx (hKeyA, cszPort, NULL, &dw, (LPVOID)&pmcl->nPort, &cbSize); pmcl->bChanges = 0; bRet = TRUE; cleanup: if (hKeyA) RegCloseKey (hKeyA); return bRet; } /*+ RebuildSchemes * * correct key references in the midi schemes when an instrument * is moved from one external midi port to another * *-=================================================================*/ STATICFN BOOL WINAPI RebuildSchemes ( LPTSTR pszOldKey, LPTSTR pszNewKey) { HKEY hkSchemes; UINT ii; TCHAR sz[MAX_ALIAS]; UINT cchNew; cchNew = 0; if (pszNewKey) cchNew = lstrlen(pszNewKey) + 1; #ifdef DEBUG AuxDebugEx (3, DEBUGLINE TEXT ("RebuildSchemes('%s','%s')\r\n"), pszOldKey, pszNewKey ? pszNewKey : TEXT ("NULL")); #endif if (RegCreateKey (HKEY_LOCAL_MACHINE, cszSchemeRoot, &hkSchemes)) return FALSE; for (ii = 0; ! RegEnumKey (hkSchemes, ii, sz, sizeof(sz)/sizeof(TCHAR)); ++ii) { HKEY hKeyA; UINT jj; if (RegOpenKey (hkSchemes, sz, &hKeyA)) continue; for (jj = 0; ! RegEnumKey (hKeyA, jj, sz, sizeof(sz)/sizeof(TCHAR)); ++jj) { UINT cb; TCHAR szKey[MAX_PATH]; cb = sizeof(szKey); if (RegQueryValue (hKeyA, sz, szKey, &cb)) continue; if (IsSzEqual(pszOldKey, szKey)) { if (cchNew) RegSetValue (hKeyA, sz, REG_SZ, pszNewKey, cchNew); else RegDeleteKey (hKeyA, sz); #ifdef DEBUG AuxDebugEx (4, DEBUGLINE TEXT ("RebuildSchemes - fixing %d\\%d\r\n"), ii, jj); #endif } } } return TRUE; } /*+ OpenInstrumentKey * *-=================================================================*/ STATICFN HKEY WINAPI OpenInstrumentKey ( HWND hWnd, PMCLASS pmcl, BOOL bCreate) // create an new key (do not remove or rebuild existing) { TCHAR szKey[MAX_ALIAS]; HKEY hkInst; HKEY hKeyA = NULL; ZeroMemory (szKey, sizeof (szKey)); #ifdef DEBUG AuxDebugEx (5, DEBUGLINE TEXT ("OpenInstrumentKey(%X,%08X,%d) szKey=%s\r\n"), hWnd, pmcl, bCreate, pmcl->pszKey ? pmcl->pszKey : TEXT ("NULL")); #endif hkInst = NULL; if (!pmcl->hkMidi && RegCreateKey (HKEY_LOCAL_MACHINE, cszDriversRoot, &pmcl->hkMidi)) goto cleanup; if (RegEnumKey (pmcl->hkMidi, pmcl->ixDevice, szKey, sizeof(szKey)/sizeof(TCHAR))) { assert3(0, TEXT ("Failed to enum Midi device %d"), pmcl->ixDevice); goto cleanup; } #ifdef DEBUG AuxDebugEx (6, DEBUGLINE TEXT ("ixDevice = %d, Key is %s\r\n"), pmcl->ixDevice, szKey); #endif // if this is a driver key, or if we are not creating and // the instrument has not changed parentage, we can just // open the existing key and update its content // if (!pmcl->bRemote || (!bCreate && IsPrefix (szKey, pmcl->pszKey, TEXT('\\')))) { if (RegOpenKey (pmcl->hkMidi, pmcl->pszKey, &hKeyA)) goto cleanup; #ifdef DEBUG AuxDebugEx (6, DEBUGLINE TEXT ("opened key %s\r\n"), pmcl->pszKey); #endif } else { UINT kk; TCHAR szEnum[10]; pmcl->bChanges |= MCL_TREE_CHANGED; lstrcat (szKey, cszSlashInstruments); if (RegCreateKey (pmcl->hkMidi, szKey, &hkInst)) goto cleanup; // find an unused keyname // for (kk = 0; kk < 128; ++kk) { wsprintf (szEnum, csz02d, kk); if (RegOpenKey (hkInst, szEnum, &hKeyA)) break; RegCloseKey (hKeyA); } lstrcat (szKey, cszSlash); lstrcat (szKey, szEnum); // create a key with that name // if (RegCreateKey (hkInst, szEnum, &hKeyA)) goto cleanup; #ifdef DEBUG AuxDebugEx (6, DEBUGLINE TEXT ("created key %s\r\n"), szKey); #endif // we are moving an instrument from one // external midi port to another // if (!bCreate) { #ifdef DEBUG AuxDebugEx (3, DEBUGLINE TEXT ("Deleting key midi\\%s\r\n"), pmcl->pszKey); #endif RegDeleteKey (pmcl->hkMidi, pmcl->pszKey); RebuildSchemes (pmcl->pszKey, szKey); } lstrcpy (pmcl->pszKey, szKey); } cleanup: if (hkInst) RegCloseKey (hkInst); return hKeyA; } /*+ SaveDetails * *-=================================================================*/ STATICFN UINT WINAPI SaveDetails ( HWND hWnd, PMCLASS pmcl, BOOL bCreate) { HWND hWndT; HKEY hKeyA; UINT bChanges; UINT cbSize; // this should only be called on shutdown // of details page (or on exit of wizard) // assert (pmcl->bDetails); hKeyA = OpenInstrumentKey (hWnd, pmcl, bCreate); if ( ! hKeyA) return FALSE; hWndT = GetDlgItem (hWnd, IDE_ALIAS); if (hWndT) { TCHAR sz[NUMELMS(pmcl->szAlias)]; GetWindowText (hWndT, sz, NUMELMS(sz)); if ( ! IsSzEqual(sz, pmcl->szAlias)) { lstrcpy (pmcl->szAlias, sz); pmcl->bChanges |= MCL_ALIAS_CHANGED; } } #ifdef DEBUG AuxDebugEx (2, DEBUGLINE TEXT ("--------SaveInstrument---------\r\n")); AuxDebugEx (2, TEXT ("\tChanges=%x\r\n"), pmcl->bChanges); AuxDebugEx (2, TEXT ("\tFriendly='%s'\r\n"), pmcl->szAlias); AuxDebugEx (2, TEXT ("\tDefinition='%s'\r\n"), pmcl->szFile); #endif // save value data from this key // cbSize = (lstrlen(pmcl->szFile)+1) * sizeof(TCHAR); RegSetValueEx (hKeyA, cszDefinition, 0, REG_SZ, (LPBYTE)pmcl->szFile, cbSize); cbSize = (lstrlen(pmcl->szAlias)+1) * sizeof(TCHAR); RegSetValueEx (hKeyA, cszFriendlyName, 0, REG_SZ, (LPBYTE)pmcl->szAlias, cbSize); RegSetValueEx (hKeyA, cszPort, 0, REG_BINARY, (LPVOID)&pmcl->nPort, 1); RegCloseKey (hKeyA); bChanges = pmcl->bChanges; pmcl->bChanges = 0; // return 'changed' flag // return bChanges; } /*+ ParseAngleBrackets * * replace '<>' delimiters with 0s and return a pointer * to the delimited string. This function does nothing if * the string does not end in a '>' delimiter * *-=================================================================*/ static LPTSTR __inline WINAPI ParseAngleBrackets ( LPTSTR pszArg) { LPTSTR psz = pszArg + lstrlen(pszArg); while (--psz > pszArg) { if (*psz == TEXT('>')) { *psz = 0; while (--psz >= pszArg) { if (*psz == TEXT('<')) { *psz = 0; return psz+1; } } } } return NULL; } /*+ fnFindDevice * *-=================================================================*/ struct _find_data { HWND hWnd; UINT idMfg; UINT idProd; LPTSTR pszInstr; }; STATICFN BOOL WINAPI fnFindDevice ( LPVOID lpv, UINT nEnum, LPIDFHEADER pHdr, LPIDFINSTINFO pInst) { struct _find_data * pfd = lpv; TCHAR szTemp[MAX_PATH]; assert (pfd); MultiByteToWideChar(GetACP(), 0, pHdr->abInstID, -1, szTemp, sizeof(szTemp)/sizeof(TCHAR)); if (!pfd->pszInstr || IsSzEqual (pfd->pszInstr, szTemp)) { if (SetDlgItemText (pfd->hWnd, pfd->idMfg, (TCHAR*)(pInst->abData+pInst->cbManufactASCII ))) pfd->idMfg = 0; if (SetDlgItemText (pfd->hWnd, pfd->idProd, (TCHAR*)(pInst->abData + pInst->cbManufactASCII + pInst->cbManufactUNICODE))) pfd->idProd = 0; // we can stop enumerating now // return FALSE; } // return true to consider ennumeration // return TRUE; } /*+ ActivateInstrumentPage * *-=================================================================*/ STATICFN void WINAPI ActivateInstrumentPage ( HWND hWnd, PMCLASS pmcl) { pmcl->bDetails = FALSE; if (GetDlgItem (hWnd, IDC_TYPES)) { pmcl->bDetails = TRUE; LoadTypesIntoTree (hWnd, IDC_TYPES, pmcl); SetTypesEdit (hWnd, IDE_TYPES, pmcl); LoadDevicesIntoList (hWnd, IDC_DEVICES, pmcl, FALSE); if ( ! pmcl->bRemote) { HWND hWndT = GetDlgItem (hWnd, IDC_DEVICES); if (hWndT) EnableWindow (hWndT, FALSE); } } else { struct _find_data fd; TCHAR szFile[NUMELMS(pmcl->szFile)]; if ( ! IsFullPath (pmcl->szFile)) { UINT cch; GetIDFDirectory (szFile, NUMELMS(szFile)); cch = lstrlen (szFile); if (cch && szFile[cch-1] != TEXT('\\')) szFile[cch++] = TEXT('\\'); lstrcpyn (szFile + cch, pmcl->szFile, NUMELMS(szFile)-cch); } else lstrcpy (szFile, pmcl->szFile); fd.hWnd = hWnd; fd.idMfg = IDC_MANUFACTURER; fd.idProd = IDC_DEVICE_TYPE; fd.pszInstr = ParseAngleBrackets(szFile); idfEnumInstruments (szFile, fnFindDevice, &fd); if (fd.idMfg) SetDlgItemText (hWnd, fd.idMfg, cszEmpty); if (fd.idProd) { LoadString (ghInstance, IDS_UNSPECIFIED, szFile, NUMELMS(szFile)); SetDlgItemText (hWnd, fd.idProd, szFile); } } } /*+ IsInstrumentKey * * return TRUE if the keyname passed refers to an instrument key * rather than a device key. device keys usually end in '>', * while instrument keys will always be of the form * \Instruments\ where and can be arbitrary * strings. * *-=================================================================*/ STATICFN BOOL WINAPI IsInstrumentKey ( LPTSTR pszKey) { UINT cch = lstrlen(pszKey); if (!cch) return FALSE; if (pszKey[cch-1] == TEXT('>')) return FALSE; while (--cch) if (pszKey[cch] == TEXT('\\')) return TRUE; return FALSE; } /*+ InitInstrumentProps * *-=================================================================*/ STATICFN BOOL WINAPI InitInstrumentProps ( HWND hWnd, PMCLASS pmcl) { LPPROPSHEETPAGE ppsp = pmcl->ppsp; PMPSARGS pmpsa; assert (ppsp && ppsp->dwSize == sizeof(*ppsp)); if (!ppsp) return FALSE; // EndDialog (hWnd, FALSE); pmcl->bRemote = FALSE; pmpsa = (LPVOID)ppsp->lParam; if (pmpsa && pmpsa->lpfnMMExtPSCallback) { pmpsa->lpfnMMExtPSCallback (MM_EPS_GETNODEDESC, (DWORD_PTR)pmcl->szAlias, sizeof(pmcl->szAlias), (DWORD_PTR)pmpsa->lParam); #ifdef DEBUG AuxDebugEx (3, TEXT ("\tgot szAlias='%s'\r\n"), pmcl->szAlias); #endif pmpsa->lpfnMMExtPSCallback (MM_EPS_GETNODEID, (DWORD_PTR)pmcl->szFullKey, sizeof(pmcl->szFullKey), (DWORD_PTR)pmpsa->lParam); #ifdef DEBUG AuxDebugEx (3, TEXT ("\tgot szFullKey='%s'\r\n"), pmcl->szFullKey); #endif // skip over the midi\ part of the key if we have been // passed that. we want the driver name to be the first // part of the key // pmcl->pszKey = pmcl->szFullKey; if (!lstrnicmp (pmcl->pszKey, (LPTSTR)cszMidiSlash, lstrlen(cszMidiSlash))) { pmcl->pszKey += lstrlen(cszMidiSlash); } // If this is an instrument key, set bRemote to true // if (IsInstrumentKey(pmcl->pszKey)) pmcl->bRemote = TRUE; } else LoadString (ghInstance, IDS_UNSPECIFIED, pmcl->szAlias, NUMELMS(pmcl->szAlias)); SetDlgItemText (hWnd, IDE_ALIAS, pmcl->szAlias); Static_SetIcon(GetDlgItem (hWnd, IDC_CLASS_ICON), LoadIcon (ghInstance, MAKEINTRESOURCE(IDI_INSTRUMENT))); LoadClass (hWnd, pmcl); //ActivateInstrumentPage(hWnd, pmcl); return TRUE; } /*+ NotifyMapper * *-=================================================================*/ STATICFN void WINAPI NotifyMapper ( PMCLASS pmcl, UINT bChanges, HWND hWnd) { // tell midi mapper about tree changes, IDF changes and port changes // if (bChanges & (MCL_TREE_CHANGED | MCL_IDF_CHANGED | MCL_PORT_CHANGED)) { KickMapper (hWnd); } } /*+ * *-=================================================================*/ STATICFN BOOL WINAPI RemoveInstrument ( HWND hWnd, PMCLASS pmcl) { RegDeleteKey (pmcl->hkMidi, pmcl->pszKey); RebuildSchemes (pmcl->pszKey, NULL); return TRUE; } BOOL WINAPI RemoveInstrumentByKeyName ( LPCTSTR pszKey) { MCLASS mcl; BOOL rc = FALSE; memset ((TCHAR *)&mcl, 0x00, sizeof(mcl)); mcl.pszKey = (LPTSTR)pszKey; if (!lstrnicmp (mcl.pszKey, (LPTSTR)cszMidiSlash, lstrlen(cszMidiSlash))) { mcl.pszKey += lstrlen(cszMidiSlash); } if (LoadClass (NULL, &mcl)) { rc = RemoveInstrument (NULL, &mcl); if (mcl.hkMidi) RegCloseKey (mcl.hkMidi); } return rc; } /*+ MidiInstrumentCommands * *-=================================================================*/ BOOL WINAPI MidiInstrumentCommands ( HWND hWnd, UINT_PTR uId, LPNMHDR lpnm) { PMCLASS pmcl = GetDlgData(hWnd); #ifdef DEBUG AuxDebugEx (4, DEBUGLINE TEXT ("InstrumentCommands(..%d..) %d(%xx)\r\n"), uId, lpnm->code, lpnm->code); #endif if (!pmcl) return FALSE; switch (uId) { case IDE_ALIAS: if (lpnm->code == EN_CHANGE) PropSheet_Changed(GetParent(hWnd), hWnd); break; case IDB_REMOVE: if (RemoveInstrument (hWnd, pmcl)) { PMPSARGS pmpsa = (LPVOID)pmcl->ppsp->lParam; if (pmpsa && pmpsa->lpfnMMExtPSCallback) pmpsa->lpfnMMExtPSCallback (MM_EPS_TREECHANGE, 0, 0, (DWORD_PTR)pmpsa->lParam); NotifyMapper (pmcl, MCL_TREE_CHANGED, hWnd); SetDlgData(hWnd, NULL); if (pmcl->hkMidi) RegCloseKey (pmcl->hkMidi), pmcl->hkMidi = NULL; LocalFree ((HLOCAL)(UINT_PTR)(DWORD_PTR)pmcl); PropSheet_PressButton(GetParent(hWnd), PSBTN_CANCEL); } break; case IDB_NEWTYPE: InstallNewIDF (hWnd); LoadTypesIntoTree (hWnd, IDC_TYPES, pmcl); SetTypesEdit (hWnd, IDE_TYPES, pmcl); break; case IDC_TYPES: if ((lpnm->code == TVN_SELCHANGED) && !pmcl->bFillingList) { HandleTypesSelChange (pmcl, lpnm); SetTypesEdit (hWnd, IDE_TYPES, pmcl); PropSheet_Changed(GetParent(hWnd), hWnd); #ifdef DEBUG AuxDebugEx (5, DEBUGLINE TEXT ("file='%s'\r\n"), pmcl->szFile); #endif } break; case IDC_DEVICES: if (lpnm->code == CBN_SELCHANGE) { int ix = ComboBox_GetCurSel (lpnm->hwndFrom); pmcl->ixDevice = (UINT) ((ix >= 0) ? ComboBox_GetItemData (lpnm->hwndFrom, ix) : -1); PropSheet_Changed(GetParent(hWnd), hWnd); #ifdef DEBUG AuxDebugEx (4, DEBUGLINE TEXT ("IDC_DEVICES.selChange(%d) %d\r\n"), ix, pmcl->ixDevice); #endif } break; // we get these only if invoked as a dialog, not as a property // sheet // case IDOK: { UINT bChanges = SaveDetails (hWnd, pmcl, FALSE); NotifyMapper (pmcl, bChanges, hWnd); } // fall through case IDCANCEL: EndDialog (hWnd, uId); break; case 0: { LONG lRet = FALSE; switch (lpnm->code) { case PSN_APPLY: #ifdef DEBUG AuxDebugEx (4, DEBUGLINE TEXT ("ID_APPLY\r\n")); #endif if (pmcl->bDetails) { UINT bChanges = SaveDetails (hWnd, pmcl, FALSE); NotifyMapper (pmcl, bChanges, hWnd); // tell mmsys.cpl about tree & alias changes // if (bChanges & (MCL_TREE_CHANGED | MCL_ALIAS_CHANGED)) { PMPSARGS pmpsa = (LPVOID)pmcl->ppsp->lParam; if (pmpsa && pmpsa->lpfnMMExtPSCallback) pmpsa->lpfnMMExtPSCallback (MM_EPS_TREECHANGE, 0, 0, (DWORD_PTR)pmpsa->lParam); } // we do this because the SysTreeView for IDF files // forgets its selection when APPLY is pressed. go figure // #ifdef DEBUG AuxDebugEx (7, DEBUGLINE TEXT ("PSN_APPLY: re-doing selection '%s'\r\n"), pmcl->szFile); //ActivateInstrumentPage (hWnd, pmcl); AuxDebugEx (7, DEBUGLINE TEXT ("PSN_APPLY: done re-doing selection '%s'\r\n"), pmcl->szFile); #endif } break; case PSN_KILLACTIVE: break; case PSN_SETACTIVE: #ifdef DEBUG AuxDebugEx (4, DEBUGLINE TEXT ("PSN_SETACTIVE\r\n")); #endif ActivateInstrumentPage (hWnd, pmcl); #ifdef DEBUG AuxDebugEx (4, DEBUGLINE TEXT ("PSN_SETACTIVE ends\r\n")); #endif break; } SetWindowLongPtr (hWnd, DWLP_MSGRESULT, (LONG_PTR)lRet); break; } } return FALSE; } const static DWORD aKeyWordIds[] = { // Context Help IDs IDC_CLASS_ICON, IDH_MMCPL_DEVPROP_DETAILS_INSTRUMENT, IDE_ALIAS, IDH_MMCPL_DEVPROP_DETAILS_INSTRUMENT, IDC_DEVICES, IDH_MMCPL_DEVPROP_DETAILS_MIDI_PORT, IDC_TYPES, IDH_MMCPL_DEVPROP_DETAILS_INS_DEF, IDB_NEWTYPE, IDH_MMCPL_DEVPROP_DETAILS_BROWSE, 0, 0 }; /*+ MidiInstrumentDlgProc * *-=================================================================*/ INT_PTR CALLBACK MidiInstrumentDlgProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { #if defined DEBUG || defined DEBUG_RETAIL TCHAR chNest = szNestLevel[0]++; #endif switch (uMsg) { case WM_COMMAND: { NMHDR nmh; nmh.hwndFrom = GET_WM_COMMAND_HWND(wParam, lParam); nmh.idFrom = GET_WM_COMMAND_ID(wParam, lParam); nmh.code = GET_WM_COMMAND_CMD(wParam, lParam); MidiInstrumentCommands(hWnd, nmh.idFrom, &nmh); } break; case WM_NOTIFY: #ifdef DEBUG AuxDebugEx (3, DEBUGLINE TEXT ("WM_NOTIFY(%x,%x,%x)\r\n"), hWnd, wParam, lParam); #endif #if defined DEBUG || defined DEBUG_RETAIL ++szNestLevel[0]; #endif MidiInstrumentCommands(hWnd, wParam, (LPVOID)lParam); #if defined DEBUG || defined DEBUG_RETAIL --szNestLevel[0]; #endif break; case WM_INITDIALOG: { PMCLASS pmcl; pmcl = (LPVOID)LocalAlloc(LPTR, sizeof(*pmcl)); if (!pmcl) { EndDialog(hWnd, FALSE); break; } pmcl->ppsp = (LPVOID)lParam; SetDlgData (hWnd, pmcl); #ifdef DEBUG AuxDebugEx (5, DEBUGLINE TEXT ("midiInstrument.WM_INITDLG ppsp=%08X\r\n")); #endif //AuxDebugDump (8, pmcl->ppsp, sizeof(*(pmcl->ppsp))); InitInstrumentProps (hWnd, pmcl); break; } case WM_DESTROY: { PMCLASS pmcl = GetDlgData(hWnd); if (pmcl) { if (pmcl->hkMidi) RegCloseKey (pmcl->hkMidi), pmcl->hkMidi = NULL; #ifdef USE_IDF_ICONS if (pmcl->hIDFImageList) { HWND hWndT = GetDlgItem (hWnd, IDC_TYPES); if (hWndT) TreeView_SetImageList (hWndT, NULL, TVSIL_NORMAL); ImageList_Destroy (pmcl->hIDFImageList); pmcl->hIDFImageList = NULL; } #endif LocalFree ((HLOCAL)(UINT_PTR)(DWORD_PTR)pmcl); } break; } case WM_CONTEXTMENU: WinHelp ((HWND) wParam, NULL, HELP_CONTEXTMENU, (UINT_PTR) (LPTSTR) aKeyWordIds); break; case WM_HELP: { LPHELPINFO lphi = (LPVOID) lParam; WinHelp (lphi->hItemHandle, NULL, HELP_WM_HELP, (UINT_PTR) (LPTSTR) aKeyWordIds); break; } } #if defined DEBUG || defined DEBUG_RETAIL szNestLevel[0] = chNest; #endif return FALSE; } /// --------------------- Wizard stuff ---------------------- static LPTSTR aidWiz[] = { MAKEINTRESOURCE(IDD_MIDIWIZ02), MAKEINTRESOURCE(IDD_MIDIWIZ03), MAKEINTRESOURCE(IDD_MIDIWIZ04) }; #define WIZ_TEMPLATE_DEVICE aidWiz[0] #define WIZ_TEMPLATE_IDF aidWiz[1] #define WIZ_TEMPLATE_ALIAS aidWiz[2] typedef struct _wizdata { LPPROPSHEETPAGE ppspActive; HBITMAP hBmp; MCLASS mcl; PMCMIDI pmcm; HPROPSHEETPAGE ahpsp[NUMELMS(aidWiz)]; } WIZDATA, * PWIZDATA; /*+ FindInstrument * *-=================================================================*/ STATICFN PINSTRUM WINAPI FindInstrument ( PMCMIDI pmcm, LPTSTR pszFriendly) { UINT ii; for (ii = 0; ii < pmcm->nInstr; ++ii) { assert (pmcm->api[ii]); if (IsSzEqual(pszFriendly, pmcm->api[ii]->szFriendly)) return pmcm->api[ii]; } return NULL; } /*+ UniqueFriendlyName * *-=================================================================*/ STATICFN BOOL WINAPI fnFirstInstr ( LPVOID lpv, UINT nEnum, LPIDFHEADER pHdr, LPIDFINSTINFO pInst) { LPTSTR pszInstr = lpv; assert (pszInstr); MultiByteToWideChar(GetACP(), 0, pHdr->abInstID, -1, pszInstr, MAX_ALIAS); return FALSE; } STATICFN BOOL WINAPI UniqueFriendlyName ( PMCMIDI pmcm, PMCLASS pmcl, LPTSTR pszAlias, UINT cchAlias) { TCHAR szFile[MAX_PATH * 2]; LPTSTR pszInstr; UINT cch; UINT ii; GetIDFDirectory (szFile, sizeof(szFile)/sizeof(TCHAR)); cch = lstrlen(szFile); if (cch && szFile[cch-1] != TEXT('\\')) szFile[cch++] = TEXT('\\'); lstrcpy (szFile + cch, pmcl->szFile); pszInstr = ParseAngleBrackets (szFile); if ( ! pszInstr) { pszInstr = szFile + lstrlen(szFile) + 1; idfEnumInstruments (szFile, fnFirstInstr, pszInstr); } // if no instrument name from the IDF file, get a default // from our resources // if ( ! lstrlen (pszInstr)) { LoadString (ghInstance, IDS_DEF_INSTRNAME, pszInstr, MAX_ALIAS); return FALSE; } // make the instrument name the same as the alias, and prepare // to append a number if the alias turns out not to be unique // lstrcpyn (pszAlias, pszInstr, cchAlias); cch = lstrlen (pszAlias); cch = min (cch, (UINT)MAX_ALIAS-3); ii = 1; // loop while we are trying to use an instrument name // that has already been used // while (FindInstrument (pmcm, pszAlias)) { static CONST TCHAR cszSpaceD[] = TEXT (" %d"); wsprintf (pszAlias + cch, cszSpaceD, ++ii); if (ii > NUMELMS(pmcm->api)) { assert2(0, TEXT ("infinite loop in UniqueFriendlyName!")); break; } } return TRUE; } /*+ MidiWizardCommands * *-=================================================================*/ BOOL WINAPI MidiWizardCommands ( HWND hWnd, UINT_PTR uId, LPNMHDR lpnm) { PWIZDATA pwd; LPPROPSHEETPAGE ppsp = GetDlgData(hWnd); LONG lRet = TRUE; #ifdef DEBUG AuxDebugEx (4, DEBUGLINE TEXT ("WizardCmd ppsp=%08X code=%d(0x%X)\r\n"), ppsp, lpnm->code, lpnm->code); #endif pwd = NULL; if (ppsp) pwd = (LPVOID)ppsp->lParam; assert (pwd); switch (uId) { case IDC_TYPES: if ((lpnm->code == TVN_SELCHANGED) && !pwd->mcl.bFillingList) { HandleTypesSelChange (&pwd->mcl, lpnm); UniqueFriendlyName (pwd->pmcm, &pwd->mcl, pwd->mcl.szAlias, NUMELMS(pwd->mcl.szAlias)); #ifdef DEBUG AuxDebugEx (5, DEBUGLINE TEXT ("file='%s'\r\n"), pwd->mcl.szFile); #endif } break; case IDB_NEWTYPE: InstallNewIDF (hWnd); LoadTypesIntoTree (hWnd, IDC_TYPES, &pwd->mcl); break; //case IDC_DEVICES: // break; //case IDE_ALIAS: // break; case 0: { switch (lpnm->code) { case PSN_HELP: break; case PSN_KILLACTIVE: #ifdef DEBUG AuxDebugEx (4, DEBUGLINE TEXT ("PSN_KILLACTIVE\r\n")); #endif break; case PSN_SETACTIVE: { DWORD dwWizBtn; #ifdef DEBUG AuxDebugEx (4, DEBUGLINE TEXT ("PSN_SETACTIVE\r\n")); #endif if (pwd) pwd->ppspActive = ppsp; if (ppsp->pszTemplate == WIZ_TEMPLATE_DEVICE) // midi device LoadDevicesIntoList (hWnd, IDC_DEVICES, &pwd->mcl, TRUE); else if (ppsp->pszTemplate == WIZ_TEMPLATE_IDF) // idf file LoadTypesIntoTree (hWnd, IDC_TYPES, &pwd->mcl); else if (ppsp->pszTemplate == WIZ_TEMPLATE_ALIAS) // alias SetDlgItemText (hWnd, IDE_ALIAS, pwd->mcl.szAlias); dwWizBtn = PSWIZB_NEXT | PSWIZB_BACK; if (ppsp->pszTemplate == aidWiz[NUMELMS(aidWiz)-1]) dwWizBtn = PSWIZB_FINISH | PSWIZB_BACK; else if (ppsp->pszTemplate == aidWiz[0]) dwWizBtn = PSWIZB_NEXT; PropSheet_SetWizButtons (GetParent(hWnd), dwWizBtn); } break; case PSN_WIZNEXT: #ifdef DEBUG AuxDebugEx (4, DEBUGLINE TEXT ("PSN_WIZNEXT\r\n")); #endif if (ppsp->pszTemplate == WIZ_TEMPLATE_DEVICE) // midi device { HWND hWndT; int ix; pwd->mcl.ixDevice = (UINT)-1; pwd->mcl.nPort = 0; hWndT = GetDlgItem (hWnd, IDC_DEVICES); if (hWndT) { ix = ListBox_GetCurSel (hWndT); if (ix >= 0) pwd->mcl.ixDevice = (UINT) ListBox_GetItemData (hWndT, ix); } if (pwd->mcl.ixDevice == (UINT)-1) SetWindowLongPtr (hWnd, DWLP_MSGRESULT, (LONG_PTR)-1); } else if (ppsp->pszTemplate == WIZ_TEMPLATE_IDF) // idf file { if ( ! pwd->mcl.szAlias[0]) { LoadString (ghInstance, IDS_DEF_INSTRNAME, pwd->mcl.szAlias, NUMELMS(pwd->mcl.szAlias)); } } else if (ppsp->pszTemplate == WIZ_TEMPLATE_IDF) // alias { GetDlgItemText (hWnd, IDE_ALIAS, pwd->mcl.szAlias, NUMELMS(pwd->mcl.szAlias)); } break; case PSN_WIZBACK: #ifdef DEBUG AuxDebugEx (4, DEBUGLINE TEXT ("PSN_WIZBACK\r\n")); #endif break; case PSN_WIZFINISH: #ifdef DEBUG AuxDebugEx (4, DEBUGLINE TEXT ("PSN_WIZFINISH\r\n")); #endif //if (!save success) lRet = FALSE; //SetWindowLong (hWnd, DWL_MSGRESULT, lRet); SaveDetails (hWnd, &pwd->mcl, TRUE); break; default: lRet = FALSE; } } break; } return lRet; } /*+ MidiWizardDlgProc * *-=================================================================*/ INT_PTR CALLBACK MidiWizardDlgProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { BOOL bRet = TRUE; #if defined DEBUG || defined DEBUG_RETAIL TCHAR chNest = szNestLevel[0]++; #endif switch (uMsg) { case WM_COMMAND: { NMHDR nmh; nmh.hwndFrom = GET_WM_COMMAND_HWND(wParam, lParam); nmh.idFrom = GET_WM_COMMAND_ID(wParam, lParam); nmh.code = GET_WM_COMMAND_CMD(wParam, lParam); bRet = MidiWizardCommands(hWnd, nmh.idFrom, &nmh); } break; case WM_NOTIFY: #ifdef DEBUG AuxDebugEx (6, DEBUGLINE TEXT ("WM_NOTIFY(%x,%x,%x)\r\n"), hWnd, wParam, lParam); #endif bRet = MidiWizardCommands(hWnd, wParam, (LPVOID)lParam); break; case WM_INITDIALOG: { PWIZDATA pwd; LPPROPSHEETPAGE ppsp = (LPVOID)lParam; SetDlgData (hWnd, lParam); pwd = (LPVOID)ppsp->lParam; SendDlgItemMessage(hWnd, IDC_WIZBMP, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)pwd->hBmp); #ifdef DEBUG AuxDebugEx (5, DEBUGLINE TEXT ("MidiWizard.WM_INITDLG ppsp=%08X\r\n"), ppsp); #endif } break; case WM_DESTROY: { PWIZDATA pwd; LPPROPSHEETPAGE ppsp = GetDlgData(hWnd); if (ppsp && (pwd = (LPVOID)ppsp->lParam) != NULL) { if (pwd->mcl.hkMidi) RegCloseKey (pwd->mcl.hkMidi), pwd->mcl.hkMidi = NULL; #ifdef USE_IDF_ICONS if (pwd->mcl.hIDFImageList) { HWND hWndT = GetDlgItem (hWnd, IDC_TYPES); if (hWndT) TreeView_SetImageList (hWndT, NULL, TVSIL_NORMAL); ImageList_Destroy (pwd->mcl.hIDFImageList); pwd->mcl.hIDFImageList = NULL; } #endif } } break; default: bRet = FALSE; break; } #if defined DEBUG || defined DEBUG_RETAIL szNestLevel[0] = chNest; #endif return bRet; } INT CALLBACK iSetupDlgCallback( IN HWND hwndDlg, IN UINT uMsg, IN LPARAM lParam ) /*++ Routine Description: Call back used to remove the "?" from the wizard page. Arguments: hwndDlg - Handle to the property sheet dialog box. uMsg - Identifies the message being received. This parameter is one of the following values: PSCB_INITIALIZED - Indicates that the property sheet is being initialized. The lParam value is zero for this message. PSCB_PRECREATE Indicates that the property sheet is about to be created. The hwndDlg parameter is NULL and the lParam parameter is a pointer to a dialog template in memory. This template is in the form of a DLGTEMPLATE structure followed by one or more DLGITEMTEMPLATE structures. lParam - Specifies additional information about the message. The meaning of this value depends on the uMsg parameter. Return Value: The function returns zero. --*/ { switch( uMsg ) { case PSCB_INITIALIZED: break; case PSCB_PRECREATE: if( lParam ){ DLGTEMPLATE *pDlgTemplate = (DLGTEMPLATE *)lParam; pDlgTemplate->style &= ~DS_CONTEXTHELP; } break; } return FALSE; } /*+ MidiInstrumentsWizard * *-=================================================================*/ INT_PTR MidiInstrumentsWizard ( HWND hWnd, PMCMIDI pmcm, // optional LPTSTR pszDriverKey) // optional { WIZDATA wd; PROPSHEETHEADER psh; PROPSHEETPAGE psp; UINT ii; INT_PTR iRet = -1; LPTSTR psz; ZeroMemory (&wd, sizeof(wd)); wd.mcl.bDetails = TRUE; wd.mcl.bRemote = TRUE; wd.mcl.ixDevice = 0; LoadString (ghInstance, IDS_DEF_DEFINITION, wd.mcl.szFile, NUMELMS(wd.mcl.szFile)); // set the default driver key to what was passed. // If someone passed us a path, rather than a driver key // null out the '\\' characters so that we see only the // leading driver part of the key. // wd.mcl.pszKey = wd.mcl.szFullKey; if (pszDriverKey) lstrcpy (wd.mcl.szFullKey, pszDriverKey); if (!lstrnicmp (wd.mcl.pszKey, (LPTSTR)cszMidiSlash, lstrlen(cszMidiSlash))) wd.mcl.pszKey += lstrlen(cszMidiSlash); psz = wd.mcl.pszKey; while (*psz) { if (*psz == TEXT('\\')) *psz = 0; ++psz; } // load all current instrument names from the registry // if (!(wd.pmcm = pmcm)) { wd.pmcm = (LPVOID) LocalAlloc (LPTR, sizeof(MCMIDI)); if (!wd.pmcm) return -1; LoadInstruments (wd.pmcm, FALSE); } psp.dwSize = sizeof(psp); psp.dwFlags = PSP_DEFAULT; psp.hInstance = ghInstance; psp.pfnDlgProc = MidiWizardDlgProc; psp.lParam = (LPARAM)&wd; for (psh.nPages = 0, ii = 0; ii < NUMELMS(aidWiz); ++ii) { HPROPSHEETPAGE hpsp; psp.pszTemplate = aidWiz[ii]; wd.ahpsp[psh.nPages] = hpsp = CreatePropertySheetPage(&psp); if (hpsp) ++psh.nPages; } if ( ! psh.nPages) return -1; wd.hBmp = LoadBitmap(ghInstance, MAKEINTRESOURCE(IDB_WIZBMP)); #ifdef DEBUG AuxDebugEx (3, DEBUGLINE TEXT ("Wizard bitmap = %08X\r\n")); #endif psh.dwSize = sizeof(psh); psh.dwFlags = PSH_PROPTITLE | PSH_WIZARD_LITE | PSH_USECALLBACK; psh.hwndParent = hWnd; psh.hInstance = ghInstance; psh.pszCaption = MAKEINTRESOURCE(IDS_WIZNAME); psh.nPages = NUMELMS(aidWiz); psh.nStartPage = 0; psh.phpage = wd.ahpsp; psh.pfnCallback = iSetupDlgCallback; iRet = PropertySheet (&psh); // free dynamically allocated stuff. // if (wd.hBmp) DeleteObject (wd.hBmp); // if no MCMIDI was passed, we dynamically loaded one, // so now we need to free it. // if ( ! pmcm) { if (wd.pmcm->hkMidi) RegCloseKey (wd.pmcm->hkMidi); FreeInstruments (wd.pmcm); LocalFree ((HLOCAL)(UINT_PTR)(DWORD_PTR)wd.pmcm); } return iRet; }