This function gets the list of languages that are 'equivalent' to the given language. We say language L1 is equivalent to language L2 if speakers of L2 can readily understand L1 without intentional study or extraordinary effort.
The equivalence relationship is defined in a static table. Please refer to the definition of 'KEquivalentLists' for details. Each row in the table is formatted like this:
L1, L2, L3, ..., Ln, ELangNone
@codeend
In which L2, ..., Ln are equivalents of L1, and ELangNone marks the end of an
entry. The list is ordered. Compared with L3, L2 is nearer to L1. When choosing
an equivalent of L1, L2 shall be preferred over L3, L3 shall be preferred
over L4, and so on.
L1 is always returned as the nearest equivalent of L1 itself.
BaflUtils::NearestLanguageFileV2 searches language specific resource files
according to the 'equivalent' relationship returned by this function.
@param aLang The language whose equivalents needs to be found out.
@param aEquivalents On return, this array contains the ordered list of
languages that are equivalent to the given language. If there is no
entry for the given language in the table, this array will contain
two elements on return: the first is the given language itself
and the 2nd one is ELangNone. For any language that has equivalents
defined, content of he corresponding entry is returned.
@see BaflUtils::NearestLanguageFileV2
*/
EXPORT_C void
BaflUtils::GetEquivalentLanguageList(TLanguage aLang, TLanguagePath& aEquivalents)
{
aEquivalents[0] = aLang;
aEquivalents[1] = ELangNone;
const TInt len = sizeof(KEquivalentLists) / sizeof(KEquivalentLists[0]);
for (TInt i = 0; i < len; ++i)
{
const TLanguage *ptr = KEquivalentLists[i];
if (ptr[0] == aLang)
{
TInt index = 1;
while (ELangNone != *ptr)
{
aEquivalents[index++] = (TLanguage)*(++ptr);
}
aEquivalents[index] = ELangNone;
break;
} // end if ptr[0]
} // end for i
}
/**
NearestLanguageFileV2 is very similar to the existing 'NearestLanguageFile'
function. The only difference between NearestLanguageFile and
NearestLanguageFileV2 is the order in which language specific
resource files are searched for.
NearestLanguageFile searches language specific resource files in the
order defined by the 'downgrade path' of the given language. Content of the
downgrade path is dependent on the current active locale, and parts of
it is runtime configurable.
NearestLanguageFileV2 searches for language specific resource files
in the order defined by the 'language equivalence table', which is a
static data table fixed at build time. There is one entry in the table for
each language that has one or more equivalents.
@param aFs An active file server session.
@param aName Name of the language-neutral resource file name which consist of
an optional drive specification, followed by an optional path name,
followed by basename for filename, followed by a period and extension.
On return, in case of a match, this is replaced by the language-specific version
which consists of the last two characters of the extension plus any preceding
numeric characters being replaced by the language code. Remains unchanged when there's no match
@param aLanguage On return, in case of a match, this is replaced by the corresponding language.
In case of no match, it is set to ELangNone.
@see TLanguage
@see BaflUtils::GetEquivalentLanguageList
*/
EXPORT_C void
BaflUtils::NearestLanguageFileV2(const RFs& aFs,TFileName& aName, TLanguage& aLanguage)
{
TNearestLanguageFileFinder finder(aFs);
TBool goodSuffix=finder.SetFileName(aName);
// Continue only if the suffix is good.
if(goodSuffix)
{
// add preset customised resource drive to drive list
// Note that errors returned from AddCustomResourceDrive are ignored. This is because if
// a custom resource drive has not been found we still want to continue on with searching
// other drives according to our algorithm
finder.AddCustomResourceDrive();
GetEquivalentLanguageList(User::Language(), finder.iPath);
if (!finder.FindLanguageAndDrive()
&& KErrNone != finder.FindFirstLanguageFileAndDrive())
finder.RepairFileName();
aLanguage = finder.Language();
}
else
{
aLanguage = ELangNone;
}
}
// TLibAssocBase
EXPORT_C TLibAssocBase::TLibAssocBase(const RLibrary& aLib,TAny* aPtr)
: iLibrary(aLib),iPtr(aPtr)
/**
Constructs the object taking the specified DLL and a class instance.
@param aLib A reference to a DLL that has already been opened.
@param aPtr An untyped pointer to an object to be associated with the DLL.
Typically, this object will have been created using
the ordinal 1 function from that DLL. */
{}
EXPORT_C void TLibAssocBase::Set(const RLibrary& aLib,TAny* aPtr)
/**
Implements TLibAssoc::Set().
@param aLib A reference to a DLL that has already been opened.
@param aClass A pointer to an object to be associated with the DLL.
Typically, this object will have been created using
the ordinal 1 function from that DLL.
@see TLibAssoc::Set */
{
__ASSERT_ALWAYS(iLibrary.Handle()==KNullHandle&&iPtr==NULL,Panic(EBafPanicLibAssocAlreadySet));
iLibrary=aLib;
iPtr=aPtr;
}
EXPORT_C void TLibAssocBase::DoUnload(TAny* aThis)
/**
Calls Close() on the associated DLL.
@param aThis An untyped pointer to a TLibAssoc type.
*/
{
TLibAssocBase& l=*(TLibAssocBase*)aThis;
l.iPtr=NULL;
l.iLibrary.Close();
}
//
// class BaflUtils
//
EXPORT_C void BaflUtils::CopyWithTruncation(TDes& aDest,const TDesC& aSrc,TChar aTruncationSymbol)
/** Copies a descriptor, abbreviating it to fit the destination descriptor.
If aSrc is less than the maximum length of aDest, then the string is simply
copied to aDest.
If this is not so, then the left-most characters of aSrc are copied to aDest,
up to aDest's maximum length-1. aDest's final character is set to be aTruncationSymbol.
@param aDest On return, the truncated string
@param aSrc The string to truncate
@param aTruncationSymbol The truncation character to add */
{ // static
TInt maxLength=aDest.MaxLength();
if (aSrc.Length()<=maxLength)
aDest.Copy(aSrc);
else
{
aDest.Copy(aSrc.Left(maxLength-1));
aDest.Append(aTruncationSymbol);
}
}
EXPORT_C TBool BaflUtils::FileExists(const RFs& aFileSession,const TDesC& aFileName)
/** Checks if the specified file exists.
@param aFs File server session
@param aFileName File to check
@return ETrue if the file exists, otherwise EFalse */
{ // static
TEntry entry;
return(aFileSession.Entry(aFileName,entry)==KErrNone);
}
EXPORT_C TBool BaflUtils::PathExists(RFs& aFs,const TDesC& aPath)
/** Tests whether a path exists.
The path should contain a drive letter and a directory, or EFalse is returned.
EFalse is also returned if it contains a filename or filename extension.
If the path is badly formed, for instance if it contains illegal characters,
or any directory name consists of a single or double dot, or any directory
name includes wildcard characters, the function returns EFalse.
@param aFs A connected session with the file server.
@param aPath The path to test for. It should end in a backslash.
@return ETrue if the path exists, EFalse if not. EFalse is also returned if the
specified path is badly formed. */
{ // static
TParse parse;
TInt retcode;
retcode = parse.Set(aPath, NULL, NULL);
if (retcode != KErrNone)
return EFalse;
if ((! parse.DrivePresent()) || (parse.NameOrExtPresent()))
return EFalse;
if (parse.Path().Length() == 0)
return EFalse;
TFileName dirName = parse.DriveAndPath();
if (dirName.Length() > KMaxFileName)
return(EFalse);
RDir dir;
retcode = dir.Open(aFs,dirName,0);
if (retcode == KErrNone)
dir.Close();
return (retcode == KErrNone);
}
EXPORT_C void BaflUtils::EnsurePathExistsL(RFs& aFileSession,const TDesC& aFileName)
/** Makes one or more directories, if they do not already exist.
Any valid path component in the specified path that does not already exist
is created as a directory. If the specified path already exists, the function
returns normally.
@param aFs File server session
@param aFileName Path to ensure exists
@see RFs::MkDirAll() */
{ // static
TInt error=aFileSession.MkDirAll(aFileName);
if (error!=KErrAlreadyExists)
User::LeaveIfError(error);
}
EXPORT_C TPtrC BaflUtils::ExtractAppNameFromFullName(const TFullName &aName)
/** Gets the application name from a full thread name.
@param aName Thread name
@return Application name
@see RThread */
{
// static - return the app name (after first :: before next ::, if any) from a full thread name
TChar delimiter=':';
TInt start=aName.Locate(delimiter);
if (start<0)
start=0; // should never happen
else if (aName.Length()>start+2)
start+=2;
TPtrC rest=aName.Mid(start);
TInt end=rest.Locate(delimiter);
return end<0 ? rest : rest.Left(end);
}
LOCAL_C TBool IsLanguageExtended(const TLanguage aLanguage)
{
// For compatibility reasons, ELangNone is 0xFFFF. However, it's not an extended language.
if ((aLanguage==ELangNone) || ((static_cast<TUint>(aLanguage))<=KDialectMask))
return EFalse;
else
return ETrue;
}
LOCAL_C TLanguage BaseLanguage(const TLanguage aLanguage)
{
if (IsLanguageExtended(aLanguage))
return static_cast<TLanguage>(aLanguage & KDialectMask);
else
return aLanguage;
}
LOCAL_C TLanguage NextLanguage(TLanguage aLanguage)
/** Returns the next best language to use after aLanguage,
based on Symbian's base table of language near-equivalence.
@internalAll */
{
switch (aLanguage)
{
case ELangAustralian:
case ELangNewZealand:
case ELangSouthAfricanEnglish:
case ELangInternationalEnglish:
case ELangAmerican:
case ELangEnglish_Apac:
case ELangEnglish_Taiwan:
case ELangEnglish_HongKong:
case ELangEnglish_Prc:
case ELangEnglish_Japan:
case ELangEnglish_Thailand:
return ELangEnglish;
case ELangCanadianEnglish:
return ELangAmerican; // 2-stage downgrade
case ELangSwissFrench:
case ELangBelgianFrench:
case ELangInternationalFrench:
case ELangCanadianFrench:
return ELangFrench;
case ELangSwissGerman:
case ELangAustrian:
return ELangGerman;
case ELangInternationalSpanish:
case ELangLatinAmericanSpanish:
return ELangSpanish;
case ELangSwissItalian:
return ELangItalian;
case ELangFinlandSwedish:
return ELangSwedish;
case ELangCyprusTurkish:
return ELangTurkish;
case ELangBelgianFlemish:
return ELangDutch;
case ELangHongKongChinese:
return ELangTaiwanChinese;
case ELangCyprusGreek:
return ELangGreek;
case ELangMalay_Apac:
return ELangMalay;
case ELangBrazilianPortuguese:
return ELangPortuguese;
default:
return ELangNone;
}
}
void AddLanguage(TLanguagePath& aPath, TLanguage aNewLanguage)
/** Add language to the language path if there is space.
The first empty slot must have "ELangNone" in it. This will also be true
on exit. */
{
TLanguage *p = aPath;
const TLanguage *end = &(aPath[KMaxDowngradeLanguages]);
while (p != end)
{
if (*p == aNewLanguage)
// language already in list
break;
if (*p == ELangNone)
{
// found the end of the list
p[0] = aNewLanguage;
p[1] = ELangNone;
break;
}
++p;
}
return;
}
void MakeLanguageDowngradePath(TLanguagePath& aPath,
TLanguage aCurrent, TLanguage aIdeal, const TLocale& aLocale)
{
TInt j = 0;
if( aIdeal != ELangNone)
{
aPath[j++]=aIdeal;
}
aPath[j++] = aCurrent;
aPath[j++] = ELangNone;
if (aCurrent & ~KDialectMask)
AddLanguage(aPath, static_cast<TLanguage>(aCurrent & KDialectMask));
for (TInt i=0;i<=2;i++)
{
AddLanguage(aPath, aLocale.LanguageDowngrade(i));
AddLanguage(aPath, BaseLanguage(aLocale.LanguageDowngrade(i)));
}
while (ELangNone != (aCurrent = NextLanguage(BaseLanguage(aCurrent))))
AddLanguage(aPath, aCurrent);
}
TInt RRealDirectoryScanner::Open(RFs& aFs, const TDesC& aMatchPattern)
{
return iDir.Open(aFs, aMatchPattern,
KEntryAttReadOnly | KEntryAttHidden | KEntryAttSystem | KEntryAttArchive);
}
TInt RRealDirectoryScanner::Next(TEntry& aOut)
{
return iDir.Read(aOut);
}
void RRealDirectoryScanner::Close()
{
iDir.Close();
}
/**
Simply counts the number of numerical characters at the end of the name passed.
@internalComponent
@param aFilename The filename to parse
@return Count of the numeric digits at the end of the name passed,
e.g. x.r491 gives 3.
*/
TInt TNearestLanguageFileFinder::CountDigitsFromEnd(const TDesC& aFilename)
{
TInt digitCount = 0;
for (TInt idx = aFilename.Length()-1; idx>=0 && ISDIGIT (aFilename [idx]); --idx)
{
++digitCount;
}
return digitCount;
}
/**
Counts the number of digits at the end of a filename.
@internalComponent
@param aFilename The filename to parse
@return Count of the numeric digits at the end of the suffix,
e.g. x.r491 gives 3.
0 if no numeric end of suffix,
KErrBadName for an invalid filename,
KErrNotSupported if the filename (minus path) is less
than or equal to KInvNameAndMinSuffixLength in length
*/
TInt TNearestLanguageFileFinder::CountDigitsFromEndInSuffix (const TDesC& aFilename)
{
TInt digitCount = 0;
TInt slashIdx = 0;
TInt len = aFilename.Length ();
// NOTE: We didn't use TChar here as they are too slow.
// We also didn't use TParse as they are too large.
// don't work on the path
for (slashIdx=len-1; slashIdx >= 0 && aFilename[slashIdx] != '\\'; --slashIdx)
{/*do nothing*/};
// Get new length
if (slashIdx>=0) {len = len-slashIdx-1;}
// Initial test to see if filename legal size.
if (len > KInvNameAndMinSuffixLength)
{
digitCount = CountDigitsFromEnd(aFilename);
// Can't store something bigger or we'll panic!
if (digitCount > KMaxSuffixLength)
{
digitCount = KErrBadName;
}
else
// numeric filename, e.g. "1234".
// No preceeding alpha character
if (!(len-digitCount))
{
digitCount = KErrBadName;
}
}
else
{
digitCount = KErrNotSupported;
}
return digitCount;
}
RDirectoryScanner& TNearestLanguageFileFinder::DirectoryScanner()
{
return iDirScanner;
}
TBool TNearestLanguageFileFinder::FileExists(const TDesC& aFileName) const
{
return BaflUtils::FileExists(iFs, aFileName);
}
TBool TNearestLanguageFileFinder::FindDrive()
{
ASSERT(iFileName);
TBool found=EFalse;
TInt driveLength=iDrives.Length();
for (TInt drive = 0; drive!=driveLength; ++drive)
{
(*iFileName)[0] = iDrives[drive];
if (FileExists(*iFileName))
{
found=ETrue;
break;
}
}
return found;
}
TBool TNearestLanguageFileFinder::AppendLanguageCode(TLanguage aLanguage)
{
TInt rest = static_cast<TInt>(aLanguage);
#ifdef _DEBUG
_LIT(KErrorMessage, "Bafl");
#endif
__ASSERT_DEBUG(0 <= rest, User::Panic(KErrorMessage,KErrArgument));
iFileName->SetLength(iBaseLength);
const TInt remaining = iFileName->MaxLength() - iBaseLength;
TInt soFar = 0;
TBuf<1> num;
num.Append('0');
TBool appendLangSuccess = ETrue;
TInt digitCount = 0;
TInt digit = 0;
while (rest)
{
if (remaining == soFar)
{
// no more room in descriptor- return rather than panic,
// file cannot exist.
iFileName->SetLength(iBaseLength);
appendLangSuccess= EFalse;
break;
}
// Convert the number to ASCII by consistantly getting the base 10 remainder to convert.
// The number is updated minus the remainder for the next iteration.
// eg (rest = 123) -> (12, r3) -> (1, r2) -> (0, r1)
// Then insert the ASCII representation of the remainder into the filename end
// so it appears the correct way round.
// eg (filename.r) -> (filename.r3) -> (filename.r23) -> (filename.r123)
digit = rest % 10;
digitCount++;
rest /= 10;
num[0] = static_cast<TText16>(digit + '0');
iFileName->Insert(iBaseLength, num);
// Minimum suffix length is KInvNameAndMinSuffixLength
// so we have to insert zeros to make this up.
while (!rest && digitCount < KInvNameAndMinSuffixLength)
{
num[0] = static_cast<TText16>('0');
iFileName->Insert(iBaseLength, num);
++digitCount;
}
++soFar;
}
return appendLangSuccess;
}
TBool TNearestLanguageFileFinder::FindLanguageAndDrive()
/** Search for files across all drives in all languages in the path plus the
language-neutral file. */
{
ASSERT(iFileName);
// No point appending if the suffix is bad
for (const TLanguage* currentLang = iPath; *currentLang != ELangNone; ++currentLang)
{
if (AppendLanguageCode(*currentLang) && FindDrive())
{
iLanguage = *currentLang;
return ETrue;
}
}
// search for language-neutral file
iFileName->SetLength(iBaseLength);
iFileName->Append(iSuffix);
return FindDrive();
}
TInt TNearestLanguageFileFinder::LanguageNumberFromFile(const TDesC& aFileName, const TDesC& aStem)
{
TInt lang = 0;
TInt multiplier = 1;
TInt leadingZeroCount = 0;
TInt languageNumber = KErrNotFound;
const TText* firstChar = aFileName.Ptr();
const TText* lastChar = firstChar + aFileName.Length() - 1;
const TText* currentChar = lastChar;
// string cannot contain only numbers, because it must have a ':' in it
while ('0' <= *currentChar && *currentChar <= '9')
{
if (*currentChar == '0')
leadingZeroCount++;
else
{
leadingZeroCount = 0;
lang += multiplier * (*currentChar - '0');
}
multiplier *= 10;
--currentChar;
}
TInt along=lastChar - currentChar;
if (2 <= along)
{
// We have at least 2 digits at the end.
// trim of bad leading zeros
TInt maxTrim = along - 2;
if (maxTrim < leadingZeroCount)
{
leadingZeroCount = maxTrim;
}
currentChar += leadingZeroCount;
// we have at least 2 digits at the end but does the rest of it match the stem?
TPtrC foundStem(firstChar, currentChar - firstChar + 1);
//foundStem.CompareF(aStem.Right(foundStem.Length()))
if (0 == foundStem.CompareF(aStem))
{
languageNumber=lang;
}
}
return languageNumber;
}
TInt TNearestLanguageFileFinder::FindFirstLanguageFile(RFs& aFs)
{
ASSERT(iFileName);
iFileName->SetLength(iBaseLength);
TPtrC name(*iFileName);
TParsePtrC nameToParse(name);
TPtrC nameStem(nameToParse.NameAndExt());
iFileName->Append('*');
TInt bestLanguageMatch = KMaxTInt;
RDirectoryScanner& scanner = DirectoryScanner();
TInt err = scanner.Open(aFs, *iFileName);
if (err != KErrNone)
{
return err;
}
TEntry entry;
while (KErrNone == scanner.Next(entry))
{
TInt lang = LanguageNumberFromFile(entry.iName, nameStem);
if (0 < lang && lang < bestLanguageMatch)
{
bestLanguageMatch = lang;
}
}
scanner.Close();
if (bestLanguageMatch != KMaxTInt)
{
iLanguage = static_cast<TLanguage>(bestLanguageMatch);
AppendLanguageCode(static_cast<TLanguage>(bestLanguageMatch));
return KErrNone;
}
return KErrNotFound;
}
// Try each drive for any language files
// iFileName must have a directory specifier
TInt TNearestLanguageFileFinder::FindFirstLanguageFileAndDrive()
{
ASSERT(iFileName);
TInt findFirstResult=KErrNotFound;
TInt driveLength=iDrives.Length();
for (TInt drive = 0; drive != driveLength; ++drive)
{
(*iFileName)[0] = iDrives[drive];
TInt err = FindFirstLanguageFile(CONST_CAST(RFs&,iFs));
if (err == KErrNone || err == KErrNoMemory)
{
findFirstResult=err;
break;
}
}
return findFirstResult;
}
/**
Invalid filenames are any filename whose length (minus path) must be greater
than KInvNameAndMinSuffixLength, and whose form is purely numerical, i.e. '1234'
*/
TBool TNearestLanguageFileFinder::SetFileName(TFileName& aFileName)
{
iDrives.Zero();
iFileName = &aFileName;
iOriginalBaseLength = iFileName->Length();
TInt suffixLength = CountDigitsFromEndInSuffix (aFileName);
// No point trying for filenames thats are badly formed
// or that are too large.
if (suffixLength >= 0 &&
KInvNameAndMinSuffixLength < iOriginalBaseLength)
{
if (suffixLength > 0)
{
// all of suffix to be replaced
iSuffix = iFileName->Right(suffixLength);
iOriginalBaseLength -= suffixLength;
iFileName->SetLength(iOriginalBaseLength);
}
else
{
// No numerical part to suffix
TInt periodIdx = 0;
// Search for the period within range KInvNameAndMinSuffixLength
// from the end. As this must work for all values of
// KInvNameAndMinSuffixLength
for (TInt i = iOriginalBaseLength-1;
!periodIdx && i >= (iOriginalBaseLength-KInvNameAndMinSuffixLength-1);
--i)
{
if ((*iFileName) [i] == '.')
{
periodIdx = i;
}
}
// Don't handle files ending in a period.
// This is because the behaviour is different between Windows
// and Symbian Fs. In Windows it strips the period off.
//
// However, and this shouldn't happen as it is not shown
// (in the documentation) to be valid.
// Just try our best.
if (periodIdx == iOriginalBaseLength-1)
{
iSuffix.Zero();
return EFalse;
}
else
if (periodIdx)
{
// If there are KInvNameAndMinSuffixLength chars after the period
// simply replace them.
TInt right = iOriginalBaseLength-periodIdx-1;
iSuffix = iFileName->Right(right);
iOriginalBaseLength -= right;
iFileName->SetLength(iOriginalBaseLength);
}
else
{
// Make the suffix start from KInvNameAndMinSuffixLength
// from the right
TInt right = KInvNameAndMinSuffixLength;
iSuffix = iFileName->Right(right);
iOriginalBaseLength -= right;
iFileName->SetLength(iOriginalBaseLength);
}
}
}
else
{
// bad or no suffix - treat the same
iSuffix.Zero();
return EFalse;
}
// For filenames with no drive letter prefix and also for filenames
// shorter than the drive letter length, i.e. with no drive
// information, insert it.
// Handles if the user simply enters the drive, e.g. "c:".
if (iOriginalBaseLength < KMaxDriveName || (*iFileName)[1] != ':')
{
// Set up the default if none supplied and make room in the filename
// array to contain a drive specification. Set initial drive letter to -1
// so the iFileName is repaired before exited
iInitialDriveLetter = -1;
iFileName->Insert(0, _L("_:"));
iDrives.Append('Z');
}
else
{
// Use the drive supplied inthe aName to NearestLanguageFile()
iInitialDriveLetter = (*iFileName)[0];
iDrives.Append(iInitialDriveLetter);
}
iBaseLength = iFileName->Length();
return ETrue;
}
TLanguage TNearestLanguageFileFinder::Language()
{
return iLanguage;
}
TNearestLanguageFileFinder::TNearestLanguageFileFinder(
const RFs& aFs)
: iFs(aFs), iFileName(0), iLanguage(ELangNone)
{
}
void TNearestLanguageFileFinder::RepairFileName()
{
ASSERT(iFileName);
iFileName->SetLength(iBaseLength);
if (iInitialDriveLetter == -1)
iFileName->Delete(0, 2);
else
(*iFileName)[0] = static_cast<TText>(iInitialDriveLetter);
iFileName->SetLength(iOriginalBaseLength);
iFileName->Append(iSuffix);
}
/**
Add the custom resource drive to the start of the iDrives string.
The custom resource drive is a preset writeable drive on which customised
resource files may be present. This drive takes priority over the other
drives when searching for language files.
@return KErrNone if iDrives string was successfully modified; KErrAlreadyExists
if the drive is already present in the string; otherwise one of
the other system-wide error codes (iDrives will be unmodified).
*/
TInt TNearestLanguageFileFinder::AddCustomResourceDrive()
{
TInt drive = GetCustomResourceDriveNumber();
if (drive<0)
return drive;
// if drive not already in drive list
if (iDrives.LocateF('A' + drive) < 0)
{
// add it
_LIT(KDrivePlaceholder, "_");
iDrives.Insert(0, KDrivePlaceholder);
iDrives[0] = 'A' + drive;
return KErrNone;
}
else
return KErrAlreadyExists;
}
void TNearestLanguageFileFinder::AddAllDrives()
{
ASSERT(iDrives.Length() < 2);
if (iDrives.Length() == 0)
{
iDrives = KAllDrives;
return;
}
TInt pos = KAllDrives().LocateF(iDrives[0]);
if (pos < 0)
{
iDrives = KAllDrives;
return;
}
iDrives.Append(KAllDrives().Left(pos));
iDrives.Append(KAllDrives().Mid(pos + 1));
}
/**
Get the value of the custom resource drive.
The custom resource drive is a preset writeable drive on which customised language resource
files can reside. The drive number is accessed via the HAL attribute ECustomResourceDrive.
It is then returned if it has been defined as a valid drive no.
Otherwise for backward compatibility reasons an attempt is then made to access the system
drive HAL attribute instead. This drive number is returned if it has been defined as a valid
drive number.
Otherwise if neither a valid ECustomResourceDrive or ESystemDrive exists then KErrNotFound
is returned.
Note that the ESystemDrive HAL attribute has been deprecated. It is accessed here to cater
for existing implementations which still expect it to be used.
@return The drive number (corresponding to a TDriveNumber value) if successful;
KErrNotFound if neither a valid ECustomResourceDrive or a valid ESystemDrive HAL attribute
is defined;
@see HAL::ECustomResourceDrive
@see HAL::ESystemDrive
*/
TInt TNearestLanguageFileFinder::GetCustomResourceDriveNumber() const
{
TInt drive = KErrNotFound;
// access custom resource drive attribute
if (HAL::Get(HAL::ECustomResourceDrive, drive) == KErrNone)
{
// check that drive is valid
if (drive>=EDriveA && drive<=EDriveZ)
return drive;
}
// access system drive attribute
// (Note that ESystemDrive is deprecated. It is checked here
// solely for backward compatibility reasons.)
if (HAL::Get(HAL::ESystemDrive, drive) == KErrNone)
{
// check that drive is valid
if (drive>=EDriveA && drive<=EDriveZ)
return drive;
}
return KErrNotFound;
}
/** Get the value of the system drive.
The system drive can be set to one of the built-in read/write drives. Which
drive is used is hardware-dependent. On some hardware, there may not be a
system drive. The system drive is used as the drive on which localisable files
are searched for. This enables a phone to be localised dynamically, using
files not in the ROM.
@param aDriveNumber On return, contains the drive number of the system drive.
@return KErrNone is always returned.
@deprecated This method has been replaced by (and now internally calls)
RFs:GetSystemDrive, which always returns a valid drive number.
@see BaflUtils::NearestLanguageFile
@see RFs::GetSystemDrive
*/
EXPORT_C TInt BaflUtils::GetSystemDrive(TDriveNumber& aDriveNumber)
{
aDriveNumber = RFs::GetSystemDrive();
return KErrNone;
}
/** Set most appropriate extension language code for filename and set corresponding language.
Symbian uses numeric values to identify natural languages as specified by the TLanguage enumeration
defined in e32const.h. These values are used at the end of filename extensions to identify the
languages pertaining to files which have language specific variants such as resource files.
For instance filename.r01 and filename.r02 would be the English and French versions of the
resource file filename.rsc. Language codes can be between 2 to 5 digits in length.
Starting from Symbian OS v7.0 this function constructs and uses a language downgrade path which
consists of up to sixteen TLanguage values the first of which is the ideal language followed by
the language of the current locale. Up to the next three can be customised using
TLocale::SetLanguageDowngrade(). The rest of the language downgrade path is based on a
table of language near equivalence which is internal to Symbian.
This function searches the custom resource drive (if set, retrieved from HAL)
and then searches the optional drive specified in aName or 'Z:' if none is
specified in aName. The custom resource drive is retrieved from the HAL
attribute ECustomResourceDrive if set, if not set it will retrieve the legacy
value set in the legacy HAL attribute ESystemDrive. No custom resource drive
is searched if neither are set.
Note - setting the custom resource drive will reduce the performance of this
routine which will adversely affect device performance e.g. at boot up.
On NAND Flash based devices use of a composite Z: drive file system made up of multiple
ROM images is the preferred mechanism for customising language resources on devices in
Symbian OS 9.2 onwards, see Developer Library Base Porting Guide Porting: background
information NAND flash NAND Flash image format. Thus use of the custom resource drive
HAL attribute is effectively obsolete.
The last two characters of aName are removed along with any digits which appear before them.
Then language codes specified in the constructed language downgrade path are appended in turn to
aName as a match is searched for in the file system. In case no match is found using the
constructed language downgradepath then files in the specified directory are searched for a
suitable extension with preference given to the one specified if present. In cases where a
match takes place the aName and aLanguage arguments are updated otherwise aName is left
unchanged and aLanguage is set to ELangNone.
Here are some examples of correct and incorrect function usage with different aName inputs,
file system state and downgrade paths as follows:
@code
Following files exist:
C:\\abc.rsc - Language Neutral resource file.
C:\\abc.r01 - Resource file for the English language.
C:\\abc.r10 - Resource file for the American-English language.
C:\\abc.r160 - Resource file for the English as appropriate in Japan.
Constructed Downgrade Language Path cases:
- Case 1. (ELangAmerican -> ELangEnglish -> ELangNone).
- Case 2. (ELangEnglish_Japan -> ELangEnglish -> ELangNone).
- Case 3. Same as case 1, However "C:\\abc.r10" is deleted prior to the function call.
- Case 4. Same as case 1, However both "C:\\abc.r01" and "C:\\abc.r10" are deleted prior to the function call.