Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

681 lines
20 KiB

  1. /****************************** Module*Header *****************************\
  2. * Module Name: rtlmir.c *
  3. * *
  4. * This module contains all the Right-To-Left (RTL) Mirroring support *
  5. * routines which are used across the whole IShell project. It abstracts *
  6. * platform-support routines of RTL mirroring (NT5 and Memphis) and removes *
  7. * linkage depedenency with the Mirroring APIs. *
  8. * *
  9. * Functions prefixed with Mirror, deal with the new Mirroring APIs *
  10. * *
  11. * *
  12. * Created: 01-Feb-1998 8:41:18 pm *
  13. * Author: Samer Arafeh [samera] *
  14. * *
  15. * Copyright (c) 1998 Microsoft Corporation *
  16. \**************************************************************************/
  17. #include "..\stock.h"
  18. #if (WINVER < 0x0500)
  19. #error WINVER setting must be >= 0x0500
  20. #endif
  21. #ifndef DS_BIDI_RTL
  22. #define DS_BIDI_RTL 0x8000
  23. #endif
  24. const DWORD dwNoMirrorBitmap = NOMIRRORBITMAP;
  25. const DWORD dwExStyleRTLMirrorWnd = WS_EX_LAYOUTRTL;
  26. const DWORD dwExStyleNoInheritLayout = WS_EX_NOINHERITLAYOUT;
  27. const DWORD dwPreserveBitmap = LAYOUT_BITMAPORIENTATIONPRESERVED;
  28. /*
  29. * Remove linkage dependecy for the RTL mirroring APIs, by retreiving
  30. * their addresses at runtime.
  31. */
  32. typedef DWORD (WINAPI *PFNGETLAYOUT)(HDC); // gdi32!GetLayout
  33. typedef DWORD (WINAPI *PFNSETLAYOUT)(HDC, DWORD); // gdi32!SetLayout
  34. typedef BOOL (WINAPI *PFNSETPROCESSDEFLAYOUT)(DWORD); // user32!SetProcessDefaultLayout
  35. typedef BOOL (WINAPI *PFNGETPROCESSDEFLAYOUT)(DWORD*); // user32!GetProcessDefaultLayout
  36. typedef LANGID (WINAPI *PFNGETUSERDEFAULTUILANGUAGE)(void); // kernel32!GetUserDefaultUILanguage
  37. typedef BOOL (WINAPI *PFNENUMUILANGUAGES)(UILANGUAGE_ENUMPROC, DWORD, LONG_PTR); // kernel32!EnumUILanguages
  38. typedef struct {
  39. LANGID LangID;
  40. BOOL bInstalled;
  41. } MUIINSTALLLANG, *LPMUIINSTALLLANG;
  42. #ifdef UNICODE
  43. #define ConvertHexStringToInt ConvertHexStringToIntW
  44. #else
  45. #define ConvertHexStringToInt ConvertHexStringToIntA
  46. #endif
  47. /***************************************************************************\
  48. * ConvertHexStringToIntA
  49. *
  50. * Converts a hex numeric string into an integer.
  51. *
  52. * History:
  53. * 04-Feb-1998 samera Created
  54. \***************************************************************************/
  55. BOOL ConvertHexStringToIntA( CHAR *pszHexNum , int *piNum )
  56. {
  57. int n=0L;
  58. CHAR *psz=pszHexNum;
  59. for(n=0 ; ; psz=CharNextA(psz))
  60. {
  61. if( (*psz>='0') && (*psz<='9') )
  62. n = 0x10 * n + *psz - '0';
  63. else
  64. {
  65. CHAR ch = *psz;
  66. int n2;
  67. if(ch >= 'a')
  68. ch -= 'a' - 'A';
  69. n2 = ch - 'A' + 0xA;
  70. if (n2 >= 0xA && n2 <= 0xF)
  71. n = 0x10 * n + n2;
  72. else
  73. break;
  74. }
  75. }
  76. /*
  77. * Update results
  78. */
  79. *piNum = n;
  80. return (psz != pszHexNum);
  81. }
  82. /***************************************************************************\
  83. * ConvertHexStringToIntW
  84. *
  85. * Converts a hex numeric string into an integer.
  86. *
  87. * History:
  88. * 14-June-1998 msadek Created
  89. \***************************************************************************/
  90. BOOL ConvertHexStringToIntW( WCHAR *pszHexNum , int *piNum )
  91. {
  92. int n=0L;
  93. WCHAR *psz=pszHexNum;
  94. for(n=0 ; ; psz=CharNextW(psz))
  95. {
  96. if( (*psz>='0') && (*psz<='9') )
  97. n = 0x10 * n + *psz - '0';
  98. else
  99. {
  100. WCHAR ch = *psz;
  101. int n2;
  102. if(ch >= 'a')
  103. ch -= 'a' - 'A';
  104. n2 = ch - 'A' + 0xA;
  105. if (n2 >= 0xA && n2 <= 0xF)
  106. n = 0x10 * n + n2;
  107. else
  108. break;
  109. }
  110. }
  111. /*
  112. * Update results
  113. */
  114. *piNum = n;
  115. return (psz != pszHexNum);
  116. }
  117. /***************************************************************************\
  118. * IsBiDiLocalizedSystemEx
  119. *
  120. * returns TRUE if running on a lozalized BiDi (Arabic/Hebrew) NT5 or Memphis.
  121. * Should be called whenever SetProcessDefaultLayout is to be called.
  122. *
  123. * History:
  124. * 02-Feb-1998 samera Created
  125. \***************************************************************************/
  126. BOOL IsBiDiLocalizedSystemEx( LANGID *pLangID )
  127. {
  128. int iLCID=0L;
  129. static TRIBIT s_tbBiDi = TRIBIT_UNDEFINED;
  130. static LANGID s_langID = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
  131. if (s_tbBiDi == TRIBIT_UNDEFINED)
  132. {
  133. BOOL bRet = FALSE;
  134. if(staticIsOS(OS_WIN2000ORGREATER))
  135. {
  136. /*
  137. * Need to use NT5 detection method (Multiligual UI ID)
  138. */
  139. s_langID = Mirror_GetUserDefaultUILanguage();
  140. if(s_langID)
  141. {
  142. WCHAR wchLCIDFontSignature[16];
  143. iLCID = MAKELCID(s_langID, SORT_DEFAULT );
  144. /*
  145. * Let's verify this is a RTL (BiDi) locale. Since reg value is a hex string, let's
  146. * convert to decimal value and call GetLocaleInfo afterwards.
  147. * LOCALE_FONTSIGNATURE always gives back 16 WCHARs.
  148. */
  149. if( GetLocaleInfoW( iLCID ,
  150. LOCALE_FONTSIGNATURE ,
  151. (WCHAR *) &wchLCIDFontSignature[0] ,
  152. (sizeof(wchLCIDFontSignature)/sizeof(WCHAR))) )
  153. {
  154. /* Let's verify the bits we have a BiDi UI locale */
  155. if(( wchLCIDFontSignature[7] & (WCHAR)0x0800) && Mirror_IsUILanguageInstalled(s_langID) )
  156. {
  157. bRet = TRUE;
  158. }
  159. }
  160. }
  161. } else {
  162. /*
  163. * Check if BiDi-Memphis is running with Lozalized Resources (
  164. * i.e. Arabic/Hebrew systems) -It should be enabled ofcourse-.
  165. */
  166. if( (staticIsOS(OS_WIN98ORGREATER)) && (GetSystemMetrics(SM_MIDEASTENABLED)) )
  167. {
  168. HKEY hKey;
  169. if( RegOpenKeyExA( HKEY_CURRENT_USER ,
  170. "Control Panel\\Desktop\\ResourceLocale" ,
  171. 0,
  172. KEY_READ, &hKey) == ERROR_SUCCESS)
  173. {
  174. CHAR szResourceLocale[12];
  175. DWORD dwSize = sizeof(szResourceLocale);
  176. RegQueryValueExA( hKey , "" , 0 , NULL, (LPBYTE)szResourceLocale , &dwSize );
  177. szResourceLocale[(sizeof(szResourceLocale)/sizeof(CHAR))-1] = 0;
  178. RegCloseKey(hKey);
  179. if( ConvertHexStringToIntA( szResourceLocale , &iLCID ) )
  180. {
  181. iLCID = PRIMARYLANGID(LANGIDFROMLCID(iLCID));
  182. if( (LANG_ARABIC == iLCID) || (LANG_HEBREW == iLCID) )
  183. {
  184. bRet = TRUE;
  185. s_langID = LANGIDFROMLCID(iLCID);
  186. }
  187. }
  188. }
  189. }
  190. }
  191. COMPILETIME_ASSERT(sizeof(s_tbBiDi) == sizeof(long));
  192. // close multiproc race on startup
  193. InterlockedExchange((long*)&s_tbBiDi, bRet ? TRIBIT_TRUE : TRIBIT_FALSE);
  194. }
  195. if (s_tbBiDi == TRIBIT_TRUE && pLangID)
  196. {
  197. *pLangID = s_langID;
  198. }
  199. return (s_tbBiDi == TRIBIT_TRUE);
  200. }
  201. BOOL IsBiDiLocalizedSystem( void )
  202. {
  203. return IsBiDiLocalizedSystemEx(NULL);
  204. }
  205. /***************************************************************************\
  206. * IsBiDiLocalizedWin95
  207. *
  208. * returns TRUE if running on a lozalized BiDi (Arabic/Hebrew) Win95.
  209. * Needed for legacy operating system check for needed RTL UI elements
  210. * For example, DefView ListView, TreeView,...etc
  211. * History:
  212. * 12-June-1998 a-msadek Created
  213. \***************************************************************************/
  214. BOOL IsBiDiLocalizedWin95(BOOL bArabicOnly)
  215. {
  216. HKEY hKey;
  217. DWORD dwType;
  218. BOOL bRet = FALSE;
  219. CHAR szResourceLocale[12];
  220. DWORD dwSize = sizeof(szResourceLocale)/sizeof(CHAR);
  221. int iLCID=0L;
  222. /*
  223. * Check if BiDi-Win95 is running with Lozalized Resources (
  224. * i.e. Arabic/Hebrew systems) -It should be enabled ofcourse-.
  225. */
  226. if( (staticIsOS(OS_WIN95ORGREATER)) && (!staticIsOS(OS_WIN98ORGREATER)) && (GetSystemMetrics(SM_MIDEASTENABLED)) )
  227. {
  228. if( RegOpenKeyExA( HKEY_CURRENT_USER ,
  229. "Control Panel\\Desktop\\ResourceLocale" ,
  230. 0,
  231. KEY_READ, &hKey) == ERROR_SUCCESS)
  232. {
  233. RegQueryValueExA( hKey , "" , 0 , &dwType , (LPBYTE)szResourceLocale , &dwSize );
  234. szResourceLocale[(sizeof(szResourceLocale)/sizeof(CHAR))-1] = 0;
  235. RegCloseKey(hKey);
  236. if( ConvertHexStringToIntA( szResourceLocale , &iLCID ) )
  237. {
  238. iLCID = PRIMARYLANGID(LANGIDFROMLCID(iLCID));
  239. //
  240. //If bArabicOnly we will return true if it a Arabic Win95 localized.
  241. //
  242. if( (LANG_ARABIC == iLCID) || ((LANG_HEBREW == iLCID) && !bArabicOnly ))
  243. {
  244. bRet = TRUE;
  245. }
  246. }
  247. }
  248. }
  249. return bRet;
  250. }
  251. /***************************************************************************\
  252. * Mirror_IsEnabledOS
  253. *
  254. * returns TRUE if the mirroring APIs are enabled on the current OS.
  255. *
  256. * History:
  257. * 02-Feb-1998 samera Created
  258. \***************************************************************************/
  259. BOOL Mirror_IsEnabledOS( void )
  260. {
  261. BOOL bRet = FALSE;
  262. if(staticIsOS(OS_WIN2000ORGREATER))
  263. {
  264. bRet = TRUE;
  265. } else if( staticIsOS(OS_WIN98ORGREATER) && GetSystemMetrics(SM_MIDEASTENABLED)) {
  266. bRet=TRUE;
  267. }
  268. return bRet;
  269. }
  270. /***************************************************************************\
  271. * Mirror_GetUserDefaultUILanguage
  272. *
  273. * Reads the User UI language on NT5
  274. *
  275. * History:
  276. * 22-June-1998 samera Created
  277. \***************************************************************************/
  278. LANGID Mirror_GetUserDefaultUILanguage( void )
  279. {
  280. LANGID langId=0;
  281. static PFNGETUSERDEFAULTUILANGUAGE pfnGetUserDefaultUILanguage=NULL;
  282. if( NULL == pfnGetUserDefaultUILanguage )
  283. {
  284. HMODULE hmod = GetModuleHandleA("KERNEL32");
  285. if( hmod )
  286. pfnGetUserDefaultUILanguage = (PFNGETUSERDEFAULTUILANGUAGE)
  287. GetProcAddress(hmod, "GetUserDefaultUILanguage");
  288. }
  289. if( pfnGetUserDefaultUILanguage )
  290. langId = pfnGetUserDefaultUILanguage();
  291. return langId;
  292. }
  293. /***************************************************************************\
  294. * Mirror_IsUILanguageInstalled
  295. *
  296. * Verifies that the User UI language is installed on W2k
  297. *
  298. * History:
  299. * 14-June-1999 msadek Created
  300. \***************************************************************************/
  301. BOOL Mirror_IsUILanguageInstalled( LANGID langId )
  302. {
  303. MUIINSTALLLANG MUILangInstalled = {0};
  304. MUILangInstalled.LangID = langId;
  305. static PFNENUMUILANGUAGES pfnEnumUILanguages=NULL;
  306. if( NULL == pfnEnumUILanguages )
  307. {
  308. HMODULE hmod = GetModuleHandleA("KERNEL32");
  309. if( hmod )
  310. pfnEnumUILanguages = (PFNENUMUILANGUAGES)
  311. GetProcAddress(hmod, "EnumUILanguagesW");
  312. }
  313. if( pfnEnumUILanguages )
  314. pfnEnumUILanguages(Mirror_EnumUILanguagesProc, 0, (LONG_PTR)&MUILangInstalled);
  315. return MUILangInstalled.bInstalled;
  316. }
  317. /***************************************************************************\
  318. * Mirror_EnumUILanguagesProc
  319. *
  320. * Enumerates MUI installed languages on W2k
  321. * History:
  322. * 14-June-1999 msadek Created
  323. \***************************************************************************/
  324. BOOL CALLBACK Mirror_EnumUILanguagesProc(LPTSTR lpUILanguageString, LONG_PTR lParam)
  325. {
  326. int langID = 0;
  327. ConvertHexStringToInt(lpUILanguageString, &langID);
  328. if((LANGID)langID == ((LPMUIINSTALLLANG)lParam)->LangID)
  329. {
  330. ((LPMUIINSTALLLANG)lParam)->bInstalled = TRUE;
  331. return FALSE;
  332. }
  333. return TRUE;
  334. }
  335. /***************************************************************************\
  336. * Mirror_IsWindowMirroredRTL
  337. *
  338. * returns TRUE if the window is RTL mirrored
  339. *
  340. * History:
  341. * 02-Feb-1998 samera Created
  342. \***************************************************************************/
  343. BOOL Mirror_IsWindowMirroredRTL( HWND hWnd )
  344. {
  345. return (GetWindowLongA( hWnd , GWL_EXSTYLE ) & WS_EX_LAYOUTRTL );
  346. }
  347. /***************************************************************************\
  348. * Mirror_GetLayout
  349. *
  350. * returns TRUE if the hdc is RTL mirrored
  351. *
  352. * History:
  353. * 02-Feb-1998 samera Created
  354. \***************************************************************************/
  355. DWORD Mirror_GetLayout( HDC hdc )
  356. {
  357. DWORD dwRet=0;
  358. static PFNGETLAYOUT pfnGetLayout=NULL;
  359. if( NULL == pfnGetLayout )
  360. {
  361. HMODULE hmod = GetModuleHandleA("GDI32");
  362. if( hmod )
  363. pfnGetLayout = (PFNGETLAYOUT)GetProcAddress(hmod, "GetLayout");
  364. }
  365. if( pfnGetLayout )
  366. dwRet = pfnGetLayout( hdc );
  367. return dwRet;
  368. }
  369. DWORD Mirror_IsDCMirroredRTL( HDC hdc )
  370. {
  371. return (Mirror_GetLayout( hdc ) & LAYOUT_RTL);
  372. }
  373. /***************************************************************************\
  374. * Mirror_SetLayout
  375. *
  376. * RTL Mirror the hdc
  377. *
  378. * History:
  379. * 02-Feb-1998 samera Created
  380. \***************************************************************************/
  381. DWORD Mirror_SetLayout( HDC hdc , DWORD dwLayout )
  382. {
  383. DWORD dwRet=0;
  384. static PFNSETLAYOUT pfnSetLayout=NULL;
  385. if( NULL == pfnSetLayout )
  386. {
  387. HMODULE hmod = GetModuleHandleA("GDI32");
  388. if( hmod )
  389. pfnSetLayout = (PFNSETLAYOUT)GetProcAddress(hmod, "SetLayout");
  390. }
  391. if( pfnSetLayout )
  392. dwRet = pfnSetLayout( hdc , dwLayout );
  393. return dwRet;
  394. }
  395. DWORD Mirror_MirrorDC( HDC hdc )
  396. {
  397. return Mirror_SetLayout( hdc , LAYOUT_RTL );
  398. }
  399. /***************************************************************************\
  400. * Mirror_SetProcessDefaultLayout
  401. *
  402. * Set the process-default layout.
  403. *
  404. * History:
  405. * 02-Feb-1998 samera Created
  406. \***************************************************************************/
  407. BOOL Mirror_SetProcessDefaultLayout( DWORD dwDefaultLayout )
  408. {
  409. BOOL bRet=0;
  410. static PFNSETPROCESSDEFLAYOUT pfnSetProcessDefLayout=NULL;
  411. if( NULL == pfnSetProcessDefLayout )
  412. {
  413. HMODULE hmod = GetModuleHandleA("USER32");
  414. if( hmod )
  415. pfnSetProcessDefLayout = (PFNSETPROCESSDEFLAYOUT)
  416. GetProcAddress(hmod, "SetProcessDefaultLayout");
  417. }
  418. if( pfnSetProcessDefLayout )
  419. bRet = pfnSetProcessDefLayout( dwDefaultLayout );
  420. return bRet;
  421. }
  422. BOOL Mirror_MirrorProcessRTL( void )
  423. {
  424. return Mirror_SetProcessDefaultLayout( LAYOUT_RTL );
  425. }
  426. /***************************************************************************\
  427. * Mirror_GetProcessDefaultLayout
  428. *
  429. * Get the process-default layout.
  430. *
  431. * History:
  432. * 26-Feb-1998 samera Created
  433. \***************************************************************************/
  434. BOOL Mirror_GetProcessDefaultLayout( DWORD *pdwDefaultLayout )
  435. {
  436. BOOL bRet=0;
  437. static PFNGETPROCESSDEFLAYOUT pfnGetProcessDefLayout=NULL;
  438. if( NULL == pfnGetProcessDefLayout )
  439. {
  440. HMODULE hmod = GetModuleHandleA("USER32");
  441. if( hmod )
  442. pfnGetProcessDefLayout = (PFNGETPROCESSDEFLAYOUT)
  443. GetProcAddress(hmod, "GetProcessDefaultLayout");
  444. }
  445. if( pfnGetProcessDefLayout )
  446. bRet = pfnGetProcessDefLayout( pdwDefaultLayout );
  447. return bRet;
  448. }
  449. BOOL Mirror_IsProcessRTL( void )
  450. {
  451. DWORD dwDefLayout=0;
  452. return (Mirror_GetProcessDefaultLayout(&dwDefLayout) && (dwDefLayout&LAYOUT_RTL));
  453. }
  454. ////////////////////////////////////////////////////////////////////////////
  455. // Skip_IDorString
  456. //
  457. // Skips string (or ID) and returns the next aligned WORD.
  458. ////////////////////////////////////////////////////////////////////////////
  459. PBYTE Skip_IDorString(LPBYTE pb)
  460. {
  461. LPWORD pw = (LPWORD)pb;
  462. if (*pw == 0xFFFF)
  463. return (LPBYTE)(pw + 2);
  464. while (*pw++ != 0)
  465. ;
  466. return (LPBYTE)pw;
  467. }
  468. ////////////////////////////////////////////////////////////////////////////
  469. // Skip_DialogHeader
  470. //
  471. // Skips the dialog header and returns the next aligned WORD.
  472. ////////////////////////////////////////////////////////////////////////////
  473. PBYTE Skip_DialogHeader(LPDLGTEMPLATE pdt)
  474. {
  475. LPBYTE pb;
  476. pb = (LPBYTE)(pdt + 1);
  477. // If there is a menu ordinal, add 4 bytes skip it. Otherwise it is a string or just a 0.
  478. pb = Skip_IDorString(pb);
  479. // Skip window class and window text, adjust to next word boundary.
  480. pb = Skip_IDorString(pb); // class
  481. pb = Skip_IDorString(pb); // window text
  482. // Skip font type, size and name, adjust to next dword boundary.
  483. if (pdt->style & DS_SETFONT)
  484. {
  485. pb += sizeof(WORD);
  486. pb = Skip_IDorString(pb);
  487. }
  488. pb = (LPBYTE)(((ULONG_PTR)pb + 3) & ~3); // DWORD align
  489. return pb;
  490. }
  491. ////////////////////////////////////////////////////////////////////////////
  492. // EditBiDiDLGTemplate
  493. //
  494. // Edits a dialog template for BiDi stuff.
  495. // Optionally, skipping some controls.
  496. // Works only with DLGTEMPLATE.
  497. ////////////////////////////////////////////////////////////////////////////
  498. void EditBiDiDLGTemplate(LPDLGTEMPLATE pdt, DWORD dwFlags, PWORD pwIgnoreList, int cIgnore)
  499. {
  500. LPBYTE pb;
  501. UINT cItems;
  502. if (!pdt)
  503. return;
  504. // we should never get an extended template
  505. ASSERT (((LPDLGTEMPLATEEX)pdt)->wSignature != 0xFFFF);
  506. if(dwFlags & EBDT_NOMIRROR)
  507. {
  508. // Turn off the mirroring styles for the dialog.
  509. pdt->dwExtendedStyle &= ~(WS_EX_LAYOUTRTL | WS_EX_NOINHERITLAYOUT);
  510. }
  511. cItems = pdt->cdit;
  512. // skip DLGTEMPLATE part
  513. pb = Skip_DialogHeader(pdt);
  514. while (cItems--)
  515. {
  516. UINT cbCreateParams;
  517. int i = 0;
  518. BOOL bIgnore = FALSE;
  519. if(pwIgnoreList && cIgnore)
  520. {
  521. for(i = 0;i < cIgnore; i++)
  522. {
  523. if((((LPDLGITEMTEMPLATE)pb)->id == *(pwIgnoreList +i)))
  524. {
  525. bIgnore = TRUE;
  526. }
  527. }
  528. }
  529. if((dwFlags & EBDT_NOMIRROR) && !bIgnore)
  530. {
  531. // Turn off the mirroring styles for this item.
  532. ((LPDLGITEMTEMPLATE)pb)->dwExtendedStyle &= ~(WS_EX_LAYOUTRTL | WS_EX_NOINHERITLAYOUT);
  533. }
  534. if((dwFlags & EBDT_FLIP) && !bIgnore)
  535. {
  536. ((LPDLGITEMTEMPLATE)pb)->x = pdt->cx - (((LPDLGITEMTEMPLATE)pb)->x + ((LPDLGITEMTEMPLATE)pb)->cx);
  537. // BUGBUG: Should we force RTL reading order for title as well ?
  538. // The client has the option of doining this already by PSH_RTLREADING
  539. }
  540. pb += sizeof(DLGITEMTEMPLATE);
  541. // Skip the dialog control class name.
  542. pb = Skip_IDorString(pb);
  543. // Look at window text now.
  544. pb = Skip_IDorString(pb);
  545. cbCreateParams = *((LPWORD)pb);
  546. // skip any CreateParams which include the generated size WORD.
  547. if (cbCreateParams)
  548. pb += cbCreateParams;
  549. pb += sizeof(WORD);
  550. // Point at the next dialog item. (DWORD aligned)
  551. pb = (LPBYTE)(((ULONG_PTR)pb + 3) & ~3);
  552. bIgnore = FALSE;
  553. }
  554. }