|
1 // Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include <bafl/langutil.h> |
|
17 #include <hal.h> |
|
18 #include <hal_data.h> |
|
19 #include "LangUtilImpl.h" |
|
20 |
|
21 |
|
22 /** |
|
23 Mimimum length of a filename and mimimum length of a suffix. |
|
24 Note these two values are tied together. |
|
25 */ |
|
26 const TInt KInvNameAndMinSuffixLength = 2; |
|
27 |
|
28 #define ISDIGIT(c) (c-'0' >= 0 && c-'0' <= 9) |
|
29 |
|
30 _LIT(KAllDrives, "YXWVUTSRQPONMLKJIHGFEDCBAZ"); |
|
31 |
|
32 LOCAL_C const TLanguage dp0[] = { ELangCanadianEnglish, ELangAmerican,ELangEnglish, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangAustralian,ELangNewZealand,ELangInternationalEnglish,ELangSouthAfricanEnglish,ELangNone }; |
|
33 LOCAL_C const TLanguage dp1[] = { ELangAmerican, ELangEnglish,ELangCanadianEnglish, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangAustralian,ELangNewZealand,ELangInternationalEnglish,ELangSouthAfricanEnglish, ELangNone }; |
|
34 LOCAL_C const TLanguage dp2[] = { ELangAustralian, ELangEnglish, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangInternationalEnglish,ELangSouthAfricanEnglish, ELangCanadianEnglish,ELangNone }; |
|
35 LOCAL_C const TLanguage dp3[] = { ELangSouthAfricanEnglish, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangInternationalEnglish,ELangCanadianEnglish,ELangNone }; |
|
36 LOCAL_C const TLanguage dp4[] = { ELangInternationalEnglish, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone }; |
|
37 LOCAL_C const TLanguage dp5[] = { ELangEnglish_Apac, ELangEnglish, ELangAustralian, ELangAmerican,ELangInternationalEnglish,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone }; |
|
38 LOCAL_C const TLanguage dp6[] = { ELangEnglish_Taiwan, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangInternationalEnglish,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone }; |
|
39 LOCAL_C const TLanguage dp7[] = { ELangEnglish_HongKong, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangInternationalEnglish,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone }; |
|
40 LOCAL_C const TLanguage dp8[] = { ELangEnglish_Prc, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangInternationalEnglish,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone }; |
|
41 LOCAL_C const TLanguage dp9[] = { ELangEnglish_Japan, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangInternationalEnglish,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone }; |
|
42 LOCAL_C const TLanguage dp10[] = { ELangEnglish_Thailand, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangInternationalEnglish,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone }; |
|
43 LOCAL_C const TLanguage dp11[] = { ELangEnglish_India, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangInternationalEnglish,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone }; |
|
44 LOCAL_C const TLanguage dp12[] = { ELangNewZealand, ELangEnglish, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangAustralian,ELangInternationalEnglish,ELangSouthAfricanEnglish, ELangCanadianEnglish,ELangNone }; |
|
45 LOCAL_C const TLanguage dp13[] = { ELangInternationalFrench,ELangFrench,ELangSwissFrench,ELangBelgianFrench,ELangCanadianFrench,ELangNone }; |
|
46 LOCAL_C const TLanguage dp14[] = { ELangBelgianFrench, ELangFrench,ELangInternationalFrench,ELangSwissFrench,ELangCanadianFrench,ELangNone }; |
|
47 LOCAL_C const TLanguage dp15[] = { ELangCanadianFrench, ELangFrench,ELangInternationalFrench,ELangSwissFrench,ELangBelgianFrench,ELangNone }; |
|
48 LOCAL_C const TLanguage dp16[] = { ELangFrench,ELangInternationalFrench,ELangSwissFrench,ELangBelgianFrench,ELangCanadianFrench,ELangNone }; |
|
49 LOCAL_C const TLanguage dp17[] = { ELangSwissFrench,ELangFrench,ELangInternationalFrench,ELangBelgianFrench,ELangCanadianFrench,ELangNone }; |
|
50 LOCAL_C const TLanguage dp18[] = { ELangSwissGerman,ELangGerman,ELangAustrian,ELangNone }; |
|
51 LOCAL_C const TLanguage dp19[] = { ELangAustrian,ELangGerman,ELangSwissGerman,ELangNone }; |
|
52 LOCAL_C const TLanguage dp20[] = { ELangGerman,ELangSwissGerman,ELangAustrian,ELangNone }; |
|
53 LOCAL_C const TLanguage dp21[] = { ELangSerbian,ELangCroatian,ELangNone }; |
|
54 LOCAL_C const TLanguage dp22[] = { ELangCroatian,ELangSerbian,ELangNone }; |
|
55 LOCAL_C const TLanguage dp23[] = { ELangRomanian,ELangMoldavian,ELangNone }; |
|
56 LOCAL_C const TLanguage dp24[] = { ELangMoldavian,ELangRomanian,ELangNone }; |
|
57 LOCAL_C const TLanguage dp25[] = { ELangBelgianFlemish,ELangDutch,ELangNone }; |
|
58 LOCAL_C const TLanguage dp26[] = { ELangDutch,ELangBelgianFlemish,ELangNone }; |
|
59 LOCAL_C const TLanguage dp27[] = { ELangAfrikaans,ELangDutch,ELangNone }; |
|
60 LOCAL_C const TLanguage dp28[] = { ELangMalay_Apac,ELangMalay,ELangNone }; |
|
61 LOCAL_C const TLanguage dp29[] = { ELangIndonesian_Apac,ELangIndonesian,ELangNone }; |
|
62 LOCAL_C const TLanguage dp30[] = { ELangSpanish,ELangInternationalSpanish,ELangLatinAmericanSpanish,ELangNone }; |
|
63 LOCAL_C const TLanguage dp31[] = { ELangLatinAmericanSpanish,ELangSpanish,ELangInternationalSpanish,ELangNone }; |
|
64 LOCAL_C const TLanguage dp32[] = { ELangInternationalSpanish,ELangSpanish,ELangLatinAmericanSpanish,ELangNone }; |
|
65 LOCAL_C const TLanguage dp33[] = { ELangCyprusGreek,ELangGreek,ELangNone }; |
|
66 LOCAL_C const TLanguage dp34[] = { ELangGreek,ELangCyprusGreek,ELangNone }; |
|
67 LOCAL_C const TLanguage dp35[] = { ELangSwissItalian,ELangItalian,ELangNone }; |
|
68 LOCAL_C const TLanguage dp36[] = { ELangItalian,ELangSwissItalian,ELangNone }; |
|
69 LOCAL_C const TLanguage dp37[] = { ELangBrazilianPortuguese,ELangPortuguese,ELangNone }; |
|
70 LOCAL_C const TLanguage dp38[] = { ELangPortuguese,ELangBrazilianPortuguese,ELangNone }; |
|
71 LOCAL_C const TLanguage dp39[] = { ELangFinlandSwedish,ELangSwedish,ELangNone }; |
|
72 LOCAL_C const TLanguage dp40[] = { ELangSwedish,ELangFinlandSwedish,ELangNone }; |
|
73 LOCAL_C const TLanguage dp41[] = { ELangCyprusTurkish,ELangTurkish,ELangNone }; |
|
74 LOCAL_C const TLanguage dp42[] = { ELangTurkish,ELangCyprusTurkish,ELangNone }; |
|
75 LOCAL_C const TLanguage dp43[] = { ELangHongKongChinese, ELangTaiwanChinese, ELangPrcChinese,ELangNone }; |
|
76 LOCAL_C const TLanguage dp44[] = { ELangTaiwanChinese, ELangHongKongChinese,ELangPrcChinese,ELangNone }; |
|
77 LOCAL_C const TLanguage dp45[] = { ELangPrcChinese, ELangHongKongChinese, ELangTaiwanChinese,ELangNone }; |
|
78 LOCAL_C const TLanguage * const KEquivalentLists[] = { dp0, dp1, dp2, dp3, dp4, dp5, dp6, |
|
79 dp7, dp8, dp9, dp10, dp11, dp12, dp13, dp14, dp15, dp16, dp17, |
|
80 dp18, dp19, dp20, dp21, dp22, dp23, dp24, dp25, dp26, dp27, |
|
81 dp28, dp29, dp30, dp31, dp32, dp33, dp34, dp35, dp36, dp37, |
|
82 dp38, dp39, dp40, dp41, dp42, dp43, dp44, dp45}; |
|
83 |
|
84 |
|
85 |
|
86 LOCAL_C TBool IsLanguageExtended(const TLanguage aLanguage) |
|
87 { |
|
88 // For compatibility reasons, ELangNone is 0xFFFF. However, it's not an extended language. |
|
89 if ((aLanguage==ELangNone) || ((static_cast<TUint>(aLanguage))<=KDialectMask)) |
|
90 return EFalse; |
|
91 else |
|
92 return ETrue; |
|
93 } |
|
94 |
|
95 |
|
96 LOCAL_C TLanguage BaseLanguage(const TLanguage aLanguage) |
|
97 { |
|
98 if (IsLanguageExtended(aLanguage)) |
|
99 return static_cast<TLanguage>(aLanguage & KDialectMask); |
|
100 else |
|
101 return aLanguage; |
|
102 } |
|
103 |
|
104 LOCAL_C TLanguage NextLanguage(TLanguage aLanguage) |
|
105 /** Returns the next best language to use after aLanguage, |
|
106 based on Symbian's base table of language near-equivalence. |
|
107 @internalAll */ |
|
108 { |
|
109 switch (aLanguage) |
|
110 { |
|
111 case ELangAustralian: |
|
112 case ELangNewZealand: |
|
113 case ELangSouthAfricanEnglish: |
|
114 case ELangInternationalEnglish: |
|
115 case ELangAmerican: |
|
116 case ELangEnglish_Apac: |
|
117 case ELangEnglish_Taiwan: |
|
118 case ELangEnglish_HongKong: |
|
119 case ELangEnglish_Prc: |
|
120 case ELangEnglish_Japan: |
|
121 case ELangEnglish_Thailand: |
|
122 return ELangEnglish; |
|
123 case ELangCanadianEnglish: |
|
124 return ELangAmerican; // 2-stage downgrade |
|
125 case ELangSwissFrench: |
|
126 case ELangBelgianFrench: |
|
127 case ELangInternationalFrench: |
|
128 case ELangCanadianFrench: |
|
129 return ELangFrench; |
|
130 case ELangSwissGerman: |
|
131 case ELangAustrian: |
|
132 return ELangGerman; |
|
133 case ELangInternationalSpanish: |
|
134 case ELangLatinAmericanSpanish: |
|
135 return ELangSpanish; |
|
136 case ELangSwissItalian: |
|
137 return ELangItalian; |
|
138 case ELangFinlandSwedish: |
|
139 return ELangSwedish; |
|
140 case ELangCyprusTurkish: |
|
141 return ELangTurkish; |
|
142 case ELangBelgianFlemish: |
|
143 return ELangDutch; |
|
144 case ELangHongKongChinese: |
|
145 return ELangTaiwanChinese; |
|
146 case ELangCyprusGreek: |
|
147 return ELangGreek; |
|
148 case ELangMalay_Apac: |
|
149 return ELangMalay; |
|
150 case ELangBrazilianPortuguese: |
|
151 return ELangPortuguese; |
|
152 default: |
|
153 return ELangNone; |
|
154 } |
|
155 } |
|
156 |
|
157 |
|
158 void AddLanguage(TLanguagePath& aPath, TLanguage aNewLanguage) |
|
159 /** Add language to the language path if there is space. |
|
160 The first empty slot must have "ELangNone" in it. This will also be true |
|
161 on exit. */ |
|
162 { |
|
163 TLanguage *p = aPath; |
|
164 const TLanguage *end = &(aPath[KMaxDowngradeLanguages]); |
|
165 while (p != end) |
|
166 { |
|
167 if (*p == aNewLanguage) |
|
168 // language already in list |
|
169 break; |
|
170 if (*p == ELangNone) |
|
171 { |
|
172 // found the end of the list |
|
173 p[0] = aNewLanguage; |
|
174 p[1] = ELangNone; |
|
175 break; |
|
176 } |
|
177 ++p; |
|
178 } |
|
179 return; |
|
180 } |
|
181 |
|
182 void MakeLanguageDowngradePath(TLanguagePath& aPath, |
|
183 TLanguage aCurrent, TLanguage aIdeal, const TLocale& aLocale) |
|
184 { |
|
185 TInt j = 0; |
|
186 if( aIdeal != ELangNone) |
|
187 { |
|
188 aPath[j++]=aIdeal; |
|
189 } |
|
190 aPath[j++] = aCurrent; |
|
191 aPath[j++] = ELangNone; |
|
192 |
|
193 if (aCurrent & ~KDialectMask) |
|
194 AddLanguage(aPath, static_cast<TLanguage>(aCurrent & KDialectMask)); |
|
195 |
|
196 for (TInt i=0;i<=2;i++) |
|
197 { |
|
198 AddLanguage(aPath, aLocale.LanguageDowngrade(i)); |
|
199 AddLanguage(aPath, BaseLanguage(aLocale.LanguageDowngrade(i))); |
|
200 } |
|
201 |
|
202 while (ELangNone != (aCurrent = NextLanguage(BaseLanguage(aCurrent)))) |
|
203 AddLanguage(aPath, aCurrent); |
|
204 } |
|
205 |
|
206 |
|
207 |
|
208 //EXPORT_C |
|
209 void LangUtil::GetDowngradePathL(const RFs& aFs, const TLanguage aCurrentLanguage, RArray<TLanguage>& aLanguageArray){ |
|
210 |
|
211 TLocale currentLocale; |
|
212 TNearestLanguageFileFinder languageDowngradePath(aFs); |
|
213 TLanguage idealLanguage=IdealLanguage(); |
|
214 MakeLanguageDowngradePath(languageDowngradePath.iPath,aCurrentLanguage,idealLanguage, currentLocale); |
|
215 aLanguageArray.Reset(); |
|
216 const TLanguage* p=languageDowngradePath.iPath; |
|
217 while (*p != ELangNone) |
|
218 { |
|
219 User::LeaveIfError(aLanguageArray.Append(*p)); |
|
220 ++p; |
|
221 } |
|
222 |
|
223 } |
|
224 |
|
225 |
|
226 //EXPORT_C |
|
227 void LangUtil::GetEquivalentLanguageList(TLanguage aLang, TLanguagePath& aEquivalents){ |
|
228 |
|
229 aEquivalents[0] = aLang; |
|
230 aEquivalents[1] = ELangNone; |
|
231 const TInt len = sizeof(KEquivalentLists) / sizeof(KEquivalentLists[0]); |
|
232 for (TInt i = 0; i < len; ++i) |
|
233 { |
|
234 const TLanguage *ptr = KEquivalentLists[i]; |
|
235 if (ptr[0] == aLang) |
|
236 { |
|
237 TInt index = 1; |
|
238 while (ELangNone != *ptr) |
|
239 { |
|
240 aEquivalents[index++] = (TLanguage)*(++ptr); |
|
241 } |
|
242 aEquivalents[index] = ELangNone; |
|
243 break; |
|
244 } // end if ptr[0] |
|
245 } // end for i |
|
246 |
|
247 } |
|
248 |
|
249 |
|
250 |
|
251 //EXPORT_C |
|
252 TLanguage LangUtil::IdealLanguage(){ |
|
253 |
|
254 TLanguage* langPtr=(TLanguage*)Dll::Tls(); |
|
255 |
|
256 if( langPtr==NULL) |
|
257 { |
|
258 return(ELangNone); |
|
259 } |
|
260 |
|
261 return(*langPtr); |
|
262 |
|
263 } |
|
264 |
|
265 |
|
266 //EXPORT_C |
|
267 void LangUtil::NearestLanguageFile(const RFs& aFs, TFileName& aName){ |
|
268 |
|
269 TLanguage language; |
|
270 |
|
271 NearestLanguageFile( aFs, aName, language); |
|
272 |
|
273 (void)language; |
|
274 |
|
275 } |
|
276 |
|
277 |
|
278 //EXPORT_C |
|
279 void LangUtil::NearestLanguageFile(const RFs& aFs, TFileName& aName, TLanguage& aLanguage){ |
|
280 #if defined(DO_PROFILING) |
|
281 RDebug::ProfileReset(FIRST_PROFILE_INDEX, PROFILE_COUNT); |
|
282 RDebug::ProfileStart(PROFILE_INDEX_1); |
|
283 #endif |
|
284 TNearestLanguageFileFinder finder(aFs); |
|
285 TBool goodSuffix=finder.SetFileName(aName); |
|
286 |
|
287 // Only continue if the suffix is good. |
|
288 if(goodSuffix) |
|
289 { |
|
290 // add preset customised resource drive to drive list |
|
291 // Note that errors returned from AddCustomResourceDrive are ignored. This is because if |
|
292 // a custom resource drive has not been found we still want to continue on with searching |
|
293 // other drives according to our algorithm |
|
294 finder.AddCustomResourceDrive(); |
|
295 |
|
296 TLocale locale; |
|
297 TLanguage idealLanguage; |
|
298 idealLanguage = IdealLanguage(); |
|
299 MakeLanguageDowngradePath(finder.iPath, User::Language(), idealLanguage, locale); |
|
300 if (!finder.FindLanguageAndDrive() |
|
301 && KErrNone != finder.FindFirstLanguageFileAndDrive()) |
|
302 finder.RepairFileName(); |
|
303 aLanguage = finder.Language(); |
|
304 } |
|
305 |
|
306 #if defined(DO_PROFILING) |
|
307 RDebug::ProfileEnd(PROFILE_INDEX_1); |
|
308 TProfile profile[PROFILE_COUNT]; |
|
309 RDebug::ProfileResult(&profile[0], FIRST_PROFILE_INDEX, PROFILE_COUNT); |
|
310 if(goodSuffix) |
|
311 { |
|
312 RDebug::Print(_L("BaflUtils::NearestLanguageFile profile info: %d.%03ds"), profile[PROFILE_INDEX_1-FIRST_PROFILE_INDEX].iTime/1000000, profile[PROFILE_INDEX_1-FIRST_PROFILE_INDEX].iTime%1000000); |
|
313 } |
|
314 else |
|
315 { |
|
316 RDebug::Print(_L("BaflUtils::NearestLanguageFile (bad suffix ) profile info: %d.%03ds"), profile[PROFILE_INDEX_1-FIRST_PROFILE_INDEX].iTime/1000000, profile[PROFILE_INDEX_1-FIRST_PROFILE_INDEX].iTime%1000000); |
|
317 } |
|
318 #endif |
|
319 |
|
320 } |
|
321 |
|
322 |
|
323 //EXPORT_C |
|
324 void LangUtil::NearestLanguageFileV2(const RFs& aFs, TFileName& aName, TLanguage& aLanguage){ |
|
325 |
|
326 TNearestLanguageFileFinder finder(aFs); |
|
327 TBool goodSuffix=finder.SetFileName(aName); |
|
328 |
|
329 // Continue only if the suffix is good. |
|
330 if(goodSuffix) |
|
331 { |
|
332 // add preset customised resource drive to drive list |
|
333 // Note that errors returned from AddCustomResourceDrive are ignored. This is because if |
|
334 // a custom resource drive has not been found we still want to continue on with searching |
|
335 // other drives according to our algorithm |
|
336 finder.AddCustomResourceDrive(); |
|
337 |
|
338 GetEquivalentLanguageList(User::Language(), finder.iPath); |
|
339 if (!finder.FindLanguageAndDrive() |
|
340 && KErrNone != finder.FindFirstLanguageFileAndDrive()) |
|
341 finder.RepairFileName(); |
|
342 aLanguage = finder.Language(); |
|
343 } |
|
344 else |
|
345 { |
|
346 aLanguage = ELangNone; |
|
347 } |
|
348 |
|
349 } |
|
350 |
|
351 |
|
352 //EXPORT_C |
|
353 void LangUtil::ReleaseIdealLanguage(){ |
|
354 |
|
355 TLanguage* aLanguage=(TLanguage*)Dll::Tls(); |
|
356 if( aLanguage==NULL) |
|
357 return; |
|
358 |
|
359 delete aLanguage; |
|
360 Dll::FreeTls(); |
|
361 |
|
362 } |
|
363 |
|
364 |
|
365 //EXPORT_C |
|
366 TInt LangUtil::SetIdealLanguage(TLanguage aLanguage){ |
|
367 TLanguage* langPtr=(TLanguage*)Dll::Tls(); |
|
368 if( langPtr==NULL) |
|
369 { |
|
370 langPtr=(TLanguage*)User::Alloc(sizeof(TLanguage)); |
|
371 |
|
372 if(!langPtr) |
|
373 return(KErrNoMemory); |
|
374 |
|
375 TInt ret=Dll::SetTls(langPtr); |
|
376 |
|
377 if(ret!=KErrNone) |
|
378 return(ret); |
|
379 } |
|
380 *langPtr=aLanguage; |
|
381 return(KErrNone); |
|
382 |
|
383 } |
|
384 |
|
385 TInt RRealDirectoryScanner::Open(RFs& aFs, const TDesC& aMatchPattern) |
|
386 { |
|
387 return iDir.Open(aFs, aMatchPattern, |
|
388 KEntryAttReadOnly | KEntryAttHidden | KEntryAttSystem | KEntryAttArchive); |
|
389 } |
|
390 |
|
391 TInt RRealDirectoryScanner::Next(TEntry& aOut) |
|
392 { |
|
393 return iDir.Read(aOut); |
|
394 } |
|
395 |
|
396 void RRealDirectoryScanner::Close() |
|
397 { |
|
398 iDir.Close(); |
|
399 } |
|
400 |
|
401 /** |
|
402 Simply counts the number of numerical characters at the end of the name passed. |
|
403 |
|
404 @internalComponent |
|
405 @param aFilename The filename to parse |
|
406 |
|
407 @return Count of the numeric digits at the end of the name passed, |
|
408 e.g. x.r491 gives 3. |
|
409 */ |
|
410 TInt TNearestLanguageFileFinder::CountDigitsFromEnd(const TDesC& aFilename) |
|
411 { |
|
412 TInt digitCount = 0; |
|
413 |
|
414 for (TInt idx = aFilename.Length()-1; idx>=0 && ISDIGIT (aFilename [idx]); --idx) |
|
415 { |
|
416 ++digitCount; |
|
417 } |
|
418 |
|
419 return digitCount; |
|
420 } |
|
421 |
|
422 |
|
423 /** |
|
424 Counts the number of digits at the end of a filename. |
|
425 |
|
426 @internalComponent |
|
427 @param aFilename The filename to parse |
|
428 |
|
429 @return Count of the numeric digits at the end of the suffix, |
|
430 e.g. x.r491 gives 3. |
|
431 0 if no numeric end of suffix, |
|
432 KErrBadName for an invalid filename, |
|
433 KErrNotSupported if the filename (minus path) is less |
|
434 than or equal to KInvNameAndMinSuffixLength in length |
|
435 */ |
|
436 TInt TNearestLanguageFileFinder::CountDigitsFromEndInSuffix (const TDesC& aFilename) |
|
437 { |
|
438 TInt digitCount = 0; |
|
439 TInt slashIdx = 0; |
|
440 TInt len = aFilename.Length (); |
|
441 |
|
442 // NOTE: We didn't use TChar here as they are too slow. |
|
443 // We also didn't use TParse as they are too large. |
|
444 |
|
445 // don't work on the path |
|
446 for (slashIdx=len-1; slashIdx >= 0 && aFilename[slashIdx] != '\\'; --slashIdx) |
|
447 {/*do nothing*/}; |
|
448 |
|
449 // Get new length |
|
450 if (slashIdx>=0) {len = len-slashIdx-1;} |
|
451 |
|
452 // Initial test to see if filename legal size. |
|
453 if (len > KInvNameAndMinSuffixLength) |
|
454 { |
|
455 digitCount = CountDigitsFromEnd(aFilename); |
|
456 |
|
457 // Can't store something bigger or we'll panic! |
|
458 if (digitCount > KMaxSuffixLength) |
|
459 { |
|
460 digitCount = KErrBadName; |
|
461 } |
|
462 else |
|
463 // numeric filename, e.g. "1234". |
|
464 // No preceeding alpha character |
|
465 if (!(len-digitCount)) |
|
466 { |
|
467 digitCount = KErrBadName; |
|
468 } |
|
469 } |
|
470 else |
|
471 { |
|
472 digitCount = KErrNotSupported; |
|
473 } |
|
474 |
|
475 return digitCount; |
|
476 } |
|
477 |
|
478 RDirectoryScanner& TNearestLanguageFileFinder::DirectoryScanner() |
|
479 { |
|
480 return iDirScanner; |
|
481 } |
|
482 |
|
483 TBool TNearestLanguageFileFinder::FileExists(const TDesC& aFileName) const |
|
484 { |
|
485 //return BaflUtils::FileExists(iFs, aFileName); |
|
486 TEntry entry; |
|
487 return(iFs.Entry(aFileName,entry)==KErrNone); |
|
488 |
|
489 } |
|
490 |
|
491 TBool TNearestLanguageFileFinder::FindDrive() |
|
492 { |
|
493 ASSERT(iFileName); |
|
494 TBool found=EFalse; |
|
495 TInt driveLength=iDrives.Length(); |
|
496 for (TInt drive = 0; drive!=driveLength; ++drive) |
|
497 { |
|
498 (*iFileName)[0] = iDrives[drive]; |
|
499 if (FileExists(*iFileName)) |
|
500 { |
|
501 found=ETrue; |
|
502 break; |
|
503 } |
|
504 } |
|
505 return found; |
|
506 } |
|
507 |
|
508 TBool TNearestLanguageFileFinder::AppendLanguageCode(TLanguage aLanguage) |
|
509 { |
|
510 TInt rest = static_cast<TInt>(aLanguage); |
|
511 #ifdef _DEBUG |
|
512 _LIT(KErrorMessage, "Bafl"); |
|
513 #endif |
|
514 __ASSERT_DEBUG(0 <= rest, User::Panic(KErrorMessage,KErrArgument)); |
|
515 iFileName->SetLength(iBaseLength); |
|
516 const TInt remaining = iFileName->MaxLength() - iBaseLength; |
|
517 TInt soFar = 0; |
|
518 TBuf<1> num; |
|
519 num.Append('0'); |
|
520 TBool appendLangSuccess = ETrue; |
|
521 TInt digitCount = 0; |
|
522 TInt digit = 0; |
|
523 while (rest) |
|
524 { |
|
525 if (remaining == soFar) |
|
526 { |
|
527 // no more room in descriptor- return rather than panic, |
|
528 // file cannot exist. |
|
529 iFileName->SetLength(iBaseLength); |
|
530 appendLangSuccess= EFalse; |
|
531 break; |
|
532 } |
|
533 // Convert the number to ASCII by consistantly getting the base 10 remainder to convert. |
|
534 // The number is updated minus the remainder for the next iteration. |
|
535 // eg (rest = 123) -> (12, r3) -> (1, r2) -> (0, r1) |
|
536 // Then insert the ASCII representation of the remainder into the filename end |
|
537 // so it appears the correct way round. |
|
538 // eg (filename.r) -> (filename.r3) -> (filename.r23) -> (filename.r123) |
|
539 digit = rest % 10; |
|
540 digitCount++; |
|
541 rest /= 10; |
|
542 num[0] = static_cast<TText16>(digit + '0'); |
|
543 iFileName->Insert(iBaseLength, num); |
|
544 |
|
545 // Minimum suffix length is KInvNameAndMinSuffixLength |
|
546 // so we have to insert zeros to make this up. |
|
547 while (!rest && digitCount < KInvNameAndMinSuffixLength) |
|
548 { |
|
549 num[0] = static_cast<TText16>('0'); |
|
550 iFileName->Insert(iBaseLength, num); |
|
551 ++digitCount; |
|
552 } |
|
553 |
|
554 ++soFar; |
|
555 } |
|
556 |
|
557 return appendLangSuccess; |
|
558 } |
|
559 |
|
560 |
|
561 TBool TNearestLanguageFileFinder::FindLanguageAndDrive() |
|
562 /** Search for files across all drives in all languages in the path plus the |
|
563 language-neutral file. */ |
|
564 { |
|
565 ASSERT(iFileName); |
|
566 // No point appending if the suffix is bad |
|
567 for (const TLanguage* currentLang = iPath; *currentLang != ELangNone; ++currentLang) |
|
568 { |
|
569 if (AppendLanguageCode(*currentLang) && FindDrive()) |
|
570 { |
|
571 iLanguage = *currentLang; |
|
572 return ETrue; |
|
573 } |
|
574 } |
|
575 // search for language-neutral file |
|
576 iFileName->SetLength(iBaseLength); |
|
577 iFileName->Append(iSuffix); |
|
578 return FindDrive(); |
|
579 } |
|
580 |
|
581 TInt TNearestLanguageFileFinder::LanguageNumberFromFile(const TDesC& aFileName, const TDesC& aStem) |
|
582 { |
|
583 TInt lang = 0; |
|
584 TInt multiplier = 1; |
|
585 TInt leadingZeroCount = 0; |
|
586 TInt languageNumber = KErrNotFound; |
|
587 const TText* firstChar = aFileName.Ptr(); |
|
588 const TText* lastChar = firstChar + aFileName.Length() - 1; |
|
589 const TText* currentChar = lastChar; |
|
590 // string cannot contain only numbers, because it must have a ':' in it |
|
591 while ('0' <= *currentChar && *currentChar <= '9') |
|
592 { |
|
593 if (*currentChar == '0') |
|
594 leadingZeroCount++; |
|
595 else |
|
596 { |
|
597 leadingZeroCount = 0; |
|
598 lang += multiplier * (*currentChar - '0'); |
|
599 } |
|
600 multiplier *= 10; |
|
601 --currentChar; |
|
602 } |
|
603 TInt along=lastChar - currentChar; |
|
604 if (2 <= along) |
|
605 { |
|
606 // We have at least 2 digits at the end. |
|
607 // trim of bad leading zeros |
|
608 TInt maxTrim = along - 2; |
|
609 if (maxTrim < leadingZeroCount) |
|
610 { |
|
611 leadingZeroCount = maxTrim; |
|
612 } |
|
613 currentChar += leadingZeroCount; |
|
614 // we have at least 2 digits at the end but does the rest of it match the stem? |
|
615 TPtrC foundStem(firstChar, currentChar - firstChar + 1); |
|
616 //foundStem.CompareF(aStem.Right(foundStem.Length())) |
|
617 if (0 == foundStem.CompareF(aStem)) |
|
618 { |
|
619 languageNumber=lang; |
|
620 } |
|
621 } |
|
622 return languageNumber; |
|
623 } |
|
624 |
|
625 TInt TNearestLanguageFileFinder::FindFirstLanguageFile(RFs& aFs) |
|
626 { |
|
627 ASSERT(iFileName); |
|
628 iFileName->SetLength(iBaseLength); |
|
629 TPtrC name(*iFileName); |
|
630 TParsePtrC nameToParse(name); |
|
631 TPtrC nameStem(nameToParse.NameAndExt()); |
|
632 iFileName->Append('*'); |
|
633 TInt bestLanguageMatch = KMaxTInt; |
|
634 RDirectoryScanner& scanner = DirectoryScanner(); |
|
635 TInt err = scanner.Open(aFs, *iFileName); |
|
636 if (err != KErrNone) |
|
637 { |
|
638 return err; |
|
639 } |
|
640 TEntry entry; |
|
641 while (KErrNone == scanner.Next(entry)) |
|
642 { |
|
643 TInt lang = LanguageNumberFromFile(entry.iName, nameStem); |
|
644 if (0 < lang && lang < bestLanguageMatch) |
|
645 { |
|
646 bestLanguageMatch = lang; |
|
647 } |
|
648 } |
|
649 scanner.Close(); |
|
650 if (bestLanguageMatch != KMaxTInt) |
|
651 { |
|
652 iLanguage = static_cast<TLanguage>(bestLanguageMatch); |
|
653 AppendLanguageCode(static_cast<TLanguage>(bestLanguageMatch)); |
|
654 return KErrNone; |
|
655 } |
|
656 return KErrNotFound; |
|
657 } |
|
658 |
|
659 // Try each drive for any language files |
|
660 // iFileName must have a directory specifier |
|
661 TInt TNearestLanguageFileFinder::FindFirstLanguageFileAndDrive() |
|
662 { |
|
663 ASSERT(iFileName); |
|
664 TInt findFirstResult=KErrNotFound; |
|
665 TInt driveLength=iDrives.Length(); |
|
666 for (TInt drive = 0; drive != driveLength; ++drive) |
|
667 { |
|
668 (*iFileName)[0] = iDrives[drive]; |
|
669 TInt err = FindFirstLanguageFile(CONST_CAST(RFs&,iFs)); |
|
670 if (err == KErrNone || err == KErrNoMemory) |
|
671 { |
|
672 findFirstResult=err; |
|
673 break; |
|
674 } |
|
675 } |
|
676 return findFirstResult; |
|
677 } |
|
678 |
|
679 /** |
|
680 Invalid filenames are any filename whose length (minus path) must be greater |
|
681 than KInvNameAndMinSuffixLength, and whose form is purely numerical, i.e. '1234' |
|
682 */ |
|
683 TBool TNearestLanguageFileFinder::SetFileName(TFileName& aFileName) |
|
684 { |
|
685 iDrives.Zero(); |
|
686 iFileName = &aFileName; |
|
687 iOriginalBaseLength = iFileName->Length(); |
|
688 |
|
689 TInt suffixLength = CountDigitsFromEndInSuffix (aFileName); |
|
690 |
|
691 // No point trying for filenames thats are badly formed |
|
692 // or that are too large. |
|
693 if (suffixLength >= 0 && |
|
694 KInvNameAndMinSuffixLength < iOriginalBaseLength) |
|
695 { |
|
696 if (suffixLength > 0) |
|
697 { |
|
698 // all of suffix to be replaced |
|
699 iSuffix = iFileName->Right(suffixLength); |
|
700 iOriginalBaseLength -= suffixLength; |
|
701 iFileName->SetLength(iOriginalBaseLength); |
|
702 } |
|
703 else |
|
704 { |
|
705 // No numerical part to suffix |
|
706 TInt periodIdx = 0; |
|
707 |
|
708 // Search for the period within range KInvNameAndMinSuffixLength |
|
709 // from the end. As this must work for all values of |
|
710 // KInvNameAndMinSuffixLength |
|
711 for (TInt i = iOriginalBaseLength-1; |
|
712 !periodIdx && i >= (iOriginalBaseLength-KInvNameAndMinSuffixLength-1); |
|
713 --i) |
|
714 { |
|
715 if ((*iFileName) [i] == '.') |
|
716 { |
|
717 periodIdx = i; |
|
718 } |
|
719 } |
|
720 |
|
721 // Don't handle files ending in a period. |
|
722 // This is because the behaviour is different between Windows |
|
723 // and Symbian Fs. In Windows it strips the period off. |
|
724 // |
|
725 // However, and this shouldn't happen as it is not shown |
|
726 // (in the documentation) to be valid. |
|
727 // Just try our best. |
|
728 if (periodIdx == iOriginalBaseLength-1) |
|
729 { |
|
730 iSuffix.Zero(); |
|
731 return EFalse; |
|
732 } |
|
733 else |
|
734 if (periodIdx) |
|
735 { |
|
736 // If there are KInvNameAndMinSuffixLength chars after the period |
|
737 // simply replace them. |
|
738 TInt right = iOriginalBaseLength-periodIdx-1; |
|
739 iSuffix = iFileName->Right(right); |
|
740 iOriginalBaseLength -= right; |
|
741 iFileName->SetLength(iOriginalBaseLength); |
|
742 } |
|
743 else |
|
744 { |
|
745 // Make the suffix start from KInvNameAndMinSuffixLength |
|
746 // from the right |
|
747 TInt right = KInvNameAndMinSuffixLength; |
|
748 iSuffix = iFileName->Right(right); |
|
749 iOriginalBaseLength -= right; |
|
750 iFileName->SetLength(iOriginalBaseLength); |
|
751 } |
|
752 } |
|
753 } |
|
754 else |
|
755 { |
|
756 // bad or no suffix - treat the same |
|
757 iSuffix.Zero(); |
|
758 return EFalse; |
|
759 } |
|
760 |
|
761 // For filenames with no drive letter prefix and also for filenames |
|
762 // shorter than the drive letter length, i.e. with no drive |
|
763 // information, insert it. |
|
764 // Handles if the user simply enters the drive, e.g. "c:". |
|
765 if (iOriginalBaseLength < KMaxDriveName || (*iFileName)[1] != ':') |
|
766 { |
|
767 // Set up the default if none supplied and make room in the filename |
|
768 // array to contain a drive specification. Set initial drive letter to -1 |
|
769 // so the iFileName is repaired before exited |
|
770 iInitialDriveLetter = -1; |
|
771 iFileName->Insert(0, _L("_:")); |
|
772 iDrives.Append('Z'); |
|
773 } |
|
774 else |
|
775 { |
|
776 // Use the drive supplied inthe aName to NearestLanguageFile() |
|
777 iInitialDriveLetter = (*iFileName)[0]; |
|
778 iDrives.Append(iInitialDriveLetter); |
|
779 } |
|
780 |
|
781 iBaseLength = iFileName->Length(); |
|
782 |
|
783 return ETrue; |
|
784 } |
|
785 |
|
786 |
|
787 TLanguage TNearestLanguageFileFinder::Language() |
|
788 { |
|
789 return iLanguage; |
|
790 } |
|
791 |
|
792 TNearestLanguageFileFinder::TNearestLanguageFileFinder( |
|
793 const RFs& aFs) |
|
794 : iFs(aFs), iFileName(0), iLanguage(ELangNone) |
|
795 { |
|
796 } |
|
797 |
|
798 void TNearestLanguageFileFinder::RepairFileName() |
|
799 { |
|
800 ASSERT(iFileName); |
|
801 iFileName->SetLength(iBaseLength); |
|
802 if (iInitialDriveLetter == -1) |
|
803 iFileName->Delete(0, 2); |
|
804 else |
|
805 (*iFileName)[0] = static_cast<TText>(iInitialDriveLetter); |
|
806 iFileName->SetLength(iOriginalBaseLength); |
|
807 iFileName->Append(iSuffix); |
|
808 } |
|
809 |
|
810 |
|
811 /** |
|
812 Add the custom resource drive to the start of the iDrives string. |
|
813 |
|
814 The custom resource drive is a preset writeable drive on which customised |
|
815 resource files may be present. This drive takes priority over the other |
|
816 drives when searching for language files. |
|
817 |
|
818 @return KErrNone if iDrives string was successfully modified; KErrAlreadyExists |
|
819 if the drive is already present in the string; otherwise one of |
|
820 the other system-wide error codes (iDrives will be unmodified). |
|
821 */ |
|
822 TInt TNearestLanguageFileFinder::AddCustomResourceDrive() |
|
823 { |
|
824 TInt drive = GetCustomResourceDriveNumber(); |
|
825 if (drive<0) |
|
826 return drive; |
|
827 |
|
828 // if drive not already in drive list |
|
829 if (iDrives.LocateF('A' + drive) < 0) |
|
830 { |
|
831 // add it |
|
832 _LIT(KDrivePlaceholder, "_"); |
|
833 iDrives.Insert(0, KDrivePlaceholder); |
|
834 iDrives[0] = 'A' + drive; |
|
835 return KErrNone; |
|
836 } |
|
837 else |
|
838 return KErrAlreadyExists; |
|
839 } |
|
840 |
|
841 |
|
842 void TNearestLanguageFileFinder::AddAllDrives() |
|
843 { |
|
844 ASSERT(iDrives.Length() < 2); |
|
845 if (iDrives.Length() == 0) |
|
846 { |
|
847 iDrives = KAllDrives; |
|
848 return; |
|
849 } |
|
850 TInt pos = KAllDrives().LocateF(iDrives[0]); |
|
851 if (pos < 0) |
|
852 { |
|
853 iDrives = KAllDrives; |
|
854 return; |
|
855 } |
|
856 iDrives.Append(KAllDrives().Left(pos)); |
|
857 iDrives.Append(KAllDrives().Mid(pos + 1)); |
|
858 } |
|
859 |
|
860 |
|
861 /** |
|
862 Get the value of the custom resource drive. |
|
863 |
|
864 The custom resource drive is a preset writeable drive on which customised language resource |
|
865 files can reside. The drive number is accessed via the HAL attribute ECustomResourceDrive. |
|
866 It is then returned if it has been defined as a valid drive no. |
|
867 Otherwise for backward compatibility reasons an attempt is then made to access the system |
|
868 drive HAL attribute instead. This drive number is returned if it has been defined as a valid |
|
869 drive number. |
|
870 Otherwise if neither a valid ECustomResourceDrive or ESystemDrive exists then KErrNotFound |
|
871 is returned. |
|
872 |
|
873 Note that the ESystemDrive HAL attribute has been deprecated. It is accessed here to cater |
|
874 for existing implementations which still expect it to be used. |
|
875 |
|
876 @return The drive number (corresponding to a TDriveNumber value) if successful; |
|
877 KErrNotFound if neither a valid ECustomResourceDrive or a valid ESystemDrive HAL attribute |
|
878 is defined; |
|
879 |
|
880 @see HAL::ECustomResourceDrive |
|
881 @see HAL::ESystemDrive |
|
882 */ |
|
883 TInt TNearestLanguageFileFinder::GetCustomResourceDriveNumber() const |
|
884 { |
|
885 TInt drive = KErrNotFound; |
|
886 |
|
887 // access custom resource drive attribute |
|
888 if (HAL::Get(HAL::ECustomResourceDrive, drive) == KErrNone) |
|
889 { |
|
890 // check that drive is valid |
|
891 if (drive>=EDriveA && drive<=EDriveZ) |
|
892 return drive; |
|
893 } |
|
894 |
|
895 // access system drive attribute |
|
896 // (Note that ESystemDrive is deprecated. It is checked here |
|
897 // solely for backward compatibility reasons.) |
|
898 if (HAL::Get(HAL::ESystemDrive, drive) == KErrNone) |
|
899 { |
|
900 // check that drive is valid |
|
901 if (drive>=EDriveA && drive<=EDriveZ) |
|
902 return drive; |
|
903 } |
|
904 |
|
905 return KErrNotFound; |
|
906 } |
|
907 |
|
908 |